diff --git a/.gitignore b/.gitignore index fe65afbc..8131447b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,8 @@ dev/ dev.* .env.local +# claude code local worktree scratch +.claude/worktrees/ + # prettier .prettierignore \ No newline at end of file diff --git a/public/locales/de/translations.json b/public/locales/de/translations.json index 26e0489b..3546840f 100644 --- a/public/locales/de/translations.json +++ b/public/locales/de/translations.json @@ -1250,8 +1250,14 @@ "validation": { "required": "Dieses Feld ist erforderlich", "websiteInvalid": "Webseite muss mit http:// oder https:// beginnen", + "operatorRequired": "Es sind mindestens 3 Zeichen erforderlich.", + "operatorHint": "Geben Sie die ersten drei Buchstaben ein, damit die Liste angezeigt wird, und wählen Sie aus der Liste aus. Wenn die Liste nicht erscheint, ist der Träger ungültig.", + "operatorInvalid": "Ungültiger Träger. Bitte wählen Sie aus der Liste oder versuchen Sie es mit einem anderen Träger.", "clientLanguagesRequired": "Mindestens eine Sprache ist erforderlich" }, + "placeholders": { + "operatorPlaceholder": "Geben Sie die ersten drei Buchstaben des Trägers ein" + }, "addressStreet": "Straße", "addressPostcode": "PLZ" }, diff --git a/public/locales/en/translations.json b/public/locales/en/translations.json index a500dda7..b4b74665 100644 --- a/public/locales/en/translations.json +++ b/public/locales/en/translations.json @@ -1238,8 +1238,14 @@ "validation": { "required": "This field is required", "websiteInvalid": "Website must start with http:// or https://", + "operatorRequired": "A minimum of 3 characters is needed", + "operatorHint": "Type first three letters for the list to appear and select from the list. If the list doesn't appear, Operator is invalid.", + "operatorInvalid": "Operator invalid. Either select from the list or try another Operator", "clientLanguagesRequired": "At least one language is required" }, + "placeholders": { + "operatorPlaceholder": "Type the first three letters of the operator" + }, "addressStreet": "Street", "addressPostcode": "Postcode" }, diff --git a/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetails.tsx b/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetails.tsx index a73cff8c..96b8caae 100644 --- a/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetails.tsx +++ b/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetails.tsx @@ -18,6 +18,7 @@ import { apiLanguagesToFormValues, toLanguagesForForm } from "./formatters"; import { OrganisationDetailsDisplay } from "./OrganisationDetailsDisplay"; import { OrganisationDetailsEdit } from "./OrganisationDetailsEdit"; import { createOrganisationDetailsSchema, OrganisationDetailsFormData } from "./organisationDetailsSchema"; +import { useGetOrganization } from "@/hooks/useGetOrganization"; type Props = { agent: ApiAgentProfileGet; @@ -35,8 +36,10 @@ export const OrganisationDetails = forwardRef(functio const { data: apiLanguages } = useApiLanguages(); const { data: apiAgentTypes = [] } = useApiAgentTypes(); const { data: apiServices = [] } = useApiServices(); + const { data: organizations = [] } = useGetOrganization(); const agentTypeMapping = useMemo(() => createMapping(apiAgentTypes), [apiAgentTypes]); const serviceMapping = useMemo(() => createMapping(apiServices), [apiServices]); + const organizationMapping = useMemo(() => createMapping(organizations), [organizations]); const details = agent.agentDetails; const languagesForForm = toLanguagesForForm(apiLanguages, i18n.language); @@ -96,6 +99,7 @@ export const OrganisationDetails = forwardRef(functio const serviceIds = values.services .map((title) => serviceMapping.titleToId[title]) .filter((id): id is number => id !== undefined); + const organizationId = organizationMapping.titleToId[values.operator]; updateOrganization( { @@ -106,6 +110,7 @@ export const OrganisationDetails = forwardRef(functio addressPostcode: values.addressPostcode, ...(typeId !== undefined && { typeId }), serviceIds, + ...(organizationId !== undefined && { organizationId }), }, { onSuccess: () => { @@ -124,6 +129,7 @@ export const OrganisationDetails = forwardRef(functio languagesForForm={languagesForForm} organizationTypeOptions={apiAgentTypes.map((agentType) => agentType.title)} servicesOptions={apiServices.map((service) => service.title)} + operatorOptions={organizations?.map((org) => org.title)} onCancel={handleCancel} onSubmit={handleSubmit(onSubmit)} /> diff --git a/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetailsDisplay.tsx b/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetailsDisplay.tsx index 963ac48d..7ee60342 100644 --- a/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetailsDisplay.tsx +++ b/src/components/Dashboard/Profile/sections/OrganisationDetails/OrganisationDetailsDisplay.tsx @@ -16,7 +16,6 @@ export const OrganisationDetailsDisplay = ({ rawClientLanguages, address }: Prop const { t } = useTranslation(); const { watch } = useFormContext(); const values = watch(); - return ( void; onSubmit: () => void; }; @@ -22,6 +24,7 @@ export const OrganisationDetailsEdit = ({ languagesForForm, organizationTypeOptions, servicesOptions, + operatorOptions, onCancel, onSubmit, }: Props) => { @@ -31,6 +34,16 @@ export const OrganisationDetailsEdit = ({ formState: { errors, isDirty, isValid }, } = useFormContext(); + const displayOperators = useCallback( + (value: string) => { + if (value?.length >= 3) { + return operatorOptions.filter((op) => op.toLowerCase().includes(value.toLowerCase())); + } else { + return []; + } + }, + [operatorOptions], + ); return ( <> @@ -123,14 +136,19 @@ export const OrganisationDetailsEdit = ({ name="operator" control={control} render={({ field }) => ( - + <> + + )} /> { getValue: () => T; @@ -559,6 +559,72 @@ export const EditableField = forwardRef(function EditableField )} + + {type === "autocomplete" && ( + + { + if (localValue) setOpen((o) => !o); + }} + > + + { + const v = e.target.value as T; + setLocalValue(v); + setValue(v); + setOpen(true); + }} + onBlur={handleSubmit} + onKeyDown={handleKeyDown} + style={{ border: "none", padding: 0 }} + /> + {Boolean(localValue) && ( + { + e.stopPropagation(); + const v = "" as T; + setLocalValue(v); + setValue(v); + setOpen(false); + }} + style={{ alignSelf: "flex-start", marginTop: "0px", marginRight: "0px" }} + > + + + )} + + + + {open && ( + + {options.map((option) => { + const isSelected = localValue === option; + return ( + { + const v = option as T; + setLocalValue(v); + setValue(v); + setOpen(false); + }} + > + {}} /> + {option} + + ); + })} + + )} + + )} {error && (

diff --git a/src/hooks/useGetOrganization.ts b/src/hooks/useGetOrganization.ts new file mode 100644 index 00000000..6deb1967 --- /dev/null +++ b/src/hooks/useGetOrganization.ts @@ -0,0 +1,19 @@ +import { ApiOrganizationGetList } from "need4deed-sdk"; +import { useGetQuery } from "./useGetQuery"; +import { apiPathOrganization, cacheTTL } from "@/config/constants"; + +export const useGetOrganization = () => { + const { data, isLoading, isError, error } = useGetQuery({ + queryKey: ["organization"], + apiPath: `${apiPathOrganization}`, + staleTime: cacheTTL, + addLang: false, + }); + + return { + data, + isLoading, + isError, + error, + }; +};