From 8257dca340efea160dd5f41f1cc70a0eaf179fa2 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Fri, 12 Dec 2025 23:34:35 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proxy/[niceId]/authentication/page.tsx | 362 +++++++++++------- 1 file changed, 216 insertions(+), 146 deletions(-) diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx index 0bd61e7a..15c8e197 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx @@ -34,6 +34,7 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; +import type { ResourceContextType } from "@app/contexts/resourceContext"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { useResourceContext } from "@app/hooks/useResourceContext"; @@ -58,7 +59,14 @@ import type { text } from "express"; import { Binary, Bot, InfoIcon, Key } from "lucide-react"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; -import { useEffect, useMemo, useRef, useState, useTransition } from "react"; +import { + useActionState, + useEffect, + useMemo, + useRef, + useState, + useTransition +} from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -200,10 +208,6 @@ export default function ResourceAuthenticationPage() { resource.skipToIdpId || null ); - const [loadingSaveUsersRoles, setLoadingSaveUsersRoles] = useState(false); - const [loadingSaveWhitelist, startSaveWhitelistTransition] = - useTransition(); - const [loadingRemoveResourcePassword, setLoadingRemoveResourcePassword] = useState(false); const [loadingRemoveResourcePincode, setLoadingRemoveResourcePincode] = @@ -309,12 +313,18 @@ export default function ResourceAuthenticationPage() { } } - async function onSubmitUsersRoles( - data: z.infer - ) { - try { - setLoadingSaveUsersRoles(true); + const [, submitUserRolesForm, loadingSaveUsersRoles] = useActionState( + onSubmitUsersRoles, + null + ); + async function onSubmitUsersRoles() { + const isValid = usersRolesForm.trigger(); + if (!isValid) return; + + const data = usersRolesForm.getValues(); + + try { // Validate that an IDP is selected if auto login is enabled if (autoLoginEnabled && !selectedIdpId) { toast({ @@ -364,8 +374,6 @@ export default function ResourceAuthenticationPage() { t("resourceErrorUsersRolesSaveDescription") ) }); - } finally { - setLoadingSaveUsersRoles(false); } } @@ -529,9 +537,7 @@ export default function ResourceAuthenticationPage() {
@@ -859,138 +865,202 @@ export default function ResourceAuthenticationPage() { - - - - {t("otpEmailTitle")} - - - {t("otpEmailTitleDescription")} - - - - - {!env.email.emailEnabled && ( - - - - {t("otpEmailSmtpRequired")} - - - {t("otpEmailSmtpRequiredDescription")} - - - )} - - - {whitelistEnabled && env.email.emailEnabled && ( - - - ( - - - - - - {/* @ts-ignore */} - { - return z - .email() - .or( - z - .string() - .regex( - /^\*@[\w.-]+\.[a-zA-Z]{2,}$/, - { - message: - t( - "otpEmailErrorInvalid" - ) - } - ) - ) - .safeParse( - tag - ).success; - }} - setActiveTagIndex={ - setActiveEmailTagIndex - } - placeholder={t( - "otpEmailEnter" - )} - tags={ - whitelistForm.getValues() - .emails - } - setTags={( - newRoles - ) => { - whitelistForm.setValue( - "emails", - newRoles as [ - Tag, - ...Tag[] - ] - ); - }} - allowDuplicates={ - false - } - sortTags={true} - /> - - - {t( - "otpEmailEnterDescription" - )} - - - )} - /> - - - )} - - - - - -
+ ); } + +type OneTimePasswordFormSectionProps = Pick< + ResourceContextType, + "resource" | "updateResource" +>; + +function OneTimePasswordFormSection({ + resource, + updateResource +}: OneTimePasswordFormSectionProps) { + const { env } = useEnvContext(); + const [whitelistEnabled, setWhitelistEnabled] = useState( + resource.emailWhitelistEnabled + ); + const queryClient = useQueryClient(); + + const [loadingSaveWhitelist, startTransition] = useTransition(); + const whitelistForm = useForm({ + resolver: zodResolver(whitelistSchema), + defaultValues: { emails: [] } + }); + const api = createApiClient({ env }); + const router = useRouter(); + const t = useTranslations(); + + const [activeEmailTagIndex, setActiveEmailTagIndex] = useState< + number | null + >(null); + + async function saveWhitelist() { + try { + await api.post(`/resource/${resource.resourceId}`, { + emailWhitelistEnabled: whitelistEnabled + }); + + if (whitelistEnabled) { + await api.post(`/resource/${resource.resourceId}/whitelist`, { + emails: whitelistForm.getValues().emails.map((i) => i.text) + }); + } + + updateResource({ + emailWhitelistEnabled: whitelistEnabled + }); + + toast({ + title: t("resourceWhitelistSave"), + description: t("resourceWhitelistSaveDescription") + }); + router.refresh(); + await queryClient.invalidateQueries( + resourceQueries.resourceWhitelist({ + resourceId: resource.resourceId + }) + ); + } catch (e) { + console.error(e); + toast({ + variant: "destructive", + title: t("resourceErrorWhitelistSave"), + description: formatAxiosError( + e, + t("resourceErrorWhitelistSaveDescription") + ) + }); + } + } + + return ( + + + + {t("otpEmailTitle")} + + + {t("otpEmailTitleDescription")} + + + + + {!env.email.emailEnabled && ( + + + + {t("otpEmailSmtpRequired")} + + + {t("otpEmailSmtpRequiredDescription")} + + + )} + + + {whitelistEnabled && env.email.emailEnabled && ( +
+ + ( + + + + + + {/* @ts-ignore */} + { + return z + .email() + .or( + z + .string() + .regex( + /^\*@[\w.-]+\.[a-zA-Z]{2,}$/, + { + message: + t( + "otpEmailErrorInvalid" + ) + } + ) + ) + .safeParse(tag) + .success; + }} + setActiveTagIndex={ + setActiveEmailTagIndex + } + placeholder={t( + "otpEmailEnter" + )} + tags={ + whitelistForm.getValues() + .emails + } + setTags={(newRoles) => { + whitelistForm.setValue( + "emails", + newRoles as [ + Tag, + ...Tag[] + ] + ); + }} + allowDuplicates={false} + sortTags={true} + /> + + + {t("otpEmailEnterDescription")} + + + )} + /> + + + )} +
+
+ + + +
+ ); +}