From 4ffdd6f74ffd6f039feecf9b90d6129dedd2d343 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 30 Jun 2025 12:28:27 -0700 Subject: [PATCH] clean up a few save buttons --- messages/en-US.json | 1 + .../settings/access/users/create/page.tsx | 50 ++- .../resources/[resourceId]/general/page.tsx | 78 ++-- .../resources/[resourceId]/rules/page.tsx | 405 +++++++++--------- .../settings/sites/[niceId]/general/page.tsx | 25 +- src/components/SwitchInput.tsx | 4 +- src/components/ui/input.tsx | 4 +- src/components/ui/toast.tsx | 2 +- 8 files changed, 282 insertions(+), 287 deletions(-) diff --git a/messages/en-US.json b/messages/en-US.json index 84194997..92246b66 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -206,6 +206,7 @@ "orgGeneralSettings": "Organization Settings", "orgGeneralSettingsDescription": "Manage your organization details and configuration", "saveGeneralSettings": "Save General Settings", + "saveSettings": "Save Settings", "orgDangerZone": "Danger Zone", "orgDangerZoneDescription": "Once you delete this org, there is no going back. Please be certain.", "orgDelete": "Delete Organization", diff --git a/src/app/[orgId]/settings/access/users/create/page.tsx b/src/app/[orgId]/settings/access/users/create/page.tsx index abd11068..a91fd7b9 100644 --- a/src/app/[orgId]/settings/access/users/create/page.tsx +++ b/src/app/[orgId]/settings/access/users/create/page.tsx @@ -147,6 +147,14 @@ export default function Page() { } }, [userType, env.email.emailEnabled, internalForm, externalForm]); + const userTypes: UserTypeOption[] = [ + { + id: "internal", + title: t("userTypeInternal"), + description: t("userTypeInternalDescription") + } + ]; + useEffect(() => { if (!userType) { return; @@ -193,6 +201,14 @@ export default function Page() { if (res?.status === 200) { setIdps(res.data.data.idps); setDataLoaded(true); + + if (res.data.data.idps.length) { + userTypes.push({ + id: "oidc", + title: t("userTypeExternal"), + description: t("userTypeExternalDescription") + }); + } } } @@ -288,19 +304,6 @@ export default function Page() { setLoading(false); } - const userTypes: ReadonlyArray = [ - { - id: "internal", - title: t("userTypeInternal"), - description: t("userTypeInternalDescription") - }, - { - id: "oidc", - title: t("userTypeExternal"), - description: t("userTypeExternalDescription") - } - ]; - return ( <>
@@ -320,7 +323,7 @@ export default function Page() {
- {!inviteLink && ( + {!inviteLink && userTypes.length > 1 ? ( @@ -347,7 +350,7 @@ export default function Page() { /> - )} + ) : null} {userType === "internal" && dataLoaded && ( <> @@ -496,7 +499,9 @@ export default function Page() {
@@ -528,7 +533,9 @@ export default function Page() { {sendEmail - ? t("inviteEmailSentDescription") + ? t( + "inviteEmailSentDescription" + ) : t("inviteSentDescription")} @@ -778,7 +785,14 @@ export default function Page() { form={inviteLink ? undefined : "create-user-form"} loading={loading} disabled={loading} - onClick={inviteLink ? () => router.push(`/${orgId}/settings/access/users`) : undefined} + onClick={ + inviteLink + ? () => + router.push( + `/${orgId}/settings/access/users` + ) + : undefined + } > {inviteLink ? t("done") : t("accessUserCreate")} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index a0c89773..cdff0375 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -102,6 +102,7 @@ export default function GeneralForm() { const GeneralFormSchema = z .object({ + enabled: z.boolean(), subdomain: z.string().optional(), name: z.string().min(1).max(255), proxyPort: z.number().optional(), @@ -144,6 +145,7 @@ export default function GeneralForm() { const form = useForm({ resolver: zodResolver(GeneralFormSchema), defaultValues: { + enabled: resource.enabled, name: resource.name, subdomain: resource.subdomain ? resource.subdomain : undefined, proxyPort: resource.proxyPort ? resource.proxyPort : undefined, @@ -209,6 +211,7 @@ export default function GeneralForm() { .post>( `resource/${resource?.resourceId}`, { + enabled: data.enabled, name: data.name, subdomain: data.http ? data.subdomain : undefined, proxyPort: data.proxyPort, @@ -236,6 +239,7 @@ export default function GeneralForm() { const resource = res.data.data; updateResource({ + enabled: data.enabled, name: data.name, subdomain: data.subdomain, proxyPort: data.proxyPort, @@ -282,54 +286,9 @@ export default function GeneralForm() { setTransferLoading(false); } - async function toggleResourceEnabled(val: boolean) { - const res = await api - .post>( - `resource/${resource.resourceId}`, - { - enabled: val - } - ) - .catch((e) => { - toast({ - variant: "destructive", - title: t("resourceErrorToggle"), - description: formatAxiosError( - e, - t("resourceErrorToggleDescription") - ) - }); - }); - - updateResource({ - enabled: val - }); - } - return ( !loadingPage && ( - - - - {t("resourceVisibilityTitle")} - - - {t("resourceVisibilityTitleDescription")} - - - - { - await toggleResourceEnabled(val); - }} - /> - - - @@ -348,6 +307,33 @@ export default function GeneralForm() { className="grid grid-cols-1 md:grid-cols-2 gap-4" id="general-settings-form" > + ( + +
+ + form.setValue("enabled", val)} + /> + +
+ + {t("resourceEnable")} + + + {t("resourceVisibilityTitleDescription")} + +
+
+ +
+ )} + /> + - {t("saveGeneralSettings")} + {t("saveSettings")}
diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 34a385d0..316e9cfe 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -233,35 +233,6 @@ export default function ResourceRules(props: { ); } - async function saveApplyRules(val: boolean) { - const res = await api - .post(`/resource/${params.resourceId}`, { - applyRules: val - }) - .catch((err) => { - console.error(err); - toast({ - variant: "destructive", - title: t('rulesErrorUpdate'), - description: formatAxiosError( - err, - t('rulesErrorUpdateDescription') - ) - }); - }); - - if (res && res.status === 200) { - setRulesEnabled(val); - updateResource({ applyRules: val }); - - toast({ - title: t('rulesUpdated'), - description: t('rulesUpdatedDescription') - }); - router.refresh(); - } - } - function getValueHelpText(type: string) { switch (type) { case "CIDR": @@ -273,9 +244,33 @@ export default function ResourceRules(props: { } } - async function saveRules() { + async function saveAllSettings() { try { setLoading(true); + + // Save rules enabled state + const res = await api + .post(`/resource/${params.resourceId}`, { + applyRules: rulesEnabled + }) + .catch((err) => { + console.error(err); + toast({ + variant: "destructive", + title: t('rulesErrorUpdate'), + description: formatAxiosError( + err, + t('rulesErrorUpdateDescription') + ) + }); + throw err; + }); + + if (res && res.status === 200) { + updateResource({ applyRules: rulesEnabled }); + } + + // Save rules for (let rule of rules) { const data = { action: rule.action, @@ -585,25 +580,6 @@ export default function ResourceRules(props: { {/* */} {/* */} - - - {t('rulesEnable')} - - {t('rulesEnableDescription')} - - - - { - await saveApplyRules(val); - }} - /> - - - @@ -614,167 +590,186 @@ export default function ResourceRules(props: { -
- -
- ( - - {t('rulesAction')} - - - - - - )} - /> - ( - - {t('rulesMatchType')} - - - - - - )} - /> - ( - - - - - - - - )} - /> - +
+
+ setRulesEnabled(val)} + /> +
+ +

+ {t('rulesEnableDescription')} +

- - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef - .header, - header.getContext() - )} - - ))} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - + + +
+ +
+ ( + + {t('rulesAction')} + + + + + + )} + /> + ( + + {t('rulesMatchType')} + + + + + + )} + /> + ( + + + + + + + + )} + /> + +
+ + +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef + .header, + header.getContext() + )} + ))} - )) - ) : ( - - - {t('rulesNoOne')} - - - )} - - {/* */} - {/* {t('rulesOrder')} */} - {/* */} -
+ ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + {t('rulesNoOne')} + + + )} + + {/* */} + {/* {t('rulesOrder')} */} + {/* */} + +
- - - + +
+ +
); } diff --git a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx index 7b97f99f..ba1f877c 100644 --- a/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx +++ b/src/app/[orgId]/settings/sites/[niceId]/general/page.tsx @@ -24,8 +24,7 @@ import { SettingsSectionTitle, SettingsSectionDescription, SettingsSectionBody, - SettingsSectionForm, - SettingsSectionFooter + SettingsSectionForm } from "@app/components/Settings"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; @@ -177,18 +176,18 @@ export default function GeneralPage() { - - - - + +
+ +
); } diff --git a/src/components/SwitchInput.tsx b/src/components/SwitchInput.tsx index 571a1ab4..3a287d41 100644 --- a/src/components/SwitchInput.tsx +++ b/src/components/SwitchInput.tsx @@ -4,7 +4,7 @@ import { Label } from "./ui/label"; interface SwitchComponentProps { id: string; - label: string; + label?: string; description?: string; defaultChecked?: boolean; disabled?: boolean; @@ -28,7 +28,7 @@ export function SwitchInput({ onCheckedChange={onCheckedChange} disabled={disabled} /> - + {label && }
{description && ( diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 880a44b7..0e9c519a 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -16,7 +16,7 @@ const Input = React.forwardRef( type={showPassword ? "text" : "password"} data-slot="input" className={cn( - "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base inset-shadow-2xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-2xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className @@ -43,7 +43,7 @@ const Input = React.forwardRef( type={type} data-slot="input" className={cn( - "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base inset-shadow-2xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-2xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx index db90cc75..c723859c 100644 --- a/src/components/ui/toast.tsx +++ b/src/components/ui/toast.tsx @@ -31,7 +31,7 @@ const toastVariants = cva( variant: { default: "border bg-card text-foreground", destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground" + "destructive group border-destructive bg-destructive text-white dark:text-destructive-foreground" } }, defaultVariants: {