diff --git a/actions/admin/admin.ts b/actions/admin/admin.ts index 9a150702..0ce4f11e 100644 --- a/actions/admin/admin.ts +++ b/actions/admin/admin.ts @@ -5,11 +5,17 @@ import { PostRequest, PatchRequest, } from '@/base-api/method'; -import { inviteMentorProps } from '@/types/requests'; +import type { inviteMentorProps } from '@/types/requests'; +import type { getStartedMentorFormValues } from '@/types/get-started'; +import { ToggleMentorStatusProps } from '@/types/mentor'; +import { CreateOrganizationProps } from '@/types/admin'; + import { revalidateTag } from 'next/cache'; const Url = { adminAccount: `admin/account`, adminMentors: `admin/mentor`, + mentorProfile: `mentor/profile`, + inviteAdmin: `admin/user`, }; export const checkAccount = async (params: string) => { @@ -21,17 +27,16 @@ export const checkAccount = async (params: string) => { return data; }; -export const createOrganazation = async ( - id: string, - body: { name: string; domain: string } -) => { +export const createOrganazation = async ({ + id, + body, +}: CreateOrganizationProps) => { const putRequest = new PatchRequest( `${Url.adminAccount}/${id}`, 'createOrg', body ); - const data = await putRequest.putData(); - return data; + return await putRequest.patchData(); }; export const deleteMentor = async (id: string) => { @@ -59,6 +64,75 @@ export const inviteMentore = async (body: inviteMentorProps) => { return data; }; +export const submitMentorForm = async ( + formData: getStartedMentorFormValues, + accountId: string +) => { + // Transform data to match backend format + const backendData = { + expertise: formData.expertise.reduce( + (acc, exp) => { + const expertiseMap: Record = { + marriageCounseling: 'Expert in marriage counseling', + discipleship: 'Expert in discipleship counseling', + spritual: 'Expert in spiritual counseling', + dayToDay: 'Expert in day-to-day counseling', + lifeCoach: 'Expert in life coaching', + psychology: 'Expert in psychological counseling', + }; + const key = + exp + .replace(/Counseling$/, 'Counselor') + .replace(/([A-Z])/g, ' $1') + .trim() + .toLowerCase() + 'Counselor'; + acc[key] = expertiseMap[exp] || `Expert in ${exp}`; + return acc; + }, + {} as Record + ), + capacity: formData.capacity, + availability: Object.entries(formData.availability) + .filter(([_, value]) => value !== undefined) + .reduce( + (acc, [day, value]) => { + if (!value) return acc; + + const formattedDay = day.charAt(0).toUpperCase() + day.slice(1); + const startTime = value.startTime; + const endTime = value.endTime; + const timeRange = `${startTime.hour}:${startTime.minute} ${startTime.dayPeriod} - ${endTime.hour}:${endTime.minute} ${endTime.dayPeriod}`; + + acc[formattedDay] = [timeRange]; + return acc; + }, + {} as Record + ), + age: formData.age, + gender: formData.gender.toUpperCase(), + location: formData.location, + }; + + const url = `${Url.mentorProfile}?accountId=${accountId}`; + + const patchRequest = new PatchRequest(url, 'submit-mentor-form', backendData); + return await patchRequest.patchData(); +}; + +export const toggleMentorStatus = async ({ + mentorId, + accountId, + isActive, +}: ToggleMentorStatusProps) => { + const url = `${Url.inviteAdmin}/${mentorId}/activate/${accountId}`; + const patchRequest = new PatchRequest(url, 'toggle-mentor-status', { + isActive, + }); + const data = await patchRequest.patchData(); + revalidateTag('admin-mentors'); + return data; +}; + export async function revalidateWithLogging(tag: string) { return revalidateTag(tag); } diff --git a/base-api/method.ts b/base-api/method.ts index 708233c4..9b14e3fe 100644 --- a/base-api/method.ts +++ b/base-api/method.ts @@ -48,12 +48,23 @@ class PostRequest extends ApiRequest { } } -// PUT request subclass +// PATCH request subclass class PatchRequest extends ApiRequest { constructor(url: string, tag: string, data: unknown) { super(url, tag, data, 'PATCH'); } + public async patchData() { + return this.executeRequest(); + } +} + +// PUT request subclass +class PutRequest extends ApiRequest { + constructor(url: string, tag: string, data: unknown) { + super(url, tag, data, 'PUT'); + } + public async putData() { return this.executeRequest(); } @@ -70,4 +81,4 @@ class DeleteRequest extends ApiRequest { } } -export { GetRequest, PostRequest, PatchRequest, DeleteRequest }; +export { GetRequest, PostRequest, PatchRequest, PutRequest, DeleteRequest }; diff --git a/components/shared/admin/admin-mentors/mentors.tsx b/components/shared/admin/admin-mentors/mentors.tsx index 1405829c..a3bf294a 100644 --- a/components/shared/admin/admin-mentors/mentors.tsx +++ b/components/shared/admin/admin-mentors/mentors.tsx @@ -8,12 +8,16 @@ import type { Column, FilterOption } from '@/types/data-table'; import { toast } from '@/hooks/use-toast'; import { InviteMentorDialog } from './invite-mentor-dialog'; import { endPoints } from '@/data/end-points'; -import { deleteMentor } from '@/actions/admin/admin'; +import { deleteMentor, toggleMentorStatus } from '@/actions/admin/admin'; import type { Account } from '@/types/users'; import { userProfile } from '@/actions/auth/login'; import { Mentor } from '@/types/mentor'; +import { Switch } from '@/components/ui/switch'; -const columns: Column[] = [ +const createColumns = ( + client: Account | null, + setTriggerState: React.Dispatch> +): Column[] => [ { key: 'name', header: 'Name', @@ -30,41 +34,91 @@ const columns: Column[] = [ { key: 'age', header: 'Age', - render: (mentor: Mentor) => (mentor.age === null ? 'null' : mentor.age), + render: (mentor: Mentor) => (mentor.age === null ? 'N/A' : mentor.age), }, { key: 'gender', header: 'Gender' }, { key: 'expertise', header: 'Expertise', render: (mentor: Mentor) => - mentor.expertise === null ? 'null' : mentor.expertise, + mentor.expertise + ? Object.entries(mentor.expertise) + .map(([key, description]) => `${key}: ${description}`) + .join(', ') + : 'N/A', }, { key: 'availability', header: 'Availability', render: (mentor: Mentor) => - mentor.availability === null - ? 'null' - : mentor.availability?.startDate || 'null', + mentor.availability + ? Object.entries(mentor.availability) + .map(([day, times]) => `${day}: ${times.join(', ')}`) + .join('; ') + : 'N/A', + }, + { + key: 'capacity', + header: 'Capacity', + render: (mentor: Mentor) => + mentor.capacity === null ? 'N/A' : mentor.capacity, }, { key: 'location', header: 'Location', - render: (mentor: Mentor) => - mentor.location === null ? 'null' : mentor.location, + render: (mentor: Mentor) => (mentor.location ? mentor.location : 'N/A'), }, { key: 'isActive', header: 'Status', render: (mentor: Mentor) => ( - {mentor.isActive ? 'Yes' : 'No'} +
+ { + try { + if (!client?.id) { + throw new Error('User account ID missing'); + } + + await toggleMentorStatus({ + mentorId: String(mentor.id), + accountId: client.id, + isActive: checked, + }); + + mentor.isActive = checked; + setTriggerState((prev) => !prev); + + toast({ + variant: 'success', + title: 'Status Updated!', + description: `Mentor status has been ${checked ? 'activated' : 'deactivated'}.`, + }); + } catch (error) { + console.error('Status toggle error:', error); + toast({ + variant: 'destructive', + title: 'Error!', + description: + error instanceof Error + ? error.message + : 'Failed to update mentor status', + }); + } + }} + /> + + {mentor.isActive ? 'Active' : 'Inactive'} + +
), }, ]; const filterOptions: FilterOption[] = [ - { key: 'isActive', label: 'Yes' }, - { key: 'isActive', label: 'No' }, + { key: 'gender', label: 'FEMALE' }, + { key: 'gender', label: 'MALE' }, ]; const searchFields: (keyof Mentor)[] = ['name', 'email', 'gender', 'isActive']; @@ -81,6 +135,7 @@ const MentorsTable: React.FC = () => { }; fetchUserProfile(); }, []); + const endPoint = `${endPoints.adminMentors}?accountId=${clientUser?.id}`; const [itemsPerPage, onItemsPerPageChange] = useState(10); @@ -130,7 +185,7 @@ const MentorsTable: React.FC = () => { tag="admin-mentors" apiUrl={endPoint} - columns={columns} + columns={createColumns(clientUser, setTriggerState)} searchFields={searchFields} filterOptions={filterOptions} itemsPerPage={itemsPerPage} diff --git a/components/shared/data-table.tsx b/components/shared/data-table.tsx index 2ee8a3e8..187c9437 100644 --- a/components/shared/data-table.tsx +++ b/components/shared/data-table.tsx @@ -33,8 +33,6 @@ import { DropdownMenuTrigger, } from '@radix-ui/react-dropdown-menu'; -// ... other imports - export interface Column { key: keyof T; header: string; @@ -99,7 +97,7 @@ const DataTable = ({ if (response && response.data) { setData(response.data); - setTotalPages(response.meta.totalPages); // Use meta.totalPages for pagination + setTotalPages(response.meta.totalPages); } else { throw new Error('Invalid response format'); } @@ -120,14 +118,11 @@ const DataTable = ({ const handleSearchChange = (event: React.ChangeEvent) => { setSearchTerm(event.target.value); }; - // Apply search and filters to the data const filteredData = data.filter((item) => { - // Apply search const matchesSearch = searchFields.some((field) => item[field]?.toString().toLowerCase().includes(searchTerm.toLowerCase()) ); - // Apply filters const matchesFilters = filters.every((filter) => { const [key, value] = filter.split(':'); return item[key as keyof T]?.toString() === value; @@ -136,13 +131,6 @@ const DataTable = ({ return matchesSearch && matchesFilters; }); - // Filter data based on search term - // const filteredData = data.filter((item) => - // searchFields.some((field) => - // item[field]?.toString().toLowerCase().includes(searchTerm.toLowerCase()) - // ) - // ); - const handleDelete = (id: string | number) => { setDeleteDialog({ open: true, id }); }; diff --git a/components/shared/get-started/fields/capacity-field.tsx b/components/shared/get-started/fields/capacity-field.tsx new file mode 100644 index 00000000..c5db3ab8 --- /dev/null +++ b/components/shared/get-started/fields/capacity-field.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import type { AgeFieldProps } from '@/types/get-started'; + +export const CapacityField = ({ control, className }: AgeFieldProps) => { + return ( + ( + + Capacity + + { + const value = e.target.value; + field.onChange(value); + }} + /> + + + + )} + /> + ); +}; diff --git a/components/shared/get-started/fields/gender-field.tsx b/components/shared/get-started/fields/gender-field.tsx index 9a1c84b7..99281791 100644 --- a/components/shared/get-started/fields/gender-field.tsx +++ b/components/shared/get-started/fields/gender-field.tsx @@ -1,3 +1,5 @@ +'use client'; + import { FormField, FormItem, @@ -13,13 +15,13 @@ export function GenderField({ control, options, className }: GenderFieldProps) { return ( ( - + Gender diff --git a/components/shared/get-started/fields/specialization-field.tsx b/components/shared/get-started/fields/specialization-field.tsx index 24eac87b..c6f7b795 100644 --- a/components/shared/get-started/fields/specialization-field.tsx +++ b/components/shared/get-started/fields/specialization-field.tsx @@ -6,12 +6,9 @@ import { FormLabel, } from '@/components/ui/form'; import { Checkbox } from '@/components/ui/checkbox'; -import { SpecializationFieldProps } from '@/types/get-started'; +import { ExpertiseFieldProps } from '@/types/get-started'; -export function SpecializationField({ - control, - options, -}: SpecializationFieldProps) { +export function SpecializationField({ control, options }: ExpertiseFieldProps) { return ( { + const [loading, setLoading] = useState(false); + const [clientUser, setClientUser] = useState( + initialUser || null + ); + const router = useRouter(); + + useEffect(() => { + if (!initialUser) { + const fetchUserProfile = async () => { + try { + const userAccount = await userProfile(); + setClientUser(userAccount); + } catch (error) { + console.error('Failed to fetch user profile:', error); + } + }; + fetchUserProfile(); + } + }, [initialUser]); + + const defaultTime: timeType = { + hour: getStartedForm.hours[0].value, + minute: getStartedForm.minutes[0].value, + dayPeriod: getStartedForm.dayPeriods[0].value, + }; -const GetStartedMentorForm = () => { const form = useForm({ resolver: zodResolver(getStartedMentorFormSchema), mode: 'onChange', @@ -23,7 +61,8 @@ const GetStartedMentorForm = () => { age: 29, gender: 'male', location: '', - specialization: ['marriageCounseling'], + expertise: ['marriageCounseling'], + capacity: 5, availability: { monday: undefined, tuesday: undefined, @@ -36,16 +75,39 @@ const GetStartedMentorForm = () => { }, }); - const router = useRouter(); // Initialize the useRouter hook + const onSubmit = async (data: getStartedMentorFormValues) => { + if (!clientUser?.id) { + toast({ + variant: 'destructive', + title: 'Authentication Error', + description: 'Please sign in to submit the mentor form.', + }); + return; + } - const onSubmit = (data: getStartedMentorFormValues) => { - // Navigate to /mentor after form submission - router.push('/mentor'); // Use router.push for smooth navigation - }; + try { + setLoading(true); + await submitMentorForm(data, clientUser.id); - const { - formState: { errors }, - } = form; + toast({ + variant: 'success', + title: 'Profile Updated!', + description: 'Your mentor profile has been successfully updated.', + }); + + router.push('/mentor'); + } catch (error) { + console.error('Submission error:', error); + toast({ + variant: 'destructive', + title: 'Update Failed', + description: + 'There was an error updating your profile. Please try again.', + }); + } finally { + setLoading(false); + } + }; return (
@@ -65,9 +127,14 @@ const GetStartedMentorForm = () => { control={form.control} options={getStartedForm.specializationOptions} /> + - diff --git a/components/views/admin/create-org.tsx b/components/views/admin/create-org.tsx index 95018c9e..9adbbb3c 100644 --- a/components/views/admin/create-org.tsx +++ b/components/views/admin/create-org.tsx @@ -68,10 +68,11 @@ const CreateOrgView = () => { domain: orgData.companyDomain as string, }; - const response = await createOrganazation( - clientUser?.id as string, - reqBody - ); + const response = await createOrganazation({ + id: clientUser?.id as string, + body: reqBody, + }); + if (response) { toast({ variant: 'success', diff --git a/package-lock.json b/package-lock.json index 79eb1e9f..1295ee9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,9 +89,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -808,9 +808,9 @@ } }, "node_modules/@next/env": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz", - "integrity": "sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -824,9 +824,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.7.tgz", - "integrity": "sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", "cpu": [ "arm64" ], @@ -840,9 +840,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.7.tgz", - "integrity": "sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", "cpu": [ "x64" ], @@ -856,9 +856,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.7.tgz", - "integrity": "sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", "cpu": [ "arm64" ], @@ -872,9 +872,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.7.tgz", - "integrity": "sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", "cpu": [ "arm64" ], @@ -888,9 +888,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz", - "integrity": "sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", "cpu": [ "x64" ], @@ -904,9 +904,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz", - "integrity": "sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", "cpu": [ "x64" ], @@ -920,9 +920,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.7.tgz", - "integrity": "sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", "cpu": [ "arm64" ], @@ -936,9 +936,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz", - "integrity": "sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", "cpu": [ "x64" ], @@ -5534,9 +5534,9 @@ } }, "node_modules/axios": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", - "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -8739,12 +8739,12 @@ } }, "node_modules/next": { - "version": "15.1.7", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.7.tgz", - "integrity": "sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", "license": "MIT", "dependencies": { - "@next/env": "15.1.7", + "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -8759,14 +8759,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.7", - "@next/swc-darwin-x64": "15.1.7", - "@next/swc-linux-arm64-gnu": "15.1.7", - "@next/swc-linux-arm64-musl": "15.1.7", - "@next/swc-linux-x64-gnu": "15.1.7", - "@next/swc-linux-x64-musl": "15.1.7", - "@next/swc-win32-arm64-msvc": "15.1.7", - "@next/swc-win32-x64-msvc": "15.1.7", + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { diff --git a/types/admin.ts b/types/admin.ts index 01029d68..385f906c 100644 --- a/types/admin.ts +++ b/types/admin.ts @@ -12,3 +12,8 @@ export interface Admin { status: string; profileImage: string; } + +export interface CreateOrganizationProps { + id: string; + body: { name: string; domain: string }; +} diff --git a/types/get-started.ts b/types/get-started.ts index 43b0d7ca..1ec91556 100644 --- a/types/get-started.ts +++ b/types/get-started.ts @@ -1,12 +1,13 @@ +import type React from 'react'; import { getStartedForm } from '@/data/get-started-data'; -import { Dispatch, SetStateAction } from 'react'; -import { UseFormReturn } from 'react-hook-form'; +import type { Dispatch, SetStateAction } from 'react'; +import type { UseFormReturn } from 'react-hook-form'; import { z } from 'zod'; const hourValues = getStartedForm.hours.map((hour) => hour.value) as [ string, ...string[], -]; // type assertion ensures that it is a tuple with atleast one string +]; const minuteValues = getStartedForm.minutes.map((minute) => minute.value) as [ string, ...string[], @@ -34,7 +35,7 @@ export type dailyAvailabilityType = z.infer; // the zod schema for the form One requirements export const getStartedMentorFormSchema = z.object({ age: z.preprocess( - (value) => (value === '' ? undefined : Number(value)), // changes the shadcn input which returns a string for the age to a number + (value) => (value === '' ? undefined : Number(value)), z .number() .min(9, { message: 'Age must be at least 9.' }) @@ -51,7 +52,8 @@ export const getStartedMentorFormSchema = z.object({ .max(30, { message: 'location must not be longer than 30 characters.', }), - specialization: z + + expertise: z .array( z.enum([ 'marriageCounseling', @@ -62,11 +64,18 @@ export const getStartedMentorFormSchema = z.object({ 'psychology', ]), { - // is an array that can have one or more of these fields - required_error: 'You need to select at least one specialization', + required_error: 'You need to select at least one expertise', } ) - .min(1, 'You need to select at least one specialization'), + .min(1, 'You need to select at least one expertise'), + + capacity: z.preprocess( + (value) => (value === '' ? undefined : Number(value)), + z + .number() + .min(1, { message: 'Capacity must be at least 1.' }) + .max(20, { message: 'Capacity must be no more than 20.' }) + ), availability: z.object({ monday: z.union([dailyAvailabilitySchema, z.undefined()]), tuesday: z.union([dailyAvailabilitySchema, z.undefined()]), @@ -77,52 +86,6 @@ export const getStartedMentorFormSchema = z.object({ sunday: z.union([dailyAvailabilitySchema, z.undefined()]), }), }); -// .refine( -// (data) => { -// const parseTime = (time: timeType) => { -// const hour = parseInt(time.hour, 10); -// const minute = parseInt(time.minute, 10); -// return (time.dayPeriod === "PM" ? (hour % 12) + 12 : hour % 12) * 60 + minute; -// }; - -// return Object.entries(data.availability).every(([day, times]) => { -// if (!times) return true; // Skip days with no availability -// const startTime = parseTime(times.startTime); -// const endTime = parseTime(times.endTime); -// return startTime < endTime; -// }); -// }, -// { -// message: "Start time must be earlier than end time for each day.", -// path: ["availability"], // Highlight the `availability` field in errors -// } -// ); -// .refine( -// (data) => { -// const parseTime = (hour: string, minute: string, period: string) => { -// const h = parseInt(hour, 10); -// const m = parseInt(minute, 10); -// return (period === "PM" ? (h % 12) + 12 : h % 12) * 60 + m; -// }; - -// const startTime = parseTime( -// data.startHour, -// data.startMinute, -// data.startDayPeriod -// ); -// const endTime = parseTime( -// data.endHour, -// data.endMinute, -// data.endDayPeriod -// ); - -// return startTime < endTime; -// }, -// { -// message: "", -// path: ["startHour"], // Attach to startHour or a relevant field -// } -// ); export type getStartedMentorFormValues = z.infer< typeof getStartedMentorFormSchema @@ -130,7 +93,6 @@ export type getStartedMentorFormValues = z.infer< export type availabilityType = z.infer; -// for the Availability popup in the mentor form export const MentorAvailabilityFormSchema = z.object({ availability: z.object({ monday: z.union([dailyAvailabilitySchema, z.undefined()]), @@ -142,26 +104,6 @@ export const MentorAvailabilityFormSchema = z.object({ sunday: z.union([dailyAvailabilitySchema, z.undefined()]), }), }); -// .refine( -// (data) => { -// const parseTime = (time: timeType) => { -// const hour = parseInt(time.hour, 10); -// const minute = parseInt(time.minute, 10); -// return (time.dayPeriod === "PM" ? (hour % 12) + 12 : hour % 12) * 60 + minute; -// }; - -// return Object.entries(data.availability).every(([day, times]) => { -// if (!times) return true; // Skip days with no availability -// const startTime = parseTime(times.startTime); -// const endTime = parseTime(times.endTime); -// return startTime < endTime; -// }); -// }, -// { -// message: "Start time must be earlier than end time for each day.", -// path: ["availability"], // Highlight the `availability` field in errors -// } -// ); export type MentorAvailabilityFormValues = z.infer< typeof MentorAvailabilityFormSchema @@ -173,7 +115,7 @@ export type AvailabilityType = z.infer< export const getStartedAdminFormSchema = z.object({ age: z.preprocess( - (value) => (value === '' ? undefined : Number(value)), // changes the shadcn input which returns a string for the age to a number + (value) => (value === '' ? undefined : Number(value)), z .number() .min(9, { message: 'Age must be at least 9.' }) @@ -193,9 +135,9 @@ export const getStartedAdminFormSchema = z.object({ phoneNumber: z.string().refine( (value) => { if (value.startsWith('+')) { - return value.length === 13 && /^\+\d{12}$/.test(value); // Starts with + and followed by 12 digits + return value.length === 13 && /^\+\d{12}$/.test(value); } else { - return value.length === 10 && /^\d{10}$/.test(value); // Exactly 10 digits with no + + return value.length === 10 && /^\d{10}$/.test(value); } }, { @@ -208,16 +150,22 @@ export type getStartedAdminFormValues = z.infer< typeof getStartedAdminFormSchema >; +// Update interface props to use expertise instead of specialization export interface AgeFieldProps { control: any; className?: string; } -export interface SpecializationFieldProps { +export interface ExpertiseFieldProps { control: any; options: { label: string; value: string }[]; } +export interface CapacityFieldProps { + control: any; + className?: string; +} + export interface LocationFieldProps { control: any; } diff --git a/types/mentor.ts b/types/mentor.ts index 0ec77065..51192b45 100644 --- a/types/mentor.ts +++ b/types/mentor.ts @@ -1,22 +1,28 @@ import { colors } from '@/components/shared/Mentor/Info'; -import { Account } from './users'; +//import { Account } from './users'; -// types/mentor.ts export interface Mentor { id: string | number; accountId: string; name: string; email: string; - expertise?: string | null; + expertise?: { [key: string]: string } | null; age?: number | null; gender: string; location?: string | null; - availability?: { startDate: string } | null; + availability?: { [key: string]: string[] } | null; + capacity?: number | null; isActive: boolean; createdAt: string; updatedAt: string; } +export interface ToggleMentorStatusProps { + mentorId: string | number; + accountId: string; + isActive: boolean; +} + export type User = { id: string; name: string; diff --git a/types/requests/index.ts b/types/requests/index.ts index 05ef8789..c645aca2 100644 --- a/types/requests/index.ts +++ b/types/requests/index.ts @@ -20,3 +20,20 @@ export interface inviteMentorProps { name: string; email: string; } + +export interface GetStartedMentorFormValues { + age: number; + gender: 'male' | 'female'; + location: string; + capacity: number; + specialization: string[]; + availability: { + monday?: string; + tuesday?: string; + wednesday?: string; + thursday?: string; + friday?: string; + saturday?: string; + sunday?: string; + }; +}