emloyee and user fixings
This commit is contained in:
@@ -5,8 +5,11 @@
|
||||
// "select all children" convenience toggle — the parent's own visibility is
|
||||
// derived from its children on the frontend (see AppSidebar.tsx's filter), so
|
||||
// only the children need to carry the actual grant for those groups.
|
||||
import { Check } from "lucide-react"
|
||||
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { NavItem } from "@/types/rbac"
|
||||
|
||||
interface Props {
|
||||
@@ -41,42 +44,60 @@ export function RolePermissionTree({ tree, selectedNavItemIds, selectedSubNavIte
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 rounded-lg border p-4">
|
||||
{tree.map((item) => {
|
||||
if (item.children.length === 0) {
|
||||
return (
|
||||
<label key={item.navItemId} className="flex items-center gap-3 rounded-md px-2 py-2 hover:bg-muted/50">
|
||||
<Checkbox
|
||||
checked={selectedNavItemIds.has(item.navItemId)}
|
||||
onCheckedChange={() => toggleLeaf(item.navItemId)}
|
||||
/>
|
||||
<Label className="cursor-pointer text-base font-medium">{item.label}</Label>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
<div className="overflow-hidden rounded-lg border">
|
||||
{tree.map((item, i) => {
|
||||
const hasChildren = item.children.length > 0
|
||||
const checkedCount = item.children.filter((c) => selectedSubNavItemIds.has(c.subNavItemId)).length
|
||||
const allChecked = checkedCount === item.children.length
|
||||
const allChecked = hasChildren && checkedCount === item.children.length
|
||||
const someChecked = hasChildren && checkedCount > 0 && !allChecked
|
||||
|
||||
return (
|
||||
<div key={item.navItemId} className="flex flex-col gap-1 border-t pt-2 first:border-t-0 first:pt-0">
|
||||
<label className="flex items-center gap-3 rounded-md px-2 py-2 hover:bg-muted/50">
|
||||
<Checkbox checked={allChecked} onCheckedChange={(checked) => toggleAllChildren(item, !!checked)} />
|
||||
<Label className="cursor-pointer text-base font-semibold">
|
||||
{item.label} {checkedCount > 0 && !allChecked && <span className="text-sm font-normal text-muted-foreground">({checkedCount} of {item.children.length})</span>}
|
||||
<div
|
||||
key={item.navItemId}
|
||||
className="grid grid-cols-[minmax(11rem,14rem)_1fr] items-start gap-x-4 gap-y-2 border-b bg-white px-4 py-3 last:border-b-0"
|
||||
>
|
||||
<label className="flex items-center gap-2.5 pt-0.5">
|
||||
<Checkbox
|
||||
checked={hasChildren ? allChecked : selectedNavItemIds.has(item.navItemId)}
|
||||
indeterminate={someChecked}
|
||||
onCheckedChange={
|
||||
hasChildren
|
||||
? (checked) => toggleAllChildren(item, !!checked)
|
||||
: () => toggleLeaf(item.navItemId)
|
||||
}
|
||||
className="size-5 border-2 border-muted-foreground/50 data-checked:border-blue-600! data-checked:bg-blue-600! data-checked:text-white! [&_svg]:size-4"
|
||||
/>
|
||||
<Label className={cn("cursor-pointer text-base", hasChildren ? "font-semibold" : "font-medium")}>
|
||||
{item.label}
|
||||
</Label>
|
||||
</label>
|
||||
<div className="flex flex-col gap-1 pl-9">
|
||||
{item.children.map((child) => (
|
||||
<label key={child.subNavItemId} className="flex items-center gap-3 rounded-md px-2 py-1.5 hover:bg-muted/50">
|
||||
<Checkbox
|
||||
checked={selectedSubNavItemIds.has(child.subNavItemId)}
|
||||
onCheckedChange={() => toggleChild(child.subNavItemId)}
|
||||
/>
|
||||
<Label className="cursor-pointer text-sm">{child.label}</Label>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasChildren ? (
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{item.children.map((child) => {
|
||||
const checked = selectedSubNavItemIds.has(child.subNavItemId)
|
||||
return (
|
||||
<button
|
||||
key={child.subNavItemId}
|
||||
type="button"
|
||||
aria-pressed={checked}
|
||||
onClick={() => toggleChild(child.subNavItemId)}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full border px-3 py-1 text-sm font-medium transition-colors",
|
||||
checked
|
||||
? "border-transparent bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-500/20 dark:text-blue-300 dark:hover:bg-blue-500/30"
|
||||
: "border-input bg-white text-muted-foreground hover:border-muted-foreground/40 hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
{checked && <Check className="size-3.5" />}
|
||||
{child.label}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<span className="pt-1 text-sm text-muted-foreground">No sub-items — grants the whole section.</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user