diff --git a/src/actions/email/sendInvite.ts b/src/actions/email/sendInvite.ts index a180e11..f20be0b 100644 --- a/src/actions/email/sendInvite.ts +++ b/src/actions/email/sendInvite.ts @@ -5,7 +5,6 @@ import sgMail from '@sendgrid/mail'; import { prisma } from '@/prisma/client' - export interface User { firstName: string lastName: string @@ -21,10 +20,9 @@ const getPatientName = async (id: string): Promise => { }) } - - sgMail.setApiKey(process.env.SENDGRID_API_KEY!); +// Send invite email after login (with uid) export async function sendInviteEmail(name: string, email: string, patientId: string) { const patient = await getPatientName(patientId); const patientName = patient ? `${patient.firstName} ${patient.lastName}` : ''; @@ -53,3 +51,32 @@ return { success: true }; return { success: false, error: error.message }; } } + +// Send invite email before login (without uid) +export async function sendInviteEmailDuringRegistration(clinicianName: string, email: string) { + try { + const msg = { + to: email, + from: process.env.SENDGRID_SENDER_EMAIL!, + subject: 'You\'ve been invited to join our platform!', + html: ` +

Dear ${clinicianName},

+

A new patient would like to share their symptoms data with you on our platform.

+

Register your account to view their spidergrams and track their data.

+

Here is a link to our website: https://team3.uksouth.cloudapp.azure.com

+

Kind regards,

+

The Spider team

+ `, + }; + + await sgMail.send(msg); + + return { success: true }; + } catch (error: any) { + console.error('[Invite Email Error]', error); + + return { success: false, error: error.message }; + } +} + + diff --git a/src/views/ClinicianSearch.tsx b/src/views/ClinicianSearch.tsx index 91fbf03..3bcde44 100644 --- a/src/views/ClinicianSearch.tsx +++ b/src/views/ClinicianSearch.tsx @@ -16,12 +16,14 @@ import { DialogActions, IconButton } from '@mui/material' + import { alpha } from '@mui/material/styles' import { toast } from 'react-toastify' import { searchClinicians } from '@/actions/register/registerActions' -import { sendInvitation } from '@/actions/patientSettings/userActions' + +import { sendInviteEmailDuringRegistration } from '@/actions/email/sendInvite'; export interface Clinician { id: string @@ -74,7 +76,6 @@ export const ClinicianSearch = ({ onSaveClinician, savedClinicians }: ClinicianS return () => observer.disconnect() }, []) - const handleOpenInviteModal = () => { setInviteModalOpen(true) } @@ -85,24 +86,28 @@ export const ClinicianSearch = ({ onSaveClinician, savedClinicians }: ClinicianS const handleSendInvitation = async () => { if (!inviteEmail) { - toast?.error("Please enter the clinician's email") || console.error("Please enter the clinician's email") + toast?.error("Please enter the clinician's email"); -return +return; } - + try { - const invite = await sendInvitation(inviteEmail, inviteMessage) - - if (!invite.success) { - throw new Error((invite as any).message || 'Failed to send the invitation') + const name = searchFirstName + ? `${searchFirstName} ${searchLastName || ''}`.trim() + : 'Clinician'; + + const invitation = await sendInviteEmailDuringRegistration(name, inviteEmail); + + if (invitation.success) { + toast?.success('Invitation sent successfully'); + handleCloseInviteModal(); + setInviteEmail(''); + } else { + throw new Error(invitation.error || 'Failed to send the invitation'); } - - toast?.success('Invitation sent successfully') || console.log('Invitation sent successfully') - handleCloseInviteModal() } catch (error) { - console.error('Failed to send invitation:', error) - toast?.error((error as any).message || 'Failed to send the invitation') - console.error((error as any).message || 'Failed to send the invitation') + console.error('Failed to send invitation:', error); + toast?.error((error as any).message || 'Failed to send the invitation'); } } @@ -459,3 +464,7 @@ return ) } + + + + diff --git a/src/views/DateOfBirthPicker.tsx b/src/views/DateOfBirthPicker.tsx index 431c0fc..5cfd6c0 100644 --- a/src/views/DateOfBirthPicker.tsx +++ b/src/views/DateOfBirthPicker.tsx @@ -48,7 +48,10 @@ export const DateOfBirthPicker = ({ error: error, helperText: helperText, placeholder: 'dd/mm/yyyy', - inputProps: { 'aria-label': label } + inputProps: { + readOnly: true, + 'aria-label': label + } } }} format='dd/MM/yyyy' diff --git a/src/views/Login.tsx b/src/views/Login.tsx index 71f7e74..1cee24e 100644 --- a/src/views/Login.tsx +++ b/src/views/Login.tsx @@ -107,11 +107,6 @@ const LoginV2 = ({ mode }: { mode: string }) => {
{`Log in`} - {error && ( - - {error} - - )}
{ Log In + {error && ( + + {error} + + )} + Don't have an account? @@ -186,3 +187,4 @@ const LoginV2 = ({ mode }: { mode: string }) => { export default LoginV2 + diff --git a/src/views/RegisterUser.tsx b/src/views/RegisterUser.tsx index e3b5041..627f590 100644 --- a/src/views/RegisterUser.tsx +++ b/src/views/RegisterUser.tsx @@ -329,10 +329,8 @@ return parsed?.number || phone.trim().replace(/\s/g, '') } if (shouldValidateField('hospitalNumber') && accountType === 'patient') { - if (!formData.hospitalNumber?.trim()) { - errors.hospitalNumber = 'The hospital number is required'; - } else if (!validateHospitalNumber(formData.hospitalNumber)) { - errors.hospitalNumber = 'Please enter the valid format.)'; + if (formData.hospitalNumber?.trim() && !validateHospitalNumber(formData.hospitalNumber)) { + errors.hospitalNumber = 'Please enter the valid format.'; } else if (errors.hospitalNumber && !errors.hospitalNumber.includes('already exists')) { errors.hospitalNumber = ''; } @@ -360,7 +358,7 @@ return parsed?.number || phone.trim().replace(/\s/g, '') } if (shouldValidateField('profession') && accountType === 'clinician') { - errors.profession = !formData.profession?.trim() ? 'Profession is required' : '' + errors.profession = !formData.profession?.trim() ? 'Please select a profession' : '' isValid = isValid && !errors.profession } else { errors.profession = '' @@ -473,10 +471,8 @@ return parsed?.number || phone.trim().replace(/\s/g, '') } if (fieldName === 'hospitalNumber' && accountType === 'patient') { - if (!value.trim()) { - newErrors.hospitalNumber = 'The hospital number is required'; - } else if (!validateHospitalNumber(value)) { - newErrors.hospitalNumber = 'Please enter the valid format)'; + if (value.trim() && !validateHospitalNumber(value)) { + newErrors.hospitalNumber = 'Please enter the valid format.'; } else if (newErrors.hospitalNumber && !newErrors.hospitalNumber.includes('already exists')) { newErrors.hospitalNumber = ''; } @@ -884,8 +880,8 @@ return parsed?.number || phone.trim().replace(/\s/g, '') {accountType === 'patient' && ( <> - Institution {formErrors.institution && ( - + {formErrors.institution} )} - {' '} - { - setTouchedFields(prev => ({ ...prev, profession: true })) - validateForm('profession') - setIsFirstNameFocused(!!formData.profession) - }} - error={!!formErrors.profession} - helperText={formErrors.profession} - InputLabelProps={{ shrink: isProfessionFocused || !!formData.profession }} - onFocus={() => setIsProfessionFocused(true)} - sx={{ mb: 2, '& .MuiOutlinedInput-root': { borderRadius: '8px' } }} - />{' '} - + + Profession + + {formErrors.profession && ( + + {formErrors.profession} + + )} + + )} @@ -1010,6 +1022,7 @@ return parsed?.number || phone.trim().replace(/\s/g, '') Institution