Update form

This commit is contained in:
Owen
2025-11-13 21:05:13 -05:00
committed by Owen Schwartz
parent 19f8cda3d9
commit bb5594ab2f
2 changed files with 175 additions and 99 deletions

View File

@@ -2386,7 +2386,5 @@
"maintenanceTime": "e.g., 2 hours, Nov 1 at 5:00 PM", "maintenanceTime": "e.g., 2 hours, Nov 1 at 5:00 PM",
"maintenanceEstimatedTimeDescription": "When you expect maintenance to be completed", "maintenanceEstimatedTimeDescription": "When you expect maintenance to be completed",
"editDomain": "Edit Domain", "editDomain": "Edit Domain",
"editDomainDescription": "Select a domain for your resource", "editDomainDescription": "Select a domain for your resource"
"maintenanceModeDisabledTooltip": "This feature requires a valid license to enable."
} }

View File

@@ -61,7 +61,13 @@ import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusCo
import { useUserContext } from "@app/hooks/useUserContext"; import { useUserContext } from "@app/hooks/useUserContext";
import { Alert, AlertDescription } from "@app/components/ui/alert"; import { Alert, AlertDescription } from "@app/components/ui/alert";
import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip"; import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from "@app/components/ui/tooltip";
import { LicenseOrSubscriptionRequiredAlert } from "@app/components/SecurityFeaturesAlert";
export default function GeneralForm() { export default function GeneralForm() {
const [formKey, setFormKey] = useState(0); const [formKey, setFormKey] = useState(0);
@@ -122,7 +128,7 @@ export default function GeneralForm() {
maintenanceModeType: z.enum(["forced", "automatic"]).optional(), maintenanceModeType: z.enum(["forced", "automatic"]).optional(),
maintenanceTitle: z.string().max(255).optional(), maintenanceTitle: z.string().max(255).optional(),
maintenanceMessage: z.string().max(2000).optional(), maintenanceMessage: z.string().max(2000).optional(),
maintenanceEstimatedTime: z.string().max(100).optional(), maintenanceEstimatedTime: z.string().max(100).optional()
}) })
.refine( .refine(
(data) => { (data) => {
@@ -155,9 +161,12 @@ export default function GeneralForm() {
// enableProxy: resource.enableProxy || false // enableProxy: resource.enableProxy || false
maintenanceModeEnabled: resource.maintenanceModeEnabled || false, maintenanceModeEnabled: resource.maintenanceModeEnabled || false,
maintenanceModeType: resource.maintenanceModeType || "automatic", maintenanceModeType: resource.maintenanceModeType || "automatic",
maintenanceTitle: resource.maintenanceTitle || "We'll be back soon!", maintenanceTitle:
maintenanceMessage: resource.maintenanceMessage || "We are currently performing scheduled maintenance. Please check back soon.", resource.maintenanceTitle || "We'll be back soon!",
maintenanceEstimatedTime: resource.maintenanceEstimatedTime || "", maintenanceMessage:
resource.maintenanceMessage ||
"We are currently performing scheduled maintenance. Please check back soon.",
maintenanceEstimatedTime: resource.maintenanceEstimatedTime || ""
}, },
mode: "onChange" mode: "onChange"
}); });
@@ -193,7 +202,7 @@ export default function GeneralForm() {
const rawDomains = res.data.data.domains as DomainRow[]; const rawDomains = res.data.data.domains as DomainRow[];
const domains = rawDomains.map((domain) => ({ const domains = rawDomains.map((domain) => ({
...domain, ...domain,
baseDomain: toUnicode(domain.baseDomain), baseDomain: toUnicode(domain.baseDomain)
})); }));
setBaseDomains(domains); setBaseDomains(domains);
setFormKey((key) => key + 1); setFormKey((key) => key + 1);
@@ -220,7 +229,9 @@ export default function GeneralForm() {
enabled: data.enabled, enabled: data.enabled,
name: data.name, name: data.name,
niceId: data.niceId, niceId: data.niceId,
subdomain: data.subdomain ? toASCII(data.subdomain) : undefined, subdomain: data.subdomain
? toASCII(data.subdomain)
: undefined,
domainId: data.domainId, domainId: data.domainId,
proxyPort: data.proxyPort, proxyPort: data.proxyPort,
// ...(!resource.http && { // ...(!resource.http && {
@@ -230,7 +241,8 @@ export default function GeneralForm() {
maintenanceModeType: data.maintenanceModeType, maintenanceModeType: data.maintenanceModeType,
maintenanceTitle: data.maintenanceTitle || null, maintenanceTitle: data.maintenanceTitle || null,
maintenanceMessage: data.maintenanceMessage || null, maintenanceMessage: data.maintenanceMessage || null,
maintenanceEstimatedTime: data.maintenanceEstimatedTime || null, maintenanceEstimatedTime:
data.maintenanceEstimatedTime || null
} }
) )
.catch((e) => { .catch((e) => {
@@ -261,7 +273,7 @@ export default function GeneralForm() {
maintenanceModeType: data.maintenanceModeType, maintenanceModeType: data.maintenanceModeType,
maintenanceTitle: data.maintenanceTitle || null, maintenanceTitle: data.maintenanceTitle || null,
maintenanceMessage: data.maintenanceMessage || null, maintenanceMessage: data.maintenanceMessage || null,
maintenanceEstimatedTime: data.maintenanceEstimatedTime || null, maintenanceEstimatedTime: data.maintenanceEstimatedTime || null
}); });
toast({ toast({
@@ -270,7 +282,9 @@ export default function GeneralForm() {
}); });
if (data.niceId && data.niceId !== resource?.niceId) { if (data.niceId && data.niceId !== resource?.niceId) {
router.replace(`/${updated.orgId}/settings/resources/${data.niceId}/general`); router.replace(
`/${updated.orgId}/settings/resources/${data.niceId}/general`
);
} else { } else {
router.refresh(); router.refresh();
} }
@@ -355,11 +369,15 @@ export default function GeneralForm() {
name="niceId" name="niceId"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("identifier")}</FormLabel> <FormLabel>
{t("identifier")}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
{...field} {...field}
placeholder={t("enterIdentifier")} placeholder={t(
"enterIdentifier"
)}
className="flex-1" className="flex-1"
/> />
</FormControl> </FormControl>
@@ -395,10 +413,10 @@ export default function GeneralForm() {
.target .target
.value .value
? parseInt( ? parseInt(
e e
.target .target
.value .value
) )
: undefined : undefined
) )
} }
@@ -485,20 +503,6 @@ export default function GeneralForm() {
</Form> </Form>
</SettingsSectionForm> </SettingsSectionForm>
</SettingsSectionBody> </SettingsSectionBody>
<SettingsSectionFooter>
<Button
type="submit"
onClick={() => {
console.log(form.getValues());
}}
loading={saveLoading}
disabled={saveLoading}
form="general-settings-form"
>
{t("saveSettings")}
</Button>
</SettingsSectionFooter>
</SettingsSection> </SettingsSection>
</SettingsContainer> </SettingsContainer>
@@ -515,6 +519,8 @@ export default function GeneralForm() {
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<LicenseOrSubscriptionRequiredAlert />
<SettingsSectionForm> <SettingsSectionForm>
<Form {...form}> <Form {...form}>
<form className="space-y-4"> <form className="space-y-4">
@@ -526,45 +532,49 @@ export default function GeneralForm() {
isSecurityFeatureDisabled(); isSecurityFeatureDisabled();
return ( return (
<FormItem> <FormItem>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<FormControl> <FormControl>
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger
asChild
>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span <SwitchInput
className={isDisabled ? "pointer-events-auto" : ""} id="enable-maintenance"
> checked={
<SwitchInput field.value
id="enable-maintenance" }
checked={field.value} label={t(
label={t("enableMaintenanceMode")} "enableMaintenanceMode"
disabled={isDisabled} )}
onCheckedChange={(val) => { disabled={
if (!isDisabled) { isDisabled
form.setValue("maintenanceModeEnabled", val); }
} onCheckedChange={(
}} val
/> ) => {
</span> if (
!isDisabled
) {
form.setValue(
"maintenanceModeEnabled",
val
);
}
}}
/>
</div> </div>
</TooltipTrigger> </TooltipTrigger>
{isDisabled && (
<TooltipContent className="max-w-xs">
<p>{t("maintenanceModeDisabledTooltip")}</p>
</TooltipContent>
)}
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
</FormControl> </FormControl>
</div> </div>
<FormDescription> <FormDescription>
{t("showMaintenancePage")} {t(
"showMaintenancePage"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -580,12 +590,19 @@ export default function GeneralForm() {
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-3"> <FormItem className="space-y-3">
<FormLabel> <FormLabel>
{t("maintenanceModeType")} {t(
"maintenanceModeType"
)}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<RadioGroup <RadioGroup
onValueChange={field.onChange} onValueChange={
defaultValue={field.value} field.onChange
}
defaultValue={
field.value
}
disabled={isSecurityFeatureDisabled()}
className="flex flex-col space-y-1" className="flex flex-col space-y-1"
> >
<FormItem className="flex items-start space-x-3 space-y-0"> <FormItem className="flex items-start space-x-3 space-y-0">
@@ -594,10 +611,22 @@ export default function GeneralForm() {
</FormControl> </FormControl>
<div className="space-y-1 leading-none"> <div className="space-y-1 leading-none">
<FormLabel className="font-normal"> <FormLabel className="font-normal">
<strong>{t("automatic")}</strong> ({t("recommended")}) <strong>
{t(
"automatic"
)}
</strong>{" "}
(
{t(
"recommended"
)}
)
</FormLabel> </FormLabel>
<FormDescription> <FormDescription>
{t("automaticModeDescription")} {t(
"automaticModeDescription"
)}
</FormDescription> </FormDescription>
</div> </div>
</FormItem> </FormItem>
@@ -607,10 +636,16 @@ export default function GeneralForm() {
</FormControl> </FormControl>
<div className="space-y-1 leading-none"> <div className="space-y-1 leading-none">
<FormLabel className="font-normal"> <FormLabel className="font-normal">
<strong>{t("forced")}</strong> <strong>
{t(
"forced"
)}
</strong>
</FormLabel> </FormLabel>
<FormDescription> <FormDescription>
{t("forcedModeDescription")} {t(
"forcedModeDescription"
)}
</FormDescription> </FormDescription>
</div> </div>
</FormItem> </FormItem>
@@ -621,11 +656,19 @@ export default function GeneralForm() {
)} )}
/> />
{maintenanceModeType === "forced" && ( {maintenanceModeType ===
"forced" && (
<Alert> <Alert>
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertDescription> <AlertDescription>
<strong>{t("warning:")}</strong> {t("forcedeModeWarning")} <strong>
{t(
"warning:"
)}
</strong>{" "}
{t(
"forcedeModeWarning"
)}
</AlertDescription> </AlertDescription>
</Alert> </Alert>
)} )}
@@ -635,15 +678,22 @@ export default function GeneralForm() {
name="maintenanceTitle" name="maintenanceTitle"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("pageTitle")}</FormLabel> <FormLabel>
{t(
"pageTitle"
)}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
{...field} {...field}
disabled={isSecurityFeatureDisabled()}
placeholder="We'll be back soon!" placeholder="We'll be back soon!"
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t("pageTitleDescription")} {t(
"pageTitleDescription"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -655,16 +705,25 @@ export default function GeneralForm() {
name="maintenanceMessage" name="maintenanceMessage"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("maintenancePageMessage")}</FormLabel> <FormLabel>
{t(
"maintenancePageMessage"
)}
</FormLabel>
<FormControl> <FormControl>
<Textarea <Textarea
{...field} {...field}
rows={4} rows={4}
placeholder={t("maintenancePageMessagePlaceholder")} disabled={isSecurityFeatureDisabled()}
placeholder={t(
"maintenancePageMessagePlaceholder"
)}
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t("maintenancePageMessageDescription")} {t(
"maintenancePageMessageDescription"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -677,16 +736,23 @@ export default function GeneralForm() {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
{t("maintenancePageTimeTitle")} {t(
"maintenancePageTimeTitle"
)}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
{...field} {...field}
placeholder={t("maintenanceTime")} disabled={isSecurityFeatureDisabled()}
placeholder={t(
"maintenanceTime"
)}
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t("maintenanceEstimatedTimeDescription")} {t(
"maintenanceEstimatedTimeDescription"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -698,24 +764,24 @@ export default function GeneralForm() {
</Form> </Form>
</SettingsSectionForm> </SettingsSectionForm>
</SettingsSectionBody> </SettingsSectionBody>
<SettingsSectionFooter>
<Button
type="submit"
onClick={() => {
console.log(form.getValues());
}}
loading={saveLoading}
disabled={saveLoading}
form="general-settings-form"
>
{t("saveSettings")}
</Button>
</SettingsSectionFooter>
</SettingsSection> </SettingsSection>
</SettingsContainer> </SettingsContainer>
)} )}
<div className="flex justify-end">
<Button
type="submit"
onClick={() => {
console.log(form.getValues());
}}
loading={saveLoading}
disabled={saveLoading}
form="general-settings-form"
>
{t("saveSettings")}
</Button>
</div>
<Credenza <Credenza
open={editDomainOpen} open={editDomainOpen}
onOpenChange={(setOpen) => setEditDomainOpen(setOpen)} onOpenChange={(setOpen) => setEditDomainOpen(setOpen)}
@@ -749,17 +815,29 @@ export default function GeneralForm() {
<Button <Button
onClick={() => { onClick={() => {
if (selectedDomain) { if (selectedDomain) {
const sanitizedSubdomain = selectedDomain.subdomain const sanitizedSubdomain =
? finalizeSubdomainSanitize(selectedDomain.subdomain) selectedDomain.subdomain
: ""; ? finalizeSubdomainSanitize(
selectedDomain.subdomain
)
: "";
const sanitizedFullDomain = sanitizedSubdomain const sanitizedFullDomain =
? `${sanitizedSubdomain}.${selectedDomain.baseDomain}` sanitizedSubdomain
: selectedDomain.baseDomain; ? `${sanitizedSubdomain}.${selectedDomain.baseDomain}`
: selectedDomain.baseDomain;
setResourceFullDomain(`${resource.ssl ? "https" : "http"}://${sanitizedFullDomain}`); setResourceFullDomain(
form.setValue("domainId", selectedDomain.domainId); `${resource.ssl ? "https" : "http"}://${sanitizedFullDomain}`
form.setValue("subdomain", sanitizedSubdomain); );
form.setValue(
"domainId",
selectedDomain.domainId
);
form.setValue(
"subdomain",
sanitizedSubdomain
);
setEditDomainOpen(false); setEditDomainOpen(false);
} }