From f74069e6c5e4bae25d5d5c2eda868e03f8798507 Mon Sep 17 00:00:00 2001 From: Diego Alzate Date: Thu, 20 Jun 2024 09:09:46 +0100 Subject: [PATCH 01/15] initial account hub (#598) * remove redundant type in voting utils * make input validation cleaner * extract account form * make account form title dynamic * fix build issues * create tab manager * add fallback to tab manager * fix typo and add lucid react * add new query apis and show list in account hub --- package.json | 6 +- packages/api/src/fetchUserOptions.ts | 24 ++ packages/api/src/index.ts | 1 + packages/api/src/types/CycleType.ts | 1 + packages/api/src/types/RegistrationType.ts | 12 +- packages/api/src/types/UserOptionType.ts | 21 ++ .../{form => form-input}/FormInput.tsx | 16 +- .../FormNumberInput.tsx} | 28 +- .../FormSelectInput.tsx} | 2 +- .../FormTextAreaInput.tsx} | 30 +-- .../FormTextInput.tsx} | 33 +-- .../berlin/src/components/form-input/index.ts | 5 + .../src/components/form/AccountForm.tsx | 130 +++++++++ .../ResearchGroupForm.tsx | 8 +- packages/berlin/src/components/form/index.ts | 7 +- .../src/components/header/Header.styled.tsx | 2 +- .../berlin/src/components/header/Header.tsx | 94 +------ .../components/research-group-form/index.ts | 1 - .../src/components/tab-manager/TabManager.tsx | 13 + .../src/components/tab-manager/index.ts | 1 + packages/berlin/src/pages/Account.tsx | 248 ++++++++---------- packages/berlin/src/pages/Register.tsx | 2 +- .../src/pages/SecretGroupRegistration.tsx | 2 +- packages/berlin/src/utils/validation.ts | 12 + packages/berlin/src/utils/voting.ts | 15 +- pnpm-lock.yaml | 12 + 26 files changed, 408 insertions(+), 318 deletions(-) create mode 100644 packages/api/src/fetchUserOptions.ts create mode 100644 packages/api/src/types/UserOptionType.ts rename packages/berlin/src/components/{form => form-input}/FormInput.tsx (77%) rename packages/berlin/src/components/{form/NumberInput.tsx => form-input/FormNumberInput.tsx} (57%) rename packages/berlin/src/components/{form/SelectInput.tsx => form-input/FormSelectInput.tsx} (94%) rename packages/berlin/src/components/{form/TextAreaInput.tsx => form-input/FormTextAreaInput.tsx} (52%) rename packages/berlin/src/components/{form/TextInput.tsx => form-input/FormTextInput.tsx} (50%) create mode 100644 packages/berlin/src/components/form-input/index.ts create mode 100644 packages/berlin/src/components/form/AccountForm.tsx rename packages/berlin/src/components/{research-group-form => form}/ResearchGroupForm.tsx (92%) delete mode 100644 packages/berlin/src/components/research-group-form/index.ts create mode 100644 packages/berlin/src/components/tab-manager/TabManager.tsx create mode 100644 packages/berlin/src/components/tab-manager/index.ts create mode 100644 packages/berlin/src/utils/validation.ts diff --git a/package.json b/package.json index bb190c8b..460ea736 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,6 @@ "version": "1.0.0", "type": "module", "scripts": { - "core:dev": "pnpm -r --filter core dev", - "core:build": "pnpm -r --filter core build", - "core:preview": "pnpm -r --filter core preview", "berlin:dev": "pnpm -r --filter berlin dev", "berlin:build": "pnpm -r --filter berlin build", "berlin:preview": "pnpm -r --filter berlin preview", @@ -29,6 +26,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@tanstack/react-query": "^5.13.4", "@tanstack/react-query-devtools": "^5.13.5", + "lucide-react": "^0.395.0", "react": "^18.2.0", "react-content-loader": "^7.0.0", "react-dom": "^18.2.0", @@ -57,4 +55,4 @@ "vite": "^5.0.0", "vite-plugin-node-polyfills": "^0.17.0" } -} +} \ No newline at end of file diff --git a/packages/api/src/fetchUserOptions.ts b/packages/api/src/fetchUserOptions.ts new file mode 100644 index 00000000..eff058d9 --- /dev/null +++ b/packages/api/src/fetchUserOptions.ts @@ -0,0 +1,24 @@ +import { GetUserOptionsResponse } from './types/UserOptionType'; + +async function fetchUserOptions(userId: string): Promise { + try { + const response = await fetch(`${import.meta.env.VITE_SERVER_URL}/api/users/${userId}/options`, { + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`HTTP Error! Status: ${response.status}`); + } + + const userOptions = (await response.json()) as { data: GetUserOptionsResponse }; + return userOptions.data; + } catch (error) { + console.error('Error fetching user options:', error); + return null; + } +} + +export default fetchUserOptions; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 5fd17c38..5f3de8b5 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -37,4 +37,5 @@ export { default as putRegistration } from './putRegistration'; export { default as putUser } from './putUser'; export { default as putUsersToGroups } from './putUsersToGroups'; export { default as fetchEventGroupCategories } from './fetchEventGroupCategories'; +export { default as fetchUserOptions } from './fetchUserOptions'; export * from './types'; diff --git a/packages/api/src/types/CycleType.ts b/packages/api/src/types/CycleType.ts index 66977300..e920862e 100644 --- a/packages/api/src/types/CycleType.ts +++ b/packages/api/src/types/CycleType.ts @@ -1,5 +1,6 @@ export type GetCycleResponse = { id: string; + eventId: string; createdAt: string; updatedAt: string; status: 'OPEN' | 'CLOSED' | 'UPCOMING' | null; diff --git a/packages/api/src/types/RegistrationType.ts b/packages/api/src/types/RegistrationType.ts index da9e0855..768e7645 100644 --- a/packages/api/src/types/RegistrationType.ts +++ b/packages/api/src/types/RegistrationType.ts @@ -1,4 +1,4 @@ -export type RegistrationStatus = 'DRAFT' | 'APPROVED' | 'PUBLISHED' | null; +export type RegistrationStatus = 'DRAFT' | 'APPROVED' | 'REJECTED' | null; export type GetRegistrationResponseType = { id?: string | undefined; @@ -8,6 +8,16 @@ export type GetRegistrationResponseType = { eventId?: string | undefined; createdAt: string; updatedAt: string; + event?: { + id: string; + name: string; + imageUrl: string; + link: string | null; + registrationDescription: string | null; + createdAt: string; + updatedAt: string; + description: string | null; + }; }; export type GetRegistrationsResponseType = GetRegistrationResponseType[]; diff --git a/packages/api/src/types/UserOptionType.ts b/packages/api/src/types/UserOptionType.ts new file mode 100644 index 00000000..fb0697b4 --- /dev/null +++ b/packages/api/src/types/UserOptionType.ts @@ -0,0 +1,21 @@ +export type GetUserOptionsResponse = { + id: string; + createdAt: Date; + updatedAt: Date; + userId: string | null; + registrationId: string | null; + questionId: string; + optionTitle: string; + optionSubTitle: string | null; + accepted: boolean | null; + voteScore: string; + fundingRequest: string | null; + forumQuestion: { + id: string; + createdAt: string; + updatedAt: string; + description: string | null; + cycleId: string; + title: string; + }; +}[]; diff --git a/packages/berlin/src/components/form/FormInput.tsx b/packages/berlin/src/components/form-input/FormInput.tsx similarity index 77% rename from packages/berlin/src/components/form/FormInput.tsx rename to packages/berlin/src/components/form-input/FormInput.tsx index 2f5bc69f..706e87f3 100644 --- a/packages/berlin/src/components/form/FormInput.tsx +++ b/packages/berlin/src/components/form-input/FormInput.tsx @@ -1,8 +1,8 @@ import { FieldValues, Path, UseFormReturn } from 'react-hook-form'; -import { SelectInput } from './SelectInput'; -import { TextAreaInput } from './TextAreaInput'; -import { TextInput } from './TextInput'; -import { NumberInput } from './NumberInput'; +import { FormSelectInput } from './FormSelectInput'; +import { FormTextAreaInput } from './FormTextAreaInput'; +import { FormTextInput } from './FormTextInput'; +import { FormNumberInput } from './FormNumberInput'; export function FormInput(props: { form: UseFormReturn; @@ -15,7 +15,7 @@ export function FormInput(props: { switch (props.type) { case 'TEXT': return ( - (props: { ); case 'TEXTAREA': return ( - (props: { ); case 'SELECT': return ( - (props: { ); case 'NUMBER': return ( - (props: { +export function FormNumberInput(props: { form: UseFormReturn; name: Path; label: string; required: boolean | null; customValidation?: (value: number) => string | undefined; }) { - const handleChange = ( - val: string, - onSuccess: (val: string) => void, - onFailure: (errorMsg: string) => void, - ) => { - if (props.customValidation) { - const customError = props.customValidation(Number(val)); - if (customError) { - onFailure(customError); - return; - } - } - onSuccess(val); - }; - return ( ( (props: { : [] } value={field.value ?? undefined} - onChange={(e) => - handleChange(e.target.value, field.onChange, (err) => { - props.form.setError(props.name, { message: err }); - }) - } + onChange={field.onChange} + onBlur={field.onBlur} /> )} /> diff --git a/packages/berlin/src/components/form/SelectInput.tsx b/packages/berlin/src/components/form-input/FormSelectInput.tsx similarity index 94% rename from packages/berlin/src/components/form/SelectInput.tsx rename to packages/berlin/src/components/form-input/FormSelectInput.tsx index 102beaed..a3263ac2 100644 --- a/packages/berlin/src/components/form/SelectInput.tsx +++ b/packages/berlin/src/components/form-input/FormSelectInput.tsx @@ -1,7 +1,7 @@ import { Controller, FieldValues, Path, UseFormReturn } from 'react-hook-form'; import Select from '../select'; -export function SelectInput(props: { +export function FormSelectInput(props: { form: UseFormReturn; name: Path; label: string; diff --git a/packages/berlin/src/components/form/TextAreaInput.tsx b/packages/berlin/src/components/form-input/FormTextAreaInput.tsx similarity index 52% rename from packages/berlin/src/components/form/TextAreaInput.tsx rename to packages/berlin/src/components/form-input/FormTextAreaInput.tsx index 13d550d3..6d02feb7 100644 --- a/packages/berlin/src/components/form/TextAreaInput.tsx +++ b/packages/berlin/src/components/form-input/FormTextAreaInput.tsx @@ -1,32 +1,21 @@ import { Controller, FieldValues, Path, UseFormReturn } from 'react-hook-form'; import Textarea from '../textarea'; -export function TextAreaInput(props: { +export function FormTextAreaInput(props: { form: UseFormReturn; name: Path; label: string; required: boolean | null; - customValidation?: (value: number) => string | undefined; + customValidation?: (value: string) => string | undefined; }) { - const handleChange = ( - val: string, - onSuccess: (val: string) => void, - onFailure: (errorMsg: string) => void, - ) => { - if (props.customValidation) { - const customError = props.customValidation(Number(val)); - if (customError) { - onFailure(customError); - return; - } - } - onSuccess(val); - }; - return ( (