create & update role with device approval

This commit is contained in:
Fred KISSIE
2026-01-08 03:33:03 +01:00
parent abfe476cb9
commit 39bebea5f7
12 changed files with 449 additions and 128 deletions

View File

@@ -10,12 +10,18 @@ import { useOrgContext } from "@app/hooks/useOrgContext";
import { toast } from "@app/hooks/useToast";
import { createApiClient } from "@app/lib/api";
import { Role } from "@server/db";
import { ArrowUpDown } from "lucide-react";
import { ArrowRight, ArrowUpDown, Link, MoreHorizontal } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Switch } from "./ui/switch";
import { useState, useTransition } from "react";
import { usePaidStatus } from "@app/hooks/usePaidStatus";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem
} from "./ui/dropdown-menu";
import EditRoleForm from "./EditRoleForm";
export type RoleRow = Role;
@@ -23,13 +29,13 @@ type RolesTableProps = {
roles: RoleRow[];
};
export default function UsersTable({ roles: r }: RolesTableProps) {
export default function UsersTable({ roles }: RolesTableProps) {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [editingRole, setEditingRole] = useState<RoleRow | null>(null);
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
const router = useRouter();
const [roles, setRoles] = useState<RoleRow[]>(r);
const [roleToRemove, setUserToRemove] = useState<RoleRow | null>(null);
const api = createApiClient(useEnvContext());
@@ -38,13 +44,11 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
const { isPaidUser } = usePaidStatus();
const t = useTranslations();
const [isRefreshing, setIsRefreshing] = useState(false);
const [isRefreshing, startTransition] = useTransition();
const refreshData = async () => {
console.log("Data refreshed");
setIsRefreshing(true);
try {
await new Promise((resolve) => setTimeout(resolve, 200));
router.refresh();
} catch (error) {
toast({
@@ -52,8 +56,6 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
description: t("refreshError"),
variant: "destructive"
});
} finally {
setIsRefreshing(false);
}
};
@@ -81,52 +83,76 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
friendlyName: t("description"),
header: () => <span className="p-3">{t("description")}</span>
},
// {
// id: "actions",
// enableHiding: false,
// header: () => <span className="p-3"></span>,
// cell: ({ row }) => {
// const roleRow = row.original;
...(isPaidUser
? ([
{
accessorKey: "requireDeviceApproval",
friendlyName: t("requireDeviceApproval"),
header: () => (
<span className="p-3">
{t("requireDeviceApproval")}
</span>
),
cell: ({ row }) => (
<Switch
defaultChecked={
!!row.original.requireDeviceApproval
}
disabled={!!row.original.isAdmin}
onCheckedChange={(val) => {
// ...
}}
/>
)
}
] as ExtendedColumnDef<RoleRow>[])
: []),
// return (
// <div className="flex items-center gap-2 justify-end">
// <Button
// variant={"outline"}
// disabled={roleRow.isAdmin || false}
// onClick={() => {
// setIsDeleteModalOpen(true);
// setUserToRemove(roleRow);
// }}
// >
// {t("accessRoleDelete")}
// </Button>
// </div>
// );
// }
// },
{
id: "actions",
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const roleRow = row.original;
return (
<div className="flex items-center gap-2 justify-end">
<Button
variant={"outline"}
disabled={roleRow.isAdmin || false}
onClick={() => {
setIsDeleteModalOpen(true);
setUserToRemove(roleRow);
}}
>
{t("accessRoleDelete")}
</Button>
</div>
!roleRow.isAdmin && (
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
{t("openMenu")}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
// setSelectedInternalResource(
// resourceRow
// );
setIsDeleteModalOpen(true);
}}
>
<span className="text-red-500">
{t("delete")}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant={"outline"}
onClick={() => {
setEditingRole(roleRow);
setIsEditDialogOpen(true);
}}
>
{t("edit")}
</Button>
</div>
)
);
}
}
@@ -134,11 +160,26 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
return (
<>
{editingRole && (
<EditRoleForm
role={editingRole}
open={isEditDialogOpen}
key={editingRole.roleId}
setOpen={setIsEditDialogOpen}
onSuccess={() => {
// Delay refresh to allow modal to close smoothly
setTimeout(() => {
router.refresh();
setEditingRole(null);
}, 150);
}}
/>
)}
<CreateRoleForm
open={isCreateModalOpen}
setOpen={setIsCreateModalOpen}
afterCreate={async (role) => {
setRoles((prev) => [...prev, role]);
afterCreate={() => {
startTransition(refreshData);
}}
/>
@@ -148,9 +189,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
setOpen={setIsDeleteModalOpen}
roleToDelete={roleToRemove}
afterDelete={() => {
setRoles((prev) =>
prev.filter((r) => r.roleId !== roleToRemove.roleId)
);
startTransition(refreshData);
setUserToRemove(null);
}}
/>
@@ -162,7 +201,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
createRole={() => {
setIsCreateModalOpen(true);
}}
onRefresh={refreshData}
onRefresh={() => startTransition(refreshData)}
isRefreshing={isRefreshing}
/>
</>