Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bp 81 update profile form #109

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "role" TEXT,
ADD COLUMN "title" TEXT,
ADD COLUMN "video" TEXT;
3 changes: 3 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ model User {
isEmailVerified Boolean @default(false)
about String?
picture String?
video String?
title String?
role String?
counterStartDate Int @default(0)
weeklyTransactionCounter Int @default(0)
workerProfile WorkerProfile? @relation("WorkerProfile")
Expand Down
9 changes: 8 additions & 1 deletion src/app/api/delegate/service/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ export async function POST(req: Request) {
cid: cid,
});

console.log('Creating service with args', userId, platformId, cid, signature);
console.log(
'Creating service with args',
userId,
platformId,
cid,
signature,
servicePostingFee,
);

transaction = await walletClient.writeContract({
address: config.contracts.serviceRegistry,
Expand Down
65 changes: 62 additions & 3 deletions src/app/api/users/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@ import prisma from '../../../../postgre/postgreClient';
import { logAndReturnApiError } from '../../../utils/handleApiErrors';
import { ERROR_UPDATING_USER } from '../../../../modules/BuilderPlace/apiResponses';
import { IEmailPreferences, IMutation } from '../../../../types';
import { sendTransactionalEmailValidation } from '../../../../pages/api/utils/sendgrid';

export interface UsersFields {
// email?: string | null;
//TODO complete
name?: string;
email?: string;
about?: string;
picture?: string;
video?: string;
title?: string;
role?: string;
workerProfileFields?: WorkerProfileFields;
emailPreferences?: IEmailPreferences | null;
}

interface WorkerProfileFields {
skills?: string[];
}
export interface IUpdateProfile extends IMutation<UsersFields> {}

/**
Expand Down Expand Up @@ -37,15 +48,63 @@ export async function PUT(req: Request, { params }: { params: { id: string } })

try {
console.log('Updating profile...', params.id);

// Fetch current user data from the database
const currentUser = await prisma.user.findUnique({
where: { id: Number(params.id) },
});

if (!currentUser) {
return Response.json({ error: 'User not found' }, { status: 404 });
}

const { workerProfileFields, emailPreferences, ...userDataFields } = body.data;

const isEmailUpdated = !!userDataFields.email && userDataFields.email !== currentUser.email;

let updatedUserDataFields = {
...userDataFields,
// Reset isEmailVerified if email is updated
...(isEmailUpdated && { isEmailVerified: false }),
...(!!emailPreferences && { emailPreferences }),
};

console.log('userDataFields', updatedUserDataFields);
console.log('isEmailUpdated', isEmailUpdated);
console.log('emailPreferences', emailPreferences);
console.log('userDataFields', userDataFields);

// Update User data
const user = await prisma.user.update({
where: {
id: Number(params.id),
},
data: {
emailPreferences: { ...body.data.emailPreferences },
...updatedUserDataFields,
},
});

// Update or create WorkerProfile if skills are provided
if (workerProfileFields?.skills) {
await prisma.workerProfile.upsert({
where: { id: Number(params.id) },
update: { skills: workerProfileFields.skills },
create: {
id: Number(params.id),
skills: workerProfileFields.skills,
},
});
}

// Send validation email if email is updated
if (isEmailUpdated)
await sendTransactionalEmailValidation(
user.email,
user.id.toString(),
user.name,
body?.domain,
);

return Response.json({ id: user?.id }, { status: 200 });
} catch (e: any) {
const error = logAndReturnApiError(e, ERROR_UPDATING_USER);
Expand Down
87 changes: 79 additions & 8 deletions src/components/Form/ProfileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { showErrorTransactionToast } from '../../utils/toast';
import Loading from '../Loading';
import SubmitButton from './SubmitButton';
import { SkillsInput } from './skills-input';
import { CheckIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { createVerificationEmailToast } from '../../modules/BuilderPlace/utils/toast';

export interface IUpdateProfileFormValues {
title?: string;
Expand All @@ -18,6 +20,7 @@ export interface IUpdateProfileFormValues {
name?: string;
about?: string;
skills?: string;
email?: string;
}

const validationSchema = Yup.object({
Expand All @@ -36,14 +39,17 @@ function ProfileForm({ callback }: { callback?: () => void }) {
return <Loading />;
}

const initialSkills = user?.workerProfile?.skills.toString() || userDescription?.skills_raw || '';

const initialValues: IUpdateProfileFormValues = {
title: userDescription?.title || '',
role: userDescription?.role || '',
image_url: userDescription?.image_url || user?.picture || '',
video_url: userDescription?.video_url || '',
name: userDescription?.name || user?.name || '',
about: userDescription?.about || '',
skills: userDescription?.skills_raw || '',
title: user?.title || userDescription?.title || '',
role: user?.role || userDescription?.role || '',
image_url: user?.picture || userDescription?.image_url || user?.picture || '',
video_url: user?.video || userDescription?.video_url || '',
name: user?.name || userDescription?.name || user?.name || '',
about: user?.about || userDescription?.about || '',
skills: initialSkills,
email: user?.email || '',
};

const onSubmit = async (
Expand All @@ -57,6 +63,10 @@ function ProfileForm({ callback }: { callback?: () => void }) {
callback();
}

if (initialValues.email !== values.email) {
await createVerificationEmailToast();
}

refreshData();
setSubmitting(false);
} catch (error) {
Expand Down Expand Up @@ -99,6 +109,7 @@ function ProfileForm({ callback }: { callback?: () => void }) {
<ErrorMessage name='name' />
</span>
</label>

<label className='block'>
<span className='text-base-content'>role</span>
<Field
Expand All @@ -117,6 +128,40 @@ function ProfileForm({ callback }: { callback?: () => void }) {
</span>
</label>

<label className='block'>
<span className='text-base-content'>email</span>
<div className='flex items-center'>
<Field
type='email'
id='email'
name='email'
className='mt-1 mb-1 block w-full rounded-xl border-2 border-info bg-base-200 shadow-sm focus:ring-opacity-50'
placeholder=''></Field>
</div>
{user?.isEmailVerified ? (
<div className={'flex flex-row items-center'}>
<CheckIcon
width={25}
height={25}
className='bg-base-100 bg-success p-1 mx-2 border border-success-100 rounded-full'
/>
<span className='text-sm text-base-content opacity-50'>Email verified</span>
</div>
) : (
<div className={'flex flex-row items-center'}>
<XMarkIcon
width={20}
height={20}
className='bg-base-100 bg-error p-1 mx-2 border border-error-200 rounded-full'
/>
<p className='text-sm text-base-content opacity-50'>Email not verified</p>
</div>
)}
<span className='text-alone-error'>
<ErrorMessage name='email' />
</span>
</label>

<label className='block'>
<span className='text-base-content'>picture url</span>
<Field
Expand All @@ -138,6 +183,32 @@ function ProfileForm({ callback }: { callback?: () => void }) {
</span>
</label>

<label className='block'>
<span className='text-base-content'>video presentation url</span>
<Field
type='text'
id='video_url'
name='video_url'
className='mt-1 mb-1 block w-full rounded-xl border-2 border-info bg-base-200 shadow-sm focus:ring-opacity-50'
placeholder=''
/>
{values.video_url && (
<div className='border-2 border-info bg-base-200 relative w-full transition-all duration-300 rounded-xl p-4'>
<div className='flex items-center justify-center py-3'>
<video>
<source width='300' src={values.video_url} type='video/webm' />
<source src={values.video_url} type='video/mp4' />
Sorry, your browser doesn't support videos.
</video>
<iframe src={values.video_url} width='300' height='auto' allowFullScreen />
</div>
</div>
)}
<span className='text-alone-error'>
<ErrorMessage name='video_url' />
</span>
</label>

<label className='block'>
<span className='text-base-content'>about</span>
<Field
Expand All @@ -156,7 +227,7 @@ function ProfileForm({ callback }: { callback?: () => void }) {
<label className='block'>
<span className='text-base-content'>skills</span>

<SkillsInput initialValues={userDescription?.skills_raw} entityId={'skills'} />
<SkillsInput initialValues={initialSkills} entityId={'skills'} />

<Field type='hidden' id='skills' name='skills' />
<span className='text-alone-error'>
Expand Down
Loading