Resource Rules page:

Split into 3 clear sections: Enabled Rules (with explanation), Rule Templates, and Resource Rules Configuration
Hide Rules Configuration when rules are disabled

Rule Template pages:
Rules: adopt Settings section layout; right-aligned “Add Rule” button that opens a Create Rule dialog; remove inline add form; consistent table styling
This commit is contained in:
Adrian Astles
2025-08-08 19:30:26 +08:00
parent 16a88281bb
commit 75cec731e8
5 changed files with 498 additions and 466 deletions

View File

@@ -57,8 +57,7 @@ import {
} from "@app/components/Settings"; } from "@app/components/Settings";
import { ListResourceRulesResponse } from "@server/routers/resource/listResourceRules"; import { ListResourceRulesResponse } from "@server/routers/resource/listResourceRules";
import { SwitchInput } from "@app/components/SwitchInput"; import { SwitchInput } from "@app/components/SwitchInput";
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { ArrowUpDown, Check, X, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
import { ArrowUpDown, Check, InfoIcon, X, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
import { import {
InfoSection, InfoSection,
InfoSections, InfoSections,
@@ -74,6 +73,15 @@ import { Switch } from "@app/components/ui/switch";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { ResourceRulesManager } from "@app/components/ruleTemplate/ResourceRulesManager"; import { ResourceRulesManager } from "@app/components/ruleTemplate/ResourceRulesManager";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from "@app/components/ui/dialog";
// Schema for rule validation // Schema for rule validation
const addRuleSchema = z.object({ const addRuleSchema = z.object({
@@ -103,6 +111,7 @@ export default function ResourceRules(props: {
pageIndex: 0, pageIndex: 0,
pageSize: 25 pageSize: 25
}); });
const [createDialogOpen, setCreateDialogOpen] = useState(false);
const router = useRouter(); const router = useRouter();
const t = useTranslations(); const t = useTranslations();
@@ -574,49 +583,85 @@ export default function ResourceRules(props: {
return ( return (
<SettingsContainer> <SettingsContainer>
{/* <Alert className="hidden md:block"> */} {/* 1. Enabled Rules Control & How it works */}
{/* <InfoIcon className="h-4 w-4" /> */} <SettingsSection>
{/* <AlertTitle className="font-semibold">{t('rulesAbout')}</AlertTitle> */} <SettingsSectionHeader>
{/* <AlertDescription className="mt-4"> */} <SettingsSectionTitle>
{/* <div className="space-y-1 mb-4"> */} {t('rulesEnable')}
{/* <p> */} </SettingsSectionTitle>
{/* {t('rulesAboutDescription')} */} <SettingsSectionDescription>
{/* </p> */} {t('rulesEnableDescription')}
{/* </div> */} </SettingsSectionDescription>
{/* <InfoSections cols={2}> */} </SettingsSectionHeader>
{/* <InfoSection> */} <SettingsSectionBody>
{/* <InfoSectionTitle>{t('rulesActions')}</InfoSectionTitle> */} <div className="flex items-center space-x-2">
{/* <ul className="text-sm text-muted-foreground space-y-1"> */} <SwitchInput
{/* <li className="flex items-center gap-2"> */} id="rules-toggle"
{/* <Check className="text-green-500 w-4 h-4" /> */} label={t('rulesEnable')}
{/* {t('rulesActionAlwaysAllow')} */} defaultChecked={rulesEnabled}
{/* </li> */} onCheckedChange={(val) => setRulesEnabled(val)}
{/* <li className="flex items-center gap-2"> */} />
{/* <X className="text-red-500 w-4 h-4" /> */} </div>
{/* {t('rulesActionAlwaysDeny')} */} <div className="rounded-md border bg-muted/30 p-4">
{/* </li> */} <div className="mb-3 text-sm text-muted-foreground">
{/* </ul> */} {t('rulesAboutDescription')}
{/* </InfoSection> */} </div>
{/* <InfoSection> */} <InfoSections cols={2}>
{/* <InfoSectionTitle> */} <InfoSection>
{/* {t('rulesMatchCriteria')} */} <InfoSectionTitle>{t('rulesActions')}</InfoSectionTitle>
{/* </InfoSectionTitle> */} <ul className="text-sm text-muted-foreground space-y-1">
{/* <ul className="text-sm text-muted-foreground space-y-1"> */} <li className="flex items-center gap-2">
{/* <li className="flex items-center gap-2"> */} <Check className="text-green-500 w-4 h-4" />
{/* {t('rulesMatchCriteriaIpAddress')} */} {t('rulesActionAlwaysAllow')}
{/* </li> */} </li>
{/* <li className="flex items-center gap-2"> */} <li className="flex items-center gap-2">
{/* {t('rulesMatchCriteriaIpAddressRange')} */} <X className="text-red-500 w-4 h-4" />
{/* </li> */} {t('rulesActionAlwaysDeny')}
{/* <li className="flex items-center gap-2"> */} </li>
{/* {t('rulesMatchCriteriaUrl')} */} </ul>
{/* </li> */} </InfoSection>
{/* </ul> */} <InfoSection>
{/* </InfoSection> */} <InfoSectionTitle>{t('rulesMatchCriteria')}</InfoSectionTitle>
{/* </InfoSections> */} <ul className="text-sm text-muted-foreground space-y-1">
{/* </AlertDescription> */} <li className="flex items-center gap-2">
{/* </Alert> */} {t('rulesMatchCriteriaIpAddress')}
</li>
<li className="flex items-center gap-2">
{t('rulesMatchCriteriaIpAddressRange')}
</li>
<li className="flex items-center gap-2">
{t('rulesMatchCriteriaUrl')}
</li>
</ul>
</InfoSection>
</InfoSections>
</div>
</SettingsSectionBody>
</SettingsSection>
{/* 2. Rule Templates Section */}
{rulesEnabled && (
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
{t('ruleTemplates')}
</SettingsSectionTitle>
<SettingsSectionDescription>
{t('ruleTemplatesDescription')}
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<ResourceRulesManager
resourceId={params.resourceId.toString()}
orgId={resource.orgId}
onUpdate={fetchRules}
/>
</SettingsSectionBody>
</SettingsSection>
)}
{/* 3. Resource Rules Configuration */}
{rulesEnabled && (
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle> <SettingsSectionTitle>
@@ -627,22 +672,27 @@ export default function ResourceRules(props: {
</SettingsSectionDescription> </SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody> <SettingsSectionBody>
<div className="space-y-6"> <div className="flex justify-end">
<div className="flex items-center space-x-2"> <Dialog open={createDialogOpen} onOpenChange={setCreateDialogOpen}>
<SwitchInput <DialogTrigger asChild>
id="rules-toggle" <Button variant="secondary">{t('ruleSubmit')}</Button>
label={t('rulesEnable')} </DialogTrigger>
defaultChecked={rulesEnabled} <DialogContent>
onCheckedChange={(val) => setRulesEnabled(val)} <DialogHeader>
/> <DialogTitle>{t('ruleSubmit')}</DialogTitle>
</div> <DialogDescription>
{t('rulesResourceDescription')}
</DialogDescription>
</DialogHeader>
<Form {...addRuleForm}> <Form {...addRuleForm}>
<form <form
onSubmit={addRuleForm.handleSubmit(addRule)} onSubmit={addRuleForm.handleSubmit(async (data) => {
await addRule(data);
setCreateDialogOpen(false);
})}
className="space-y-4" className="space-y-4"
> >
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 items-end"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField <FormField
control={addRuleForm.control} control={addRuleForm.control}
name="action" name="action"
@@ -652,20 +702,14 @@ export default function ResourceRules(props: {
<FormControl> <FormControl>
<Select <Select
value={field.value} value={field.value}
onValueChange={ onValueChange={field.onChange}
field.onChange
}
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="ACCEPT"> <SelectItem value="ACCEPT">{RuleAction.ACCEPT}</SelectItem>
{RuleAction.ACCEPT} <SelectItem value="DROP">{RuleAction.DROP}</SelectItem>
</SelectItem>
<SelectItem value="DROP">
{RuleAction.DROP}
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</FormControl> </FormControl>
@@ -682,25 +726,17 @@ export default function ResourceRules(props: {
<FormControl> <FormControl>
<Select <Select
value={field.value} value={field.value}
onValueChange={ onValueChange={field.onChange}
field.onChange
}
> >
<SelectTrigger className="w-full"> <SelectTrigger className="w-full">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{resource.http && ( {resource.http && (
<SelectItem value="PATH"> <SelectItem value="PATH">{RuleMatch.PATH}</SelectItem>
{RuleMatch.PATH}
</SelectItem>
)} )}
<SelectItem value="IP"> <SelectItem value="IP">{RuleMatch.IP}</SelectItem>
{RuleMatch.IP} <SelectItem value="CIDR">{RuleMatch.CIDR}</SelectItem>
</SelectItem>
<SelectItem value="CIDR">
{RuleMatch.CIDR}
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</FormControl> </FormControl>
@@ -712,16 +748,10 @@ export default function ResourceRules(props: {
control={addRuleForm.control} control={addRuleForm.control}
name="value" name="value"
render={({ field }) => ( render={({ field }) => (
<FormItem className="gap-1"> <FormItem className="gap-1 md:col-span-2">
<InfoPopup <InfoPopup
text={t('value')} text={t('value')}
info={ info={getValueHelpText(addRuleForm.watch('match')) || ''}
getValueHelpText(
addRuleForm.watch(
"match"
)
) || ""
}
/> />
<FormControl> <FormControl>
<Input {...field} /> <Input {...field} />
@@ -730,16 +760,16 @@ export default function ResourceRules(props: {
</FormItem> </FormItem>
)} )}
/> />
<Button
type="submit"
variant="secondary"
disabled={!rulesEnabled}
>
{t('ruleSubmit')}
</Button>
</div> </div>
<DialogFooter>
<Button type="submit">{t('ruleSubmit')}</Button>
</DialogFooter>
</form> </form>
</Form> </Form>
</DialogContent>
</Dialog>
</div>
<Table> <Table>
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
@@ -749,8 +779,7 @@ export default function ResourceRules(props: {
{header.isPlaceholder {header.isPlaceholder
? null ? null
: flexRender( : flexRender(
header.column.columnDef header.column.columnDef.header,
.header,
header.getContext() header.getContext()
)} )}
</TableHead> </TableHead>
@@ -774,21 +803,14 @@ export default function ResourceRules(props: {
)) ))
) : ( ) : (
<TableRow> <TableRow>
<TableCell <TableCell colSpan={columns.length} className="h-24 text-center">
colSpan={columns.length}
className="h-24 text-center"
>
{t('rulesNoOne')} {t('rulesNoOne')}
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
</TableBody> </TableBody>
{/* <TableCaption> */}
{/* {t('rulesOrder')} */}
{/* </TableCaption> */}
</Table> </Table>
{/* Pagination Controls */}
{rules.length > 0 && ( {rules.length > 0 && (
<div className="flex items-center justify-between space-x-2 py-4"> <div className="flex items-center justify-between space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground"> <div className="flex-1 text-sm text-muted-foreground">
@@ -821,8 +843,7 @@ export default function ResourceRules(props: {
</Select> </Select>
</div> </div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium"> <div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "} Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
{table.getPageCount()}
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Button <Button
@@ -865,37 +886,12 @@ export default function ResourceRules(props: {
</div> </div>
</div> </div>
)} )}
</div>
</SettingsSectionBody>
</SettingsSection>
{/* Template Assignment Section */}
{rulesEnabled && (
<SettingsSection>
<SettingsSectionHeader>
<SettingsSectionTitle>
{t('ruleTemplates')}
</SettingsSectionTitle>
<SettingsSectionDescription>
{t('ruleTemplatesDescription')}
</SettingsSectionDescription>
</SettingsSectionHeader>
<SettingsSectionBody>
<ResourceRulesManager
resourceId={params.resourceId.toString()}
orgId={resource.orgId}
onUpdate={fetchRules}
/>
</SettingsSectionBody> </SettingsSectionBody>
</SettingsSection> </SettingsSection>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button <Button onClick={saveAllSettings} loading={loading} disabled={loading}>
onClick={saveAllSettings}
loading={loading}
disabled={loading}
>
{t('saveAllSettings')} {t('saveAllSettings')}
</Button> </Button>
</div> </div>

View File

@@ -12,9 +12,13 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
import { import {
SettingsContainer, SettingsContainer,
SettingsSection, SettingsSection,
SettingsSectionHeader SettingsSectionHeader,
SettingsSectionTitle,
SettingsSectionDescription,
SettingsSectionBody,
SettingsSectionFooter,
SettingsSectionForm
} from "@app/components/Settings"; } from "@app/components/Settings";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { Button } from "@app/components/ui/button"; import { Button } from "@app/components/ui/button";
import { Input } from "@app/components/ui/input"; import { Input } from "@app/components/ui/input";
import { Textarea } from "@app/components/ui/textarea"; import { Textarea } from "@app/components/ui/textarea";
@@ -118,9 +122,16 @@ export default function GeneralPage() {
<SettingsContainer> <SettingsContainer>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle title={t("templateDetails")} /> <SettingsSectionTitle>
{t("templateDetails")}
</SettingsSectionTitle>
<SettingsSectionDescription>
Update the name and description for this rule template.
</SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> <SettingsSectionBody>
<SettingsSectionForm>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4" id="template-general-form">
<div> <div>
<label htmlFor="name" className="block text-sm font-medium mb-2"> <label htmlFor="name" className="block text-sm font-medium mb-2">
{t("name")} {t("name")}
@@ -138,17 +149,17 @@ export default function GeneralPage() {
<label htmlFor="description" className="block text-sm font-medium mb-2"> <label htmlFor="description" className="block text-sm font-medium mb-2">
{t("description")} {t("description")}
</label> </label>
<Textarea <Textarea id="description" {...register("description")} rows={3} />
id="description"
{...register("description")}
rows={3}
/>
</div> </div>
<Button type="submit" disabled={saving}> </form>
</SettingsSectionForm>
</SettingsSectionBody>
<SettingsSectionFooter>
<Button type="submit" form="template-general-form" disabled={saving}>
<Save className="w-4 h-4 mr-2" /> <Save className="w-4 h-4 mr-2" />
{saving ? t("saving") : t("save")} {saving ? t("saving") : t("save")}
</Button> </Button>
</form> </SettingsSectionFooter>
</SettingsSection> </SettingsSection>
</SettingsContainer> </SettingsContainer>
); );

View File

@@ -4,24 +4,35 @@ import { useParams } from "next/navigation";
import { import {
SettingsContainer, SettingsContainer,
SettingsSection, SettingsSection,
SettingsSectionHeader SettingsSectionHeader,
SettingsSectionTitle,
SettingsSectionDescription,
SettingsSectionBody
} from "@app/components/Settings"; } from "@app/components/Settings";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { TemplateRulesManager } from "@app/components/ruleTemplate/TemplateRulesManager"; import { TemplateRulesManager } from "@app/components/ruleTemplate/TemplateRulesManager";
import { useTranslations } from "next-intl";
export default function RulesPage() { export default function RulesPage() {
const params = useParams(); const params = useParams();
const t = useTranslations();
return ( return (
<SettingsContainer> <SettingsContainer>
<SettingsSection> <SettingsSection>
<SettingsSectionHeader> <SettingsSectionHeader>
<SettingsSectionTitle title="Template Rules" /> <SettingsSectionTitle>
{t('ruleTemplates')}
</SettingsSectionTitle>
<SettingsSectionDescription>
Manage the rules for this template. Changes propagate to all assigned resources.
</SettingsSectionDescription>
</SettingsSectionHeader> </SettingsSectionHeader>
<SettingsSectionBody>
<TemplateRulesManager <TemplateRulesManager
orgId={params.orgId as string} orgId={params.orgId as string}
templateId={params.templateId as string} templateId={params.templateId as string}
/> />
</SettingsSectionBody>
</SettingsSection> </SettingsSection>
</SettingsContainer> </SettingsContainer>
); );

View File

@@ -2,7 +2,6 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useToast } from "@app/hooks/useToast"; import { useToast } from "@app/hooks/useToast";
import { Trash2 } from "lucide-react"; import { Trash2 } from "lucide-react";
@@ -156,17 +155,9 @@ export function ResourceRulesManager({
} }
return ( return (
<div className="space-y-6"> <div className="space-y-4">
{/* Template Assignment */} <div className="space-y-2">
<Card> <div className="flex items-center gap-2">
<CardHeader>
<CardTitle>Template Assignment</CardTitle>
<CardDescription>
Assign rule templates to this resource for consistent access control
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center space-x-2">
<Select <Select
value={selectedTemplate} value={selectedTemplate}
onValueChange={(value) => { onValueChange={(value) => {
@@ -190,13 +181,17 @@ export function ResourceRulesManager({
{resourceTemplates.length > 0 && ( {resourceTemplates.length > 0 && (
<div className="space-y-2"> <div className="space-y-2">
<h4 className="font-medium">Assigned Templates</h4> <h4 className="font-medium">Assigned Templates</h4>
<div className="space-y-2">
{resourceTemplates.map((template) => ( {resourceTemplates.map((template) => (
<div key={template.templateId} className="flex items-center justify-between p-3 border rounded-lg"> <div
<div className="flex items-center space-x-2"> key={template.templateId}
className="flex items-center justify-between p-3 border rounded-md bg-muted/30"
>
<div className="flex items-center gap-2">
<span className="font-medium">{template.name}</span> <span className="font-medium">{template.name}</span>
<span className="text-sm text-muted-foreground"> {template.description && (
{template.description} <span className="text-sm text-muted-foreground">{template.description}</span>
</span> )}
</div> </div>
<Button <Button
variant="outline" variant="outline"
@@ -209,9 +204,9 @@ export function ResourceRulesManager({
</div> </div>
))} ))}
</div> </div>
</div>
)} )}
</CardContent> </div>
</Card>
<ConfirmationDialog <ConfirmationDialog
open={unassignDialogOpen} open={unassignDialogOpen}

View File

@@ -47,6 +47,15 @@ import {
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "@server/lib/validators"; import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "@server/lib/validators";
import { ArrowUpDown, Trash2, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react"; import { ArrowUpDown, Trash2, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
import { ConfirmationDialog } from "@app/components/ConfirmationDialog"; import { ConfirmationDialog } from "@app/components/ConfirmationDialog";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from "@app/components/ui/dialog";
const addRuleSchema = z.object({ const addRuleSchema = z.object({
action: z.enum(["ACCEPT", "DROP"]), action: z.enum(["ACCEPT", "DROP"]),
@@ -76,6 +85,7 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
const [rules, setRules] = useState<TemplateRule[]>([]); const [rules, setRules] = useState<TemplateRule[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [addingRule, setAddingRule] = useState(false); const [addingRule, setAddingRule] = useState(false);
const [createDialogOpen, setCreateDialogOpen] = useState(false);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
pageIndex: 0, pageIndex: 0,
pageSize: 25 pageSize: 25
@@ -366,14 +376,34 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
}); });
if (loading) { if (loading) {
return <div>Loading rules...</div>; return <div className="text-muted-foreground">Loading...</div>;
} }
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="flex justify-end">
<Dialog open={createDialogOpen} onOpenChange={setCreateDialogOpen}>
<DialogTrigger asChild>
<Button variant="secondary" disabled={addingRule}>
{addingRule ? "Adding Rule..." : t('ruleSubmit')}
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{t('ruleSubmit')}</DialogTitle>
<DialogDescription>
{t('rulesResourceDescription')}
</DialogDescription>
</DialogHeader>
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(addRule)} className="space-y-4"> <form
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 items-end"> onSubmit={form.handleSubmit(async (data) => {
await addRule(data);
setCreateDialogOpen(false);
})}
className="space-y-4"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField <FormField
control={form.control} control={form.control}
name="action" name="action"
@@ -381,17 +411,12 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
<FormItem> <FormItem>
<FormLabel>{t('rulesAction')}</FormLabel> <FormLabel>{t('rulesAction')}</FormLabel>
<FormControl> <FormControl>
<Select <Select value={field.value} onValueChange={field.onChange}>
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="ACCEPT"> <SelectItem value="ACCEPT">{RuleAction.ACCEPT}</SelectItem>
{RuleAction.ACCEPT}
</SelectItem>
<SelectItem value="DROP">{RuleAction.DROP}</SelectItem> <SelectItem value="DROP">{RuleAction.DROP}</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
@@ -400,7 +425,6 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="match" name="match"
@@ -408,10 +432,7 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
<FormItem> <FormItem>
<FormLabel>{t('rulesMatchType')}</FormLabel> <FormLabel>{t('rulesMatchType')}</FormLabel>
<FormControl> <FormControl>
<Select <Select value={field.value} onValueChange={field.onChange}>
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
@@ -426,12 +447,11 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="value" name="value"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem className="md:col-span-2">
<FormLabel>{t('value')}</FormLabel> <FormLabel>{t('value')}</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Enter value" {...field} /> <Input placeholder="Enter value" {...field} />
@@ -440,7 +460,6 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="priority" name="priority"
@@ -448,25 +467,25 @@ export function TemplateRulesManager({ templateId, orgId }: TemplateRulesManager
<FormItem> <FormItem>
<FormLabel>{t('rulesPriority')} (optional)</FormLabel> <FormLabel>{t('rulesPriority')} (optional)</FormLabel>
<FormControl> <FormControl>
<Input <Input type="number" placeholder="Auto" {...field} />
type="number"
placeholder="Auto"
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
</div> </div>
<DialogFooter>
<Button type="submit" variant="secondary" disabled={addingRule}> <Button type="submit" variant="secondary" disabled={addingRule}>
{addingRule ? "Adding Rule..." : "Add Rule"} {addingRule ? "Adding Rule..." : t('ruleSubmit')}
</Button> </Button>
</DialogFooter>
</form> </form>
</Form> </Form>
</DialogContent>
</Dialog>
</div>
<div className="rounded-md border"> <div>
<Table> <Table>
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (