mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-10 14:38:22 +02:00
Add toggle to the ui for enabled
This commit is contained in:
@@ -16,12 +16,14 @@ import { Button } from "@app/components/ui/button";
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage
|
FormMessage
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import { Input } from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
|
import { SwitchInput } from "@app/components/SwitchInput";
|
||||||
import { createGeneralFormSchema } from "@app/lib/privateResourceForm";
|
import { createGeneralFormSchema } from "@app/lib/privateResourceForm";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
@@ -41,7 +43,8 @@ export default function PrivateResourceGeneralPage() {
|
|||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: siteResource.name,
|
name: siteResource.name,
|
||||||
niceId: siteResource.niceId
|
niceId: siteResource.niceId,
|
||||||
|
enabled: siteResource.enabled
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,7 +55,8 @@ export default function PrivateResourceGeneralPage() {
|
|||||||
const data = form.getValues();
|
const data = form.getValues();
|
||||||
await save({
|
await save({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
niceId: data.niceId
|
niceId: data.niceId,
|
||||||
|
enabled: data.enabled
|
||||||
});
|
});
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
@@ -76,6 +80,42 @@ export default function PrivateResourceGeneralPage() {
|
|||||||
id="private-resource-general-form"
|
id="private-resource-general-form"
|
||||||
>
|
>
|
||||||
<SettingsFormGrid>
|
<SettingsFormGrid>
|
||||||
|
<SettingsFormCell span="full">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="enabled"
|
||||||
|
render={() => (
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<SwitchInput
|
||||||
|
id="enable-resource"
|
||||||
|
defaultChecked={
|
||||||
|
siteResource.enabled
|
||||||
|
}
|
||||||
|
label={t(
|
||||||
|
"resourceEnable"
|
||||||
|
)}
|
||||||
|
onCheckedChange={(
|
||||||
|
val
|
||||||
|
) =>
|
||||||
|
form.setValue(
|
||||||
|
"enabled",
|
||||||
|
val
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
{t(
|
||||||
|
"disabledResourceDescription"
|
||||||
|
)}
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SettingsFormCell>
|
||||||
|
|
||||||
<SettingsFormCell span="half">
|
<SettingsFormCell span="half">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger
|
PopoverTrigger
|
||||||
} from "@app/components/ui/popover";
|
} from "@app/components/ui/popover";
|
||||||
|
import { Switch } from "@app/components/ui/switch";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { useNavigationContext } from "@app/hooks/useNavigationContext";
|
import { useNavigationContext } from "@app/hooks/useNavigationContext";
|
||||||
import { useOptimisticLabels } from "@app/hooks/useOptimisticLabels";
|
import { useOptimisticLabels } from "@app/hooks/useOptimisticLabels";
|
||||||
@@ -36,7 +37,9 @@ import { getPrivateResourceSettingsHref } from "@app/lib/launcherResourceAdminHr
|
|||||||
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
|
import { UpdateSiteResourceResponse } from "@server/routers/siteResource";
|
||||||
import type { PaginationState } from "@tanstack/react-table";
|
import type { PaginationState } from "@tanstack/react-table";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
import {
|
import {
|
||||||
ArrowDown01Icon,
|
ArrowDown01Icon,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
@@ -49,8 +52,17 @@ import {
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { startTransition, useMemo, useState, useTransition } from "react";
|
import {
|
||||||
|
startTransition,
|
||||||
|
useMemo,
|
||||||
|
useOptimistic,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
useTransition,
|
||||||
|
type ComponentRef
|
||||||
|
} from "react";
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
|
import z from "zod";
|
||||||
import { ColumnFilterButton } from "./ColumnFilterButton";
|
import { ColumnFilterButton } from "./ColumnFilterButton";
|
||||||
import { LabelColumnFilterButton } from "./LabelColumnFilterButton";
|
import { LabelColumnFilterButton } from "./LabelColumnFilterButton";
|
||||||
import { LabelsTableCell } from "./LabelsTableCell";
|
import { LabelsTableCell } from "./LabelsTableCell";
|
||||||
@@ -114,6 +126,11 @@ function isSafeUrlForLink(href: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const booleanSearchFilterSchema = z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.optional()
|
||||||
|
.catch(undefined);
|
||||||
|
|
||||||
type ClientResourcesTableProps = {
|
type ClientResourcesTableProps = {
|
||||||
internalResources: InternalResourceRow[];
|
internalResources: InternalResourceRow[];
|
||||||
orgId: string;
|
orgId: string;
|
||||||
@@ -174,6 +191,30 @@ export default function PrivateResourcesTable({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function toggleInternalResourceEnabled(
|
||||||
|
val: boolean,
|
||||||
|
resourceId: number
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await api.post<AxiosResponse<UpdateSiteResourceResponse>>(
|
||||||
|
`site-resource/${resourceId}`,
|
||||||
|
{
|
||||||
|
enabled: val
|
||||||
|
}
|
||||||
|
);
|
||||||
|
router.refresh();
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("resourcesErrorUpdate"),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t("resourcesErrorUpdateDescription")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteInternalResource = async (
|
const deleteInternalResource = async (
|
||||||
resourceId: number,
|
resourceId: number,
|
||||||
siteId: number
|
siteId: number
|
||||||
@@ -429,6 +470,36 @@ export default function PrivateResourcesTable({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "enabled",
|
||||||
|
friendlyName: t("enabled"),
|
||||||
|
header: () => (
|
||||||
|
<ColumnFilterButton
|
||||||
|
options={[
|
||||||
|
{ value: "true", label: t("enabled") },
|
||||||
|
{ value: "false", label: t("disabled") }
|
||||||
|
]}
|
||||||
|
selectedValue={booleanSearchFilterSchema.parse(
|
||||||
|
searchParams.get("enabled")
|
||||||
|
)}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
handleFilterChange("enabled", value)
|
||||||
|
}
|
||||||
|
searchPlaceholder={t("searchPlaceholder")}
|
||||||
|
emptyMessage={t("emptySearchOptions")}
|
||||||
|
label={t("enabled")}
|
||||||
|
className="p-3"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<InternalResourceEnabledForm
|
||||||
|
resource={row.original}
|
||||||
|
onToggleInternalResourceEnabled={
|
||||||
|
toggleInternalResourceEnabled
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "labels",
|
id: "labels",
|
||||||
accessorKey: "labels",
|
accessorKey: "labels",
|
||||||
@@ -643,3 +714,39 @@ function ClientResourceLabelCell({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InternalResourceEnabledFormProps = {
|
||||||
|
resource: InternalResourceRow;
|
||||||
|
onToggleInternalResourceEnabled: (
|
||||||
|
val: boolean,
|
||||||
|
resourceId: number
|
||||||
|
) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function InternalResourceEnabledForm({
|
||||||
|
resource,
|
||||||
|
onToggleInternalResourceEnabled
|
||||||
|
}: InternalResourceEnabledFormProps) {
|
||||||
|
const [optimisticEnabled, setOptimisticEnabled] = useOptimistic(
|
||||||
|
resource.enabled
|
||||||
|
);
|
||||||
|
|
||||||
|
const formRef = useRef<ComponentRef<"form">>(null);
|
||||||
|
|
||||||
|
async function submitAction(formData: FormData) {
|
||||||
|
const newEnabled = !(formData.get("enabled") === "on");
|
||||||
|
setOptimisticEnabled(newEnabled);
|
||||||
|
await onToggleInternalResourceEnabled(newEnabled, resource.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form action={submitAction} ref={formRef}>
|
||||||
|
<Switch
|
||||||
|
checked={optimisticEnabled}
|
||||||
|
disabled={optimisticEnabled !== resource.enabled}
|
||||||
|
name="enabled"
|
||||||
|
onCheckedChange={() => formRef.current?.requestSubmit()}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -342,7 +342,8 @@ export function createGeneralFormSchema(t: TranslateFn) {
|
|||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
.max(255)
|
.max(255)
|
||||||
.regex(/^[a-zA-Z0-9-]+$/)
|
.regex(/^[a-zA-Z0-9-]+$/),
|
||||||
|
enabled: z.boolean()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user