mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-31 23:29:08 +00:00
clean up a few save buttons
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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<UserTypeOption> = [
|
||||
{
|
||||
id: "internal",
|
||||
title: t("userTypeInternal"),
|
||||
description: t("userTypeInternalDescription")
|
||||
},
|
||||
{
|
||||
id: "oidc",
|
||||
title: t("userTypeExternal"),
|
||||
description: t("userTypeExternalDescription")
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
@@ -320,7 +323,7 @@ export default function Page() {
|
||||
|
||||
<div>
|
||||
<SettingsContainer>
|
||||
{!inviteLink && (
|
||||
{!inviteLink && userTypes.length > 1 ? (
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
@@ -347,7 +350,7 @@ export default function Page() {
|
||||
/>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
{userType === "internal" && dataLoaded && (
|
||||
<>
|
||||
@@ -496,7 +499,9 @@ export default function Page() {
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="send-email"
|
||||
checked={sendEmail}
|
||||
checked={
|
||||
sendEmail
|
||||
}
|
||||
onCheckedChange={(
|
||||
e
|
||||
) =>
|
||||
@@ -528,7 +533,9 @@ export default function Page() {
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{sendEmail
|
||||
? t("inviteEmailSentDescription")
|
||||
? t(
|
||||
"inviteEmailSentDescription"
|
||||
)
|
||||
: t("inviteSentDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
@@ -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")}
|
||||
</Button>
|
||||
|
||||
@@ -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<GeneralFormValues>({
|
||||
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<AxiosResponse<UpdateResourceResponse>>(
|
||||
`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<AxiosResponse<UpdateResourceResponse>>(
|
||||
`resource/${resource.resourceId}`,
|
||||
{
|
||||
enabled: val
|
||||
}
|
||||
)
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("resourceErrorToggle"),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
t("resourceErrorToggleDescription")
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
updateResource({
|
||||
enabled: val
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
!loadingPage && (
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("resourceVisibilityTitle")}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t("resourceVisibilityTitleDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SwitchInput
|
||||
id="enable-resource"
|
||||
label={t("resourceEnable")}
|
||||
defaultChecked={resource.enabled}
|
||||
onCheckedChange={async (val) => {
|
||||
await toggleResourceEnabled(val);
|
||||
}}
|
||||
/>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
@@ -348,6 +307,33 @@ export default function GeneralForm() {
|
||||
className="grid grid-cols-1 md:grid-cols-2 gap-4"
|
||||
id="general-settings-form"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="enable-resource"
|
||||
defaultChecked={resource.enabled}
|
||||
onCheckedChange={(val) => form.setValue("enabled", val)}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1">
|
||||
<FormLabel className="text-base">
|
||||
{t("resourceEnable")}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t("resourceVisibilityTitleDescription")}
|
||||
</FormDescription>
|
||||
</div>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
@@ -612,7 +598,7 @@ export default function GeneralForm() {
|
||||
disabled={saveLoading}
|
||||
form="general-settings-form"
|
||||
>
|
||||
{t("saveGeneralSettings")}
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
|
||||
@@ -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: {
|
||||
{/* </AlertDescription> */}
|
||||
{/* </Alert> */}
|
||||
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>{t('rulesEnable')}</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t('rulesEnableDescription')}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SwitchInput
|
||||
id="rules-toggle"
|
||||
label={t('rulesEnable')}
|
||||
defaultChecked={rulesEnabled}
|
||||
onCheckedChange={async (val) => {
|
||||
await saveApplyRules(val);
|
||||
}}
|
||||
/>
|
||||
</SettingsSectionBody>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
@@ -614,167 +590,186 @@ export default function ResourceRules(props: {
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<Form {...addRuleForm}>
|
||||
<form
|
||||
onSubmit={addRuleForm.handleSubmit(addRule)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 items-end">
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="action"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('rulesAction')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ACCEPT">
|
||||
{RuleAction.ACCEPT}
|
||||
</SelectItem>
|
||||
<SelectItem value="DROP">
|
||||
{RuleAction.DROP}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="match"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('rulesMatchType')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{resource.http && (
|
||||
<SelectItem value="PATH">
|
||||
{RuleMatch.PATH}
|
||||
</SelectItem>
|
||||
)}
|
||||
<SelectItem value="IP">
|
||||
{RuleMatch.IP}
|
||||
</SelectItem>
|
||||
<SelectItem value="CIDR">
|
||||
{RuleMatch.CIDR}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="gap-1">
|
||||
<InfoPopup
|
||||
text={t('value')}
|
||||
info={
|
||||
getValueHelpText(
|
||||
addRuleForm.watch(
|
||||
"match"
|
||||
)
|
||||
) || ""
|
||||
}
|
||||
/>
|
||||
<FormControl>
|
||||
<Input {...field}/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="secondary"
|
||||
disabled={!rulesEnabled}
|
||||
>
|
||||
{t('ruleSubmit')}
|
||||
</Button>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center space-x-2">
|
||||
<SwitchInput
|
||||
id="rules-toggle"
|
||||
defaultChecked={rulesEnabled}
|
||||
onCheckedChange={(val) => setRulesEnabled(val)}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label className="text-base font-medium">
|
||||
{t('rulesEnable')}
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('rulesEnableDescription')}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
</div>
|
||||
|
||||
<Form {...addRuleForm}>
|
||||
<form
|
||||
onSubmit={addRuleForm.handleSubmit(addRule)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 items-end">
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="action"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('rulesAction')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ACCEPT">
|
||||
{RuleAction.ACCEPT}
|
||||
</SelectItem>
|
||||
<SelectItem value="DROP">
|
||||
{RuleAction.DROP}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="match"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('rulesMatchType')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{resource.http && (
|
||||
<SelectItem value="PATH">
|
||||
{RuleMatch.PATH}
|
||||
</SelectItem>
|
||||
)}
|
||||
<SelectItem value="IP">
|
||||
{RuleMatch.IP}
|
||||
</SelectItem>
|
||||
<SelectItem value="CIDR">
|
||||
{RuleMatch.CIDR}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={addRuleForm.control}
|
||||
name="value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="gap-1">
|
||||
<InfoPopup
|
||||
text={t('value')}
|
||||
info={
|
||||
getValueHelpText(
|
||||
addRuleForm.watch(
|
||||
"match"
|
||||
)
|
||||
) || ""
|
||||
}
|
||||
/>
|
||||
<FormControl>
|
||||
<Input {...field}/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="secondary"
|
||||
disabled={!rulesEnabled}
|
||||
>
|
||||
{t('ruleSubmit')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef
|
||||
.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
{t('rulesNoOne')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
{/* <TableCaption> */}
|
||||
{/* {t('rulesOrder')} */}
|
||||
{/* </TableCaption> */}
|
||||
</Table>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
{t('rulesNoOne')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
{/* <TableCaption> */}
|
||||
{/* {t('rulesOrder')} */}
|
||||
{/* </TableCaption> */}
|
||||
</Table>
|
||||
</div>
|
||||
</SettingsSectionBody>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
onClick={saveRules}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
{t('rulesSubmit')}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={saveAllSettings}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
{t('saveAllSettings')}
|
||||
</Button>
|
||||
</div>
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
form="general-settings-form"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
{t("saveGeneralSettings")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
|
||||
<div className="flex justify-end mt-6">
|
||||
<Button
|
||||
type="submit"
|
||||
form="general-settings-form"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
Save All Settings
|
||||
</Button>
|
||||
</div>
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 htmlFor={id}>{label}</Label>
|
||||
{label && <Label htmlFor={id}>{label}</Label>}
|
||||
</div>
|
||||
{description && (
|
||||
<span className="text-muted-foreground text-sm">
|
||||
|
||||
@@ -16,7 +16,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
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<HTMLInputElement, InputProps>(
|
||||
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
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user