Create clients working again

This commit is contained in:
Owen 2025-04-01 10:13:20 -04:00
parent 875fa215c5
commit 96d6ad8142
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
6 changed files with 101 additions and 86 deletions

View file

@ -30,7 +30,7 @@ const createClientParamsSchema = z
const createClientSchema = z
.object({
name: z.string().min(1).max(255),
siteIds: z.array(z.string().transform(Number).pipe(z.number())),
siteIds: z.array(z.number().int().positive()),
olmId: z.string(),
secret: z.string(),
type: z.enum(["olm"])

View file

@ -102,8 +102,8 @@ authenticated.get(
);
authenticated.get(
"/pick-client-defaults",
verifySiteAccess,
"/org/:orgId/pick-client-defaults",
verifyOrgAccess,
verifyUserHasAction(ActionsEnum.createClient),
client.pickClientDefaults
);

View file

@ -29,8 +29,6 @@ import CreateClientFormModal from "./CreateClientsModal";
export type ClientRow = {
id: number;
siteId: string;
siteName: string;
name: string;
mbIn: string;
mbOut: string;
@ -128,33 +126,33 @@ export default function ClientsTable({ clients, orgId }: ClientTableProps) {
);
}
},
{
accessorKey: "siteName",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
Site
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const r = row.original;
return (
<Link href={`/${r.orgId}/settings/sites/${r.siteId}`}>
<Button variant="outline">
{r.siteName}
<ArrowUpRight className="ml-2 h-4 w-4" />
</Button>
</Link>
);
}
},
// {
// accessorKey: "siteName",
// header: ({ column }) => {
// return (
// <Button
// variant="ghost"
// onClick={() =>
// column.toggleSorting(column.getIsSorted() === "asc")
// }
// >
// Site
// <ArrowUpDown className="ml-2 h-4 w-4" />
// </Button>
// );
// },
// cell: ({ row }) => {
// const r = row.original;
// return (
// <Link href={`/${r.orgId}/settings/sites/${r.siteId}`}>
// <Button variant="outline">
// {r.siteName}
// <ArrowUpRight className="ml-2 h-4 w-4" />
// </Button>
// </Link>
// );
// }
// },
{
accessorKey: "online",
header: ({ column }) => {

View file

@ -88,11 +88,12 @@ export default function CreateClientForm({
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
const [isLoading, setIsLoading] = useState(false);
const [isChecked, setIsChecked] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [clientDefaults, setClientDefaults] =
useState<PickClientDefaultsResponse | null>(null);
const [olmCommand, setOlmCommand] = useState<string | null>(null);
const [selectedSites, setSelectedSites] = useState<Array<{id: number, name: string}>>([]);
const [selectedSites, setSelectedSites] = useState<
Array<{ id: number; name: string }>
>([]);
const handleCheckboxChange = (checked: boolean) => {
setIsChecked(checked);
@ -108,7 +109,10 @@ export default function CreateClientForm({
useEffect(() => {
// Update form value when selectedSites changes
form.setValue('siteIds', selectedSites.map(site => site.id));
form.setValue(
"siteIds",
selectedSites.map((site) => site.id)
);
}, [selectedSites, form]);
useEffect(() => {
@ -132,13 +136,8 @@ export default function CreateClientForm({
setSites(sites);
};
fetchSites();
}, [open]);
useEffect(() => {
if (selectedSites.length === 0) return;
api.get(`/pick-client-defaults`)
const fetchDefaults = async () => {
api.get(`/org/${orgId}/pick-client-defaults`)
.catch((e) => {
toast({
variant: "destructive",
@ -154,16 +153,22 @@ export default function CreateClientForm({
setOlmCommand(olmConfig);
}
});
}, [selectedSites]);
};
fetchSites();
fetchDefaults();
}, [open]);
const addSite = (siteId: number, siteName: string) => {
if (!selectedSites.some(site => site.id === siteId)) {
setSelectedSites([...selectedSites, { id: siteId, name: siteName }]);
if (!selectedSites.some((site) => site.id === siteId)) {
setSelectedSites([
...selectedSites,
{ id: siteId, name: siteName }
]);
}
};
const removeSite = (siteId: number) => {
setSelectedSites(selectedSites.filter(site => site.id !== siteId));
setSelectedSites(selectedSites.filter((site) => site.id !== siteId));
};
async function onSubmit(data: CreateClientFormValues) {
@ -190,10 +195,9 @@ export default function CreateClientForm({
} as CreateClientBody;
const res = await api
.put<AxiosResponse<CreateClientResponse>>(
`/org/${orgId}/client`,
payload
)
.put<
AxiosResponse<CreateClientResponse>
>(`/org/${orgId}/client`, payload)
.catch((e) => {
toast({
variant: "destructive",
@ -205,14 +209,8 @@ export default function CreateClientForm({
if (res && res.status === 201) {
const data = res.data.data;
// For now we'll just use the first site for display purposes
// The actual client will be associated with all selected sites
const firstSite = sites.find((site) => site.siteId === selectedSites[0]?.id);
onCreate?.({
name: data.name,
siteId: firstSite?.niceId || "",
siteName: firstSite?.name || "",
id: data.clientId,
mbIn: "0 MB",
mbOut: "0 MB",
@ -265,12 +263,13 @@ export default function CreateClientForm({
role="combobox"
className={cn(
"justify-between",
selectedSites.length === 0 &&
selectedSites.length ===
0 &&
"text-muted-foreground"
)}
>
{selectedSites.length > 0
? `${selectedSites.length} site${selectedSites.length !== 1 ? 's' : ''} selected`
? `${selectedSites.length} site${selectedSites.length !== 1 ? "s" : ""} selected`
: "Select sites"}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
@ -288,15 +287,26 @@ export default function CreateClientForm({
{sites.map((site) => (
<CommandItem
value={`${site.siteId}:${site.name}:${site.niceId}`}
key={site.siteId}
key={
site.siteId
}
onSelect={() => {
addSite(site.siteId, site.name);
addSite(
site.siteId,
site.name
);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
selectedSites.some(s => s.id === site.siteId)
selectedSites.some(
(
s
) =>
s.id ===
site.siteId
)
? "opacity-100"
: "opacity-0"
)}
@ -313,12 +323,17 @@ export default function CreateClientForm({
{selectedSites.length > 0 && (
<div className="flex flex-wrap gap-2 mt-2">
{selectedSites.map(site => (
<Badge key={site.id} variant="secondary">
{selectedSites.map((site) => (
<Badge
key={site.id}
variant="secondary"
>
{site.name}
<button
type="button"
onClick={() => removeSite(site.id)}
onClick={() =>
removeSite(site.id)
}
className="ml-1 rounded-full outline-none focus:ring-2 focus:ring-offset-2"
>
<X className="h-3 w-3" />
@ -329,7 +344,9 @@ export default function CreateClientForm({
)}
<FormDescription>
The client will have connectivity to the selected sites. The sites must be configured to accept client connections.
The client will have connectivity to the
selected sites. The sites must be configured
to accept client connections.
</FormDescription>
<FormMessage />
</FormItem>

View file

@ -60,7 +60,7 @@ export default function CreateClientFormModal({
<CredenzaFooter>
<Button
type="submit"
form="create-site-form"
form="create-client-form"
loading={loading}
disabled={loading || !isChecked}
onClick={() => {

View file

@ -3,7 +3,7 @@
import * as React from "react"
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
import { cn } from "@/lib/utils"
import { cn } from "@app/lib/cn"
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,