Skip to content

Commit

Permalink
Merge pull request #27 from Alper-Soy/develop
Browse files Browse the repository at this point in the history
Prod - 08/25/2024
  • Loading branch information
Alper-Soy authored Aug 25, 2024
2 parents b48186c + 2535600 commit 286e5f8
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 4 deletions.
9 changes: 9 additions & 0 deletions API/Controllers/ProfilesController.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Application.Features.Profiles.Commands.UpdateProfile;
using Application.Features.Profiles.Queries.GetProfile;
using Microsoft.AspNetCore.Mvc;

Expand All @@ -10,4 +11,12 @@ public async Task<IActionResult> GetProfile(string username)
{
return HandleResult(await Mediator.Send(new GetProfileQuery { Username = username }));
}

[HttpPut]
public async Task<IActionResult> Update(UpdateProfileCommand command)
{
return HandleResult(await Mediator.Send(command));
}
}


Binary file modified API/activity-hub.db
Binary file not shown.
Binary file removed API/activity-hub.db-shm
Binary file not shown.
Binary file removed API/activity-hub.db-wal
Binary file not shown.
1 change: 0 additions & 1 deletion Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<ItemGroup>
<Folder Include="Features\Attendance\Queries\"/>
<Folder Include="Features\Photos\Queries\"/>
<Folder Include="Features\Profiles\Commands\"/>
<Folder Include="Photos\"/>
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task<Result<Unit>> Handle(CreateActivityCommand request, Cancellati

context.Activities.Add(request.Activity);

var result = await context.SaveChangesAsync(cancellationToken) > 0;
var result = await context.SaveChangesAsync() > 0;

return !result ? Result<Unit>.Failure("Failed to create activity") : Result<Unit>.Success(Unit.Value);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Application.Core;
using MediatR;

namespace Application.Features.Profiles.Commands.UpdateProfile;

public class UpdateProfileCommand:IRequest<Result<Unit>>
{
public string DisplayName { get; set; }
public string Bio { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Application.Core;
using Application.Interfaces;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Persistence;

namespace Application.Features.Profiles.Commands.UpdateProfile;

public class UpdateProfileHandler(DataContext context, IUserAccessor userAccessor)
: IRequestHandler<UpdateProfileCommand, Result<Unit>>
{

public async Task<Result<Unit>> Handle(UpdateProfileCommand request, CancellationToken cancellationToken)
{
var user = await context.Users.FirstOrDefaultAsync(x =>
x.UserName == userAccessor.GetUsername());

user.Bio = request.Bio ?? user.Bio;
user.DisplayName = request.DisplayName ?? user.DisplayName;

var success = await context.SaveChangesAsync() > 0;

return success ? Result<Unit>.Success(Unit.Value) : Result<Unit>.Failure("Problem updating profile");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FluentValidation;

namespace Application.Features.Profiles.Commands.UpdateProfile;



public class UpdateProfileValidator:AbstractValidator<UpdateProfileCommand>
{
public UpdateProfileValidator()
{
RuleFor(x => x.DisplayName).NotEmpty();
}
}
2 changes: 2 additions & 0 deletions client-app/src/app/api/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ const Profiles = {
},
setMainPhoto: (id: string) => axios.post(`/photos/${id}/setMain`, {}),
deletePhoto: (id: string) => axios.delete(`/photos/${id}`),
updateProfile: (profile: Partial<Profile>) =>
requests.put(`/profiles`, profile),
};

const agent = {
Expand Down
20 changes: 20 additions & 0 deletions client-app/src/app/stores/profileStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,24 @@ export default class ProfileStore {
this.loading = false;
}
};

updateProfile = async (profile: Partial<Profile>) => {
this.loading = true;
try {
await agent.Profiles.updateProfile(profile);
runInAction(() => {
if (
profile.displayName &&
profile.displayName !== store.userStore.user?.displayName
) {
store.userStore.setDisplayName(profile.displayName);
}
this.profile = { ...this.profile, ...(profile as Profile) };
this.loading = false;
});
} catch (error) {
console.log(error);
runInAction(() => (this.loading = false));
}
};
}
4 changes: 4 additions & 0 deletions client-app/src/app/stores/userStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ export default class UserStore {
setUserPhoto = (url: string) => {
if (this.user) this.user.image = url;
};

setDisplayName = (name: string) => {
if (this.user) this.user.displayName = name;
};
}
40 changes: 40 additions & 0 deletions client-app/src/features/profiles/ProfileAbout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useState } from 'react';
import { useStore } from '../../app/stores/store';
import { Button, Grid, Header, TabPane } from 'semantic-ui-react';
import ProfileEditForm from './ProfileEditForm';
import { observer } from 'mobx-react-lite';

export default observer(function ProfileAbout() {
const { profileStore } = useStore();
const { isCurrentUser, profile } = profileStore;
const [editMode, setEditMode] = useState(false);

return (
<TabPane>
<Grid>
<Grid.Column width="16">
<Header
floated="left"
icon="user"
content={`About ${profile?.displayName}`}
/>
{isCurrentUser && (
<Button
floated="right"
basic
content={editMode ? 'Cancel' : 'Edit Profile'}
onClick={() => setEditMode(!editMode)}
/>
)}
</Grid.Column>
<Grid.Column width="16">
{editMode ? (
<ProfileEditForm setEditMode={setEditMode} />
) : (
<span style={{ whiteSpace: 'pre-wrap' }}>{profile?.bio}</span>
)}
</Grid.Column>
</Grid>
</TabPane>
);
});
8 changes: 7 additions & 1 deletion client-app/src/features/profiles/ProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ interface Props {
}

export default observer(function ProfileCard({ profile }: Props) {
function truncate(str: string | undefined) {
if (str) {
return str.length > 40 ? str.substring(0, 37) + '...' : str;
}
}

return (
<Card as={Link} to={`/profiles/${profile.username}`}>
<Image src={profile.image || '/assets/user.png'} />
<Card.Content>
<Card.Header>{profile.displayName}</Card.Header>
<Card.Description>Bio goes here</Card.Description>
<Card.Description>{truncate(profile.bio)}</Card.Description>
</Card.Content>
<Card.Content extra>
<Icon name="user" />
Expand Down
3 changes: 2 additions & 1 deletion client-app/src/features/profiles/ProfileContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { observer } from 'mobx-react-lite';
import { Tab, TabPane } from 'semantic-ui-react';
import { Profile } from '../../app/models/profile';
import ProfilePhotos from './ProfilePhotos';
import ProfileAbout from './ProfileAbout';

interface Props {
profile: Profile;
}

export default observer(function ProfileContent({ profile }: Props) {
const panes = [
{ menuItem: 'About', render: () => <TabPane>About Content</TabPane> },
{ menuItem: 'About', render: () => <ProfileAbout /> },
{ menuItem: 'Photos', render: () => <ProfilePhotos profile={profile} /> },
{ menuItem: 'Events', render: () => <TabPane>Events Content</TabPane> },
{
Expand Down
43 changes: 43 additions & 0 deletions client-app/src/features/profiles/ProfileEditForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Form, Formik } from 'formik';
import { observer } from 'mobx-react-lite';
import { Button } from 'semantic-ui-react';
import { useStore } from '../../app/stores/store';
import * as Yup from 'yup';
import TextInput from '../../app/common/form/TextInput';
import TextAreaInput from '../../app/common/form/TextArea';

interface Props {
setEditMode: (editMode: boolean) => void;
}
export default observer(function ProfileEditForm({ setEditMode }: Props) {
const {
profileStore: { profile, updateProfile },
} = useStore();
return (
<Formik
initialValues={{ displayName: profile?.displayName, bio: profile?.bio }}
onSubmit={(values) => {
updateProfile(values).then(() => {
setEditMode(false);
});
}}
validationSchema={Yup.object({
displayName: Yup.string().required(),
})}
>
{({ isSubmitting, isValid, dirty }) => (
<Form className="ui form">
<TextInput placeholder="Display Name" name="displayName" />
<TextAreaInput rows={3} placeholder="Add your bio" name="bio" />
<Button
positivetype="submit"
loading={isSubmitting}
content="Update profile"
floated="right"
disabled={!isValid || !dirty}
/>
</Form>
)}
</Formik>
);
});

0 comments on commit 286e5f8

Please sign in to comment.