260321:1700 Correct Coresspondence / Doing RFA
This commit is contained in:
@@ -34,16 +34,32 @@ interface RbacMatrixProps {
|
||||
rolePermissions: Record<number, number[]>; // roleId -> permissionIds[]
|
||||
}
|
||||
|
||||
const extractArrayData = <T,>(value: unknown): T[] => {
|
||||
let current: unknown = value;
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
if (Array.isArray(current)) {
|
||||
return current as T[];
|
||||
}
|
||||
|
||||
if (!current || typeof current !== "object" || !("data" in current)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
current = (current as { data?: unknown }).data;
|
||||
}
|
||||
|
||||
return Array.isArray(current) ? (current as T[]) : [];
|
||||
};
|
||||
|
||||
const securityService = {
|
||||
getRoles: async (): Promise<Role[]> => {
|
||||
const response = await apiClient.get("/users/roles");
|
||||
const data = response.data?.data || response.data;
|
||||
return Array.isArray(data) ? data : [];
|
||||
return extractArrayData<Role>(response.data);
|
||||
},
|
||||
getPermissions: async (): Promise<Permission[]> => {
|
||||
const response = await apiClient.get("/users/permissions");
|
||||
const data = response.data?.data || response.data;
|
||||
return Array.isArray(data) ? data : [];
|
||||
return extractArrayData<Permission>(response.data);
|
||||
},
|
||||
updateRolePermissions: async (roleId: number, permissionIds: number[]) => {
|
||||
// This endpoint might not exist as a bulk update, usually it's per role
|
||||
@@ -98,6 +114,8 @@ export function RbacMatrix() {
|
||||
};
|
||||
|
||||
const hasChanges = Object.keys(pendingChanges).length > 0;
|
||||
const roleList = Array.isArray(roles) ? roles : [];
|
||||
const permissionList = Array.isArray(permissions) ? permissions : [];
|
||||
|
||||
if (rolesLoading || permsLoading) {
|
||||
return (
|
||||
@@ -125,7 +143,7 @@ export function RbacMatrix() {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[300px]">Permission</TableHead>
|
||||
{roles.map((role) => (
|
||||
{roleList.map((role) => (
|
||||
<TableHead key={role.roleId} className="text-center min-w-[100px]">
|
||||
{role.roleName}
|
||||
</TableHead>
|
||||
@@ -133,13 +151,13 @@ export function RbacMatrix() {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{permissions.map((perm) => (
|
||||
{permissionList.map((perm) => (
|
||||
<TableRow key={perm.permissionId}>
|
||||
<TableCell className="font-medium">
|
||||
<div>{perm.permissionName}</div>
|
||||
<div className="text-xs text-muted-foreground">{perm.description}</div>
|
||||
</TableCell>
|
||||
{roles.map((role) => {
|
||||
{roleList.map((role) => {
|
||||
// Assume role.permissions is populated
|
||||
const currentRolePerms = role.permissions?.map((p) => p.permissionId) || [];
|
||||
const activePerms = pendingChanges[role.roleId] || currentRolePerms;
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
const ALL_ORGANIZATIONS_VALUE = "all";
|
||||
|
||||
// Update schema to include confirmPassword
|
||||
const userSchema = z.object({
|
||||
username: z.string().min(3, "Username must be at least 3 characters"),
|
||||
@@ -92,7 +94,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
isActive: true,
|
||||
roleIds: [],
|
||||
lineId: "",
|
||||
primaryOrganizationId: undefined,
|
||||
primaryOrganizationId: ALL_ORGANIZATIONS_VALUE,
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
},
|
||||
@@ -107,7 +109,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
lastName: user.lastName,
|
||||
isActive: user.isActive,
|
||||
lineId: user.lineId || "",
|
||||
primaryOrganizationId: user.primaryOrganizationId?.toString(),
|
||||
primaryOrganizationId: user.primaryOrganizationId?.toString() || ALL_ORGANIZATIONS_VALUE,
|
||||
roleIds: user.roles?.map((r: { roleId: number }) => r.roleId) || [],
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
@@ -120,7 +122,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
lastName: "",
|
||||
isActive: true,
|
||||
lineId: "",
|
||||
primaryOrganizationId: undefined,
|
||||
primaryOrganizationId: ALL_ORGANIZATIONS_VALUE,
|
||||
roleIds: [],
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
@@ -148,6 +150,9 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
const payload = { ...data };
|
||||
delete payload.confirmPassword; // Don't send to API
|
||||
if (!payload.password) delete payload.password; // Don't send empty password on edit
|
||||
if (payload.primaryOrganizationId === ALL_ORGANIZATIONS_VALUE) {
|
||||
delete payload.primaryOrganizationId;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
updateUser.mutate(
|
||||
@@ -231,7 +236,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
<div>
|
||||
<Label>Primary Organization</Label>
|
||||
<Select
|
||||
value={watch("primaryOrganizationId") ?? undefined}
|
||||
value={watch("primaryOrganizationId") || ALL_ORGANIZATIONS_VALUE}
|
||||
onValueChange={(val) =>
|
||||
setValue("primaryOrganizationId", val)
|
||||
}
|
||||
@@ -240,7 +245,8 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
|
||||
<SelectValue placeholder="Select Organization" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{organizations?.map((org: { uuid: string; organizationCode: string; organizationName: string }) => (
|
||||
<SelectItem value={ALL_ORGANIZATIONS_VALUE}>All Organizations</SelectItem>
|
||||
{Array.isArray(organizations) && organizations.map((org: { uuid: string; organizationCode: string; organizationName: string }) => (
|
||||
<SelectItem
|
||||
key={org.uuid}
|
||||
value={org.uuid}
|
||||
|
||||
Reference in New Issue
Block a user