mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-02 08:46:38 +00:00
add approve and deny actions to devices table
This commit is contained in:
@@ -47,14 +47,20 @@ const querySchema = z.strictObject({
|
|||||||
.enum(["pending", "approved", "denied", "all"])
|
.enum(["pending", "approved", "denied", "all"])
|
||||||
.optional()
|
.optional()
|
||||||
.default("all")
|
.default("all")
|
||||||
.catch("all")
|
.catch("all"),
|
||||||
|
clientId: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => (val ? Number(val) : undefined))
|
||||||
|
.pipe(z.number().int().positive().optional())
|
||||||
});
|
});
|
||||||
|
|
||||||
async function queryApprovals(
|
async function queryApprovals(
|
||||||
orgId: string,
|
orgId: string,
|
||||||
limit: number,
|
limit: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
approvalState: z.infer<typeof querySchema>["approvalState"]
|
approvalState: z.infer<typeof querySchema>["approvalState"],
|
||||||
|
clientId?: number
|
||||||
) {
|
) {
|
||||||
let state: Array<Approval["decision"]> = [];
|
let state: Array<Approval["decision"]> = [];
|
||||||
switch (approvalState) {
|
switch (approvalState) {
|
||||||
@@ -109,7 +115,8 @@ async function queryApprovals(
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(approvals.orgId, orgId),
|
eq(approvals.orgId, orgId),
|
||||||
sql`${approvals.decision} in ${state}`
|
sql`${approvals.decision} in ${state}`,
|
||||||
|
...(clientId ? [eq(approvals.clientId, clientId)] : [])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.orderBy(
|
.orderBy(
|
||||||
@@ -202,7 +209,7 @@ export async function listApprovals(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { limit, offset, approvalState } = parsedQuery.data;
|
const { limit, offset, approvalState, clientId } = parsedQuery.data;
|
||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
@@ -223,7 +230,8 @@ export async function listApprovals(
|
|||||||
orgId.toString(),
|
orgId.toString(),
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
approvalState
|
approvalState,
|
||||||
|
clientId
|
||||||
);
|
);
|
||||||
|
|
||||||
const [{ count }] = await db
|
const [{ count }] = await db
|
||||||
|
|||||||
@@ -184,6 +184,90 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const approveDevice = async (clientRow: ClientRow) => {
|
||||||
|
try {
|
||||||
|
// Fetch approvalId for this client using clientId query parameter
|
||||||
|
const approvalsRes = await api.get<{
|
||||||
|
data: { approvals: Array<{ approvalId: number; clientId: number }> };
|
||||||
|
}>(`/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}`);
|
||||||
|
|
||||||
|
const approval = approvalsRes.data.data.approvals[0];
|
||||||
|
|
||||||
|
if (!approval) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("error"),
|
||||||
|
description: t("accessApprovalErrorUpdateDescription")
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.put(`/org/${clientRow.orgId}/approvals/${approval.approvalId}`, {
|
||||||
|
decision: "approved"
|
||||||
|
});
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: t("accessApprovalUpdated"),
|
||||||
|
description: t("accessApprovalApprovedDescription")
|
||||||
|
});
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
router.refresh();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("accessApprovalErrorUpdate"),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t("accessApprovalErrorUpdateDescription")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const denyDevice = async (clientRow: ClientRow) => {
|
||||||
|
try {
|
||||||
|
// Fetch approvalId for this client using clientId query parameter
|
||||||
|
const approvalsRes = await api.get<{
|
||||||
|
data: { approvals: Array<{ approvalId: number; clientId: number }> };
|
||||||
|
}>(`/org/${clientRow.orgId}/approvals?approvalState=pending&clientId=${clientRow.id}`);
|
||||||
|
|
||||||
|
const approval = approvalsRes.data.data.approvals[0];
|
||||||
|
|
||||||
|
if (!approval) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("error"),
|
||||||
|
description: t("accessApprovalErrorUpdateDescription")
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.put(`/org/${clientRow.orgId}/approvals/${approval.approvalId}`, {
|
||||||
|
decision: "denied"
|
||||||
|
});
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: t("accessApprovalUpdated"),
|
||||||
|
description: t("accessApprovalDeniedDescription")
|
||||||
|
});
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
router.refresh();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t("accessApprovalErrorUpdate"),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t("accessApprovalErrorUpdateDescription")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check if there are any rows without userIds in the current view's data
|
// Check if there are any rows without userIds in the current view's data
|
||||||
const hasRowsWithoutUserId = useMemo(() => {
|
const hasRowsWithoutUserId = useMemo(() => {
|
||||||
return userClients.some((client) => !client.userId);
|
return userClients.some((client) => !client.userId);
|
||||||
@@ -464,6 +548,20 @@ export default function UserDevicesTable({ userClients }: ClientTableProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
|
{clientRow.approvalState === "pending" && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => approveDevice(clientRow)}
|
||||||
|
>
|
||||||
|
<span>{t("approve")}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => denyDevice(clientRow)}
|
||||||
|
>
|
||||||
|
<span>{t("deny")}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (clientRow.archived) {
|
if (clientRow.archived) {
|
||||||
|
|||||||
Reference in New Issue
Block a user