nxtgauge-admin-solid/src/routes/admin/department.tsx

522 lines
30 KiB
TypeScript
Raw Normal View History

import { For, Show, createMemo, createSignal, onMount } from 'solid-js';
import AdminShell from '~/components/AdminShell';
import { createModuleRecord, deleteModuleRecord, listModuleRecords, updateModuleRecord } from '~/lib/admin/client';
import type { CrudRecord } from '~/lib/admin/types';
type DepartmentRecord = CrudRecord & {
code?: string;
description?: string;
totalEmployees?: number;
createdDate?: string;
departmentHead?: string;
departmentEmail?: string;
transfersEnabled?: boolean;
};
const FALLBACK_DEPARTMENTS: DepartmentRecord[] = [
{ id: 'd1', name: 'Engineering', code: 'ENG-001', description: 'Software development and technical architecture', totalEmployees: 45, departmentHead: 'Arun Kumar', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-15' },
{ id: 'd2', name: 'Marketing', code: 'MKT-002', description: 'Brand management and digital marketing', totalEmployees: 23, departmentHead: 'Priya Sharma', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-15' },
{ id: 'd3', name: 'Human Resources', code: 'HR-003', description: 'Employee relations and talent acquisition', totalEmployees: 12, departmentHead: 'Rekha Nair', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-20' },
{ id: 'd4', name: 'Finance', code: 'FIN-004', description: 'Financial planning and accounting', totalEmployees: 18, departmentHead: 'Suresh Menon', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-20' },
{ id: 'd5', name: 'Operations', code: 'OPS-005', description: 'Business operations and process management', totalEmployees: 31, departmentHead: 'Deepak Verma', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-01' },
{ id: 'd6', name: 'Customer Success', code: 'CS-006', description: 'Client support and relationship management', totalEmployees: 27, departmentHead: 'Anita Pillai', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-01' },
{ id: 'd7', name: 'Product', code: 'PRD-007', description: 'Product strategy and development', totalEmployees: 19, departmentHead: 'Kiran Rao', status: 'INACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-10' },
{ id: 'd8', name: 'Sales', code: 'SAL-008', description: 'Revenue generation and client acquisition', totalEmployees: 34, departmentHead: 'Manoj Iyer', status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-10' },
];
const permissionGroups = [
{ title: 'Employee Management', items: ['View Employees', 'Create Employees', 'Edit Employees', 'Delete Employees'] },
{ title: 'Role Management', items: ['View Roles', 'Assign Roles'] },
{ title: 'Department Settings', items: ['Manage Department Settings'] },
];
function StatusBadge(props: { status: string }) {
const active = () => props.status === 'ACTIVE';
return (
<span class={`inline-flex items-center rounded-full px-2.5 py-1 text-[11px] font-semibold ${
active() ? 'bg-[#ECFDF5] text-[#059669]' : 'bg-[#F3F4F6] text-[#6B7280]'
}`}>
<span class={`mr-1.5 h-1.5 w-1.5 rounded-full ${active() ? 'bg-[#059669]' : 'bg-[#9CA3AF]'}`} />
{active() ? 'Active' : 'Inactive'}
</span>
);
}
function FormInput(props: { label: string; required?: boolean; value: string; onInput: (v: string) => void; placeholder?: string; type?: string }) {
return (
<label class="block">
<span class="text-[13px] font-semibold text-[#374151]">
{props.label}{props.required && <span class="ml-0.5 text-[#FF5E13]">*</span>}
</span>
<input
type={props.type ?? 'text'}
value={props.value}
onInput={(e) => props.onInput(e.currentTarget.value)}
placeholder={props.placeholder}
class="mt-1.5 h-[42px] w-full rounded-xl border border-[#E5E7EB] bg-white px-3.5 text-[14px] text-[#111827] outline-none placeholder:text-[#9CA3AF] focus:border-[#FF5E13] focus:ring-2 focus:ring-[rgba(255,94,19,0.1)] transition-colors"
/>
</label>
);
}
export default function DepartmentManagementPage() {
const [view, setView] = createSignal<'list' | 'form'>('list');
const [formTab, setFormTab] = createSignal<'general' | 'settings' | 'permissions'>('general');
const [search, setSearch] = createSignal('');
const [statusFilter, setStatusFilter] = createSignal('all');
const [rows, setRows] = createSignal<DepartmentRecord[]>([]);
const [openMenuId, setOpenMenuId] = createSignal<string | null>(null);
const [editingId, setEditingId] = createSignal<string | null>(null);
const [name, setName] = createSignal('');
const [code, setCode] = createSignal('');
const [description, setDescription] = createSignal('');
const [departmentHead, setDepartmentHead] = createSignal('');
const [departmentEmail, setDepartmentEmail] = createSignal('');
const [status, setStatus] = createSignal<'ACTIVE' | 'INACTIVE'>('ACTIVE');
const [transfersEnabled, setTransfersEnabled] = createSignal(false);
const load = async () => {
try {
const res = await fetch(`/api/gateway/api/admin/departments?page=1&limit=100&q=${encodeURIComponent(search().trim())}`);
if (res.ok) {
const payload = await res.json().catch(() => null);
const list = Array.isArray(payload) ? payload : Array.isArray(payload?.data) ? payload.data : Array.isArray(payload?.items) ? payload.items : [];
if (list.length > 0) {
setRows(list.map((item: any, i: number) => ({
id: String(item.id ?? `dep-${i + 1}`),
name: String(item.name ?? ''),
code: String(item.code ?? ''),
description: String(item.description ?? ''),
totalEmployees: Number(item.totalEmployees ?? item.total_employees ?? 0),
departmentHead: String(item.departmentHead ?? item.department_head ?? ''),
status: String(item.status ?? 'ACTIVE').toUpperCase() === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE',
updatedAt: String(item.updatedAt ?? item.updated_at ?? ''),
createdDate: String(item.createdDate ?? item.created_at ?? ''),
})));
return;
}
}
} catch {}
try {
const data = await listModuleRecords<DepartmentRecord>('department', { q: search().trim() || undefined });
setRows(Array.isArray(data) && data.length > 0 ? data : FALLBACK_DEPARTMENTS);
} catch {
setRows(FALLBACK_DEPARTMENTS);
}
};
onMount(() => void load());
const filteredRows = createMemo(() => {
let r = rows();
if (statusFilter() !== 'all') r = r.filter((d) => d.status === statusFilter().toUpperCase());
const q = search().toLowerCase();
if (q) r = r.filter((d) => d.name.toLowerCase().includes(q) || String(d.code ?? '').toLowerCase().includes(q));
return r;
});
const resetForm = () => {
setEditingId(null); setName(''); setCode(''); setDescription('');
setDepartmentHead(''); setDepartmentEmail(''); setStatus('ACTIVE');
setTransfersEnabled(false); setFormTab('general');
};
const openCreate = () => { resetForm(); setView('form'); };
const openEdit = (row: DepartmentRecord) => {
setEditingId(row.id);
setName(row.name || ''); setCode(String(row.code || ''));
setDescription(String(row.description || ''));
setDepartmentHead(String(row.departmentHead || ''));
setDepartmentEmail(String(row.departmentEmail || ''));
setStatus(row.status === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE');
setTransfersEnabled(Boolean(row.transfersEnabled));
setFormTab('general'); setView('form'); setOpenMenuId(null);
};
const save = async () => {
const payload: Partial<DepartmentRecord> = {
name: name().trim() || 'New Department', code: code().trim() || undefined,
description: description().trim(), departmentHead: departmentHead().trim(),
departmentEmail: departmentEmail().trim(), status: status(),
transfersEnabled: transfersEnabled(),
};
if (editingId()) {
await updateModuleRecord<DepartmentRecord>('department', editingId()!, payload);
} else {
await createModuleRecord<DepartmentRecord>('department', payload);
}
setView('list'); resetForm(); await load();
};
const formatDate = (v?: string) => {
const s = v || '';
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;
return s.slice(0, 10) || '—';
};
const stats = createMemo(() => ({
total: rows().length,
active: rows().filter((d) => d.status === 'ACTIVE').length,
inactive: rows().filter((d) => d.status === 'INACTIVE').length,
totalEmployees: rows().reduce((acc, d) => acc + Number(d.totalEmployees || 0), 0),
}));
return (
<AdminShell>
<div class="w-full space-y-8 pb-8">
{/* Page header */}
<div class="flex items-end justify-between">
<div>
<p class="text-[12px] font-semibold uppercase tracking-widest text-[#FF5E13]">
{view() === 'form' ? 'Department Management' : 'Organisation'}
</p>
<h1 class="mt-1 text-[28px] font-bold leading-tight text-[#111827]">
{view() === 'form' ? (editingId() ? 'Edit Department' : 'Create Department') : 'Department Management'}
</h1>
<p class="mt-1 text-[14px] text-[#6B7280]">
{view() === 'form'
? 'Dashboard / Department Management / ' + (editingId() ? 'Edit Department' : 'Create Department')
: 'Manage all departments and organisational structure'}
</p>
</div>
<Show when={view() === 'list'}>
<button
type="button"
onClick={openCreate}
class="inline-flex items-center gap-2 rounded-xl bg-[#0D0D2A] px-5 py-2.5 text-[13px] font-semibold text-white shadow-sm hover:bg-[#1a1a3e] transition-colors"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Create Department
</button>
</Show>
</div>
{/* ── LIST VIEW ── */}
<Show when={view() === 'list'}>
{/* Summary cards */}
<div class="grid grid-cols-4 gap-5">
{[
{ label: 'Total Departments', value: stats().total, color: 'text-[#FF5E13]', bg: 'bg-[#FFF1EB]' },
{ label: 'Active', value: stats().active, color: 'text-[#059669]', bg: 'bg-[#ECFDF5]' },
{ label: 'Inactive', value: stats().inactive, color: 'text-[#6B7280]', bg: 'bg-[#F3F4F6]' },
{ label: 'Total Employees', value: stats().totalEmployees, color: 'text-[#2563EB]', bg: 'bg-[#EFF6FF]' },
].map((s) => (
<div class="rounded-2xl border border-[#E5E7EB] bg-white p-5 shadow-sm">
<div class={`inline-flex h-10 w-10 items-center justify-center rounded-xl ${s.bg} ${s.color} text-[18px] font-bold`}>
{s.value}
</div>
<p class="mt-3 text-[13px] font-medium text-[#6B7280]">{s.label}</p>
<p class={`mt-0.5 text-[22px] font-bold tracking-tight ${s.color}`}>{s.value}</p>
</div>
))}
</div>
{/* Table card */}
<div class="rounded-2xl border border-[#E5E7EB] bg-white shadow-sm">
{/* Filters */}
<div class="flex items-center gap-3 border-b border-[#F3F4F6] p-5">
{/* Search */}
<div class="relative flex-1 max-w-sm">
<svg class="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-[#9CA3AF]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></svg>
<input
value={search()}
onInput={(e) => { setSearch(e.currentTarget.value); void load(); }}
placeholder="Search departments..."
class="h-[40px] w-full rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] pl-10 pr-4 text-[13px] text-[#111827] outline-none placeholder:text-[#9CA3AF] focus:border-[#FF5E13] focus:bg-white focus:ring-2 focus:ring-[rgba(255,94,19,0.08)] transition-colors"
/>
</div>
{/* Status filter */}
<select
value={statusFilter()}
onChange={(e) => setStatusFilter(e.currentTarget.value)}
class="h-[40px] rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] px-3 pr-8 text-[13px] text-[#374151] outline-none focus:border-[#FF5E13] transition-colors"
>
<option value="all">All Status</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
<p class="ml-auto text-[13px] text-[#6B7280]">
<span class="font-semibold text-[#111827]">{filteredRows().length}</span> departments
</p>
</div>
{/* Table */}
<div class="overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-[#F3F4F6] bg-[#FAFAFA] text-left">
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Department Name</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Code</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Department Head</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Employees</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Status</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Created</th>
<th class="px-6 py-3.5 text-[11px] font-semibold uppercase tracking-wider text-[#9CA3AF]">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-[#F3F4F6]">
<Show
when={filteredRows().length > 0}
fallback={
<tr>
<td colspan="7" class="px-6 py-16 text-center">
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-[#F9FAFB]">
<svg class="h-7 w-7 text-[#9CA3AF]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="3" /><path d="M9 9h6M9 12h6M9 15h4" /></svg>
</div>
<p class="mt-3 text-[15px] font-semibold text-[#111827]">No departments found</p>
<p class="mt-1 text-[13px] text-[#6B7280]">Create your first department to get started.</p>
<button type="button" onClick={openCreate} class="mt-4 inline-flex items-center gap-2 rounded-xl bg-[#0D0D2A] px-4 py-2 text-[13px] font-semibold text-white">
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Create Department
</button>
</td>
</tr>
}
>
<For each={filteredRows()}>
{(row) => (
<tr class="group hover:bg-[#FAFAFA] transition-colors">
<td class="px-6 py-4">
<p class="text-[14px] font-semibold text-[#111827]">{row.name}</p>
<p class="mt-0.5 text-[12px] text-[#9CA3AF] line-clamp-1">{String(row.description || '')}</p>
</td>
<td class="px-6 py-4">
<span class="rounded-lg bg-[#F3F4F6] px-2.5 py-1 text-[12px] font-mono font-semibold text-[#374151]">
{String(row.code || '—')}
</span>
</td>
<td class="px-6 py-4 text-[13px] text-[#374151]">{String(row.departmentHead || '—')}</td>
<td class="px-6 py-4 text-[13px] font-semibold text-[#111827]">{Number(row.totalEmployees || 0)}</td>
<td class="px-6 py-4"><StatusBadge status={row.status} /></td>
<td class="px-6 py-4 text-[13px] text-[#6B7280]">{formatDate(String(row.createdDate || row.updatedAt || ''))}</td>
<td class="relative px-6 py-4">
<button
type="button"
onClick={() => setOpenMenuId(openMenuId() === row.id ? null : row.id)}
class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-[#9CA3AF] hover:bg-[#F3F4F6] hover:text-[#374151] transition-colors"
aria-label="More actions"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="5" r="1.5" /><circle cx="12" cy="12" r="1.5" /><circle cx="12" cy="19" r="1.5" /></svg>
</button>
<Show when={openMenuId() === row.id}>
<div class="absolute right-6 top-12 z-20 w-[200px] rounded-xl border border-[#E5E7EB] bg-white p-1.5 shadow-lg shadow-black/10">
<button
type="button"
onClick={() => openEdit(row)}
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] font-medium text-[#374151] hover:bg-[#F9FAFB]"
>
<svg class="h-4 w-4 text-[#FF5E13]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 20h9" /><path d="m16.5 3.5 4 4L7 21H3v-4L16.5 3.5Z" /></svg>
Edit Department
</button>
<button
type="button"
onClick={async () => { await updateModuleRecord<DepartmentRecord>('department', row.id, { status: row.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE' }); setOpenMenuId(null); await load(); }}
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] font-medium text-[#374151] hover:bg-[#F9FAFB]"
>
<svg class="h-4 w-4 text-[#FF5E13]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="9" /><path d="M9 12l2 2 4-4" /></svg>
{row.status === 'ACTIVE' ? 'Deactivate' : 'Activate'}
</button>
<div class="my-1 h-px bg-[#F3F4F6]" />
<button
type="button"
onClick={async () => { await deleteModuleRecord('department', row.id); setOpenMenuId(null); await load(); }}
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-[13px] font-medium text-[#DC2626] hover:bg-[#FEF2F2]"
>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6M10 11v6M14 11v6" /></svg>
Delete
</button>
</div>
</Show>
</td>
</tr>
)}
</For>
</Show>
</tbody>
</table>
</div>
{/* Pagination */}
<Show when={filteredRows().length > 0}>
<div class="flex items-center justify-between border-t border-[#F3F4F6] px-6 py-4">
<p class="text-[13px] text-[#6B7280]">
Showing <span class="font-semibold text-[#111827]">1{filteredRows().length}</span> of <span class="font-semibold text-[#111827]">{filteredRows().length}</span> departments
</p>
<div class="flex items-center gap-1.5">
<button type="button" class="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[#E5E7EB] text-[#6B7280] hover:bg-[#F9FAFB] transition-colors"></button>
<button type="button" class="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-[#0D0D2A] text-[13px] font-semibold text-white">1</button>
<button type="button" class="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[#E5E7EB] text-[13px] font-medium text-[#374151] hover:bg-[#F9FAFB] transition-colors">2</button>
<button type="button" class="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[#E5E7EB] text-[#6B7280] hover:bg-[#F9FAFB] transition-colors"></button>
</div>
</div>
</Show>
</div>
</Show>
{/* ── FORM VIEW (Create / Edit) ── */}
<Show when={view() === 'form'}>
<div class="rounded-2xl border border-[#E5E7EB] bg-white shadow-sm">
{/* Tab nav */}
<div class="flex items-center gap-1 border-b border-[#F3F4F6] px-6">
{(['general', 'settings', 'permissions'] as const).map((tab, i) => {
const labels = ['General Information', 'Department Settings', 'Permissions'];
return (
<button
type="button"
onClick={() => setFormTab(tab)}
class={`relative px-4 py-4 text-[13px] font-semibold transition-colors ${
formTab() === tab ? 'text-[#111827]' : 'text-[#9CA3AF] hover:text-[#6B7280]'
}`}
>
{labels[i]}
<Show when={formTab() === tab}>
<span class="absolute inset-x-0 bottom-0 h-[2px] rounded-t-full bg-[#FF5E13]" />
</Show>
</button>
);
})}
</div>
<div class="p-6">
{/* General Information */}
<Show when={formTab() === 'general'}>
<div class="space-y-5">
<div class="grid grid-cols-2 gap-5">
<FormInput label="Department Name" required value={name()} onInput={setName} placeholder="e.g. Engineering" />
<FormInput label="Department Code" required value={code()} onInput={setCode} placeholder="e.g. ENG-001" />
</div>
<label class="block">
<span class="text-[13px] font-semibold text-[#374151]">Description</span>
<textarea
value={description()}
onInput={(e) => setDescription(e.currentTarget.value)}
placeholder="Brief description of this department's purpose..."
rows="3"
class="mt-1.5 w-full rounded-xl border border-[#E5E7EB] bg-white px-3.5 py-3 text-[14px] text-[#111827] outline-none placeholder:text-[#9CA3AF] focus:border-[#FF5E13] focus:ring-2 focus:ring-[rgba(255,94,19,0.1)] transition-colors resize-none"
/>
</label>
<div class="grid grid-cols-2 gap-5">
<FormInput label="Department Head" value={departmentHead()} onInput={setDepartmentHead} placeholder="e.g. Arun Kumar" />
<FormInput label="Department Email" type="email" value={departmentEmail()} onInput={setDepartmentEmail} placeholder="dept@nxtgauge.com" />
</div>
</div>
</Show>
{/* Department Settings */}
<Show when={formTab() === 'settings'}>
<div class="space-y-6">
<div>
<p class="text-[14px] font-semibold text-[#111827]">Department Status</p>
<p class="mt-0.5 text-[13px] text-[#6B7280]">Set whether this department is currently active</p>
<div class="mt-3 flex gap-2">
{(['ACTIVE', 'INACTIVE'] as const).map((s) => (
<button
type="button"
onClick={() => setStatus(s)}
class={`h-[38px] rounded-xl border px-5 text-[13px] font-semibold transition-colors ${
status() === s
? s === 'ACTIVE' ? 'border-[#059669] bg-[#ECFDF5] text-[#059669]' : 'border-[#6B7280] bg-[#F3F4F6] text-[#374151]'
: 'border-[#E5E7EB] bg-white text-[#6B7280] hover:bg-[#F9FAFB]'
}`}
>
{s === 'ACTIVE' ? 'Active' : 'Inactive'}
</button>
))}
</div>
</div>
<div>
<p class="text-[14px] font-semibold text-[#111827]">Department Visibility</p>
<p class="mt-0.5 text-[13px] text-[#6B7280]">Choose who can see this department</p>
<div class="mt-3 grid grid-cols-2 gap-3">
{[
{ key: 'internal', label: 'Internal', desc: 'Visible to internal employees only' },
{ key: 'external', label: 'External', desc: 'Visible to external users and partners' },
].map((opt) => (
<div class="flex items-start gap-3 rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] p-4">
<div class="mt-0.5 h-4 w-4 shrink-0 rounded-full border-2 border-[#E5E7EB] bg-white" />
<div>
<p class="text-[13px] font-semibold text-[#111827]">{opt.label}</p>
<p class="mt-0.5 text-[12px] text-[#6B7280]">{opt.desc}</p>
</div>
</div>
))}
</div>
</div>
<div class="flex items-center justify-between rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] p-4">
<div>
<p class="text-[13px] font-semibold text-[#111827]">Allow Employee Transfers</p>
<p class="mt-0.5 text-[12px] text-[#6B7280]">Employees can request to transfer into this department</p>
</div>
<button
type="button"
onClick={() => setTransfersEnabled((v) => !v)}
class={`relative h-6 w-11 rounded-full transition-colors ${transfersEnabled() ? 'bg-[#FF5E13]' : 'bg-[#E5E7EB]'}`}
>
<span class={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform ${transfersEnabled() ? 'translate-x-5' : 'translate-x-0.5'}`} />
</button>
</div>
</div>
</Show>
{/* Permissions */}
<Show when={formTab() === 'permissions'}>
<div class="space-y-6">
<p class="text-[13px] text-[#6B7280]">Select the permissions available to employees in this department.</p>
<For each={permissionGroups}>
{(group) => (
<div>
<p class="mb-3 text-[13px] font-bold uppercase tracking-wider text-[#9CA3AF]">{group.title}</p>
<div class="grid grid-cols-2 gap-2">
<For each={group.items}>
{(item) => (
<label class="flex cursor-pointer items-center gap-3 rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] px-4 py-3 hover:border-[#FF5E13] hover:bg-[#FFF1EB] transition-colors">
<input type="checkbox" class="h-4 w-4 rounded accent-[#FF5E13]" />
<span class="text-[13px] font-medium text-[#374151]">{item}</span>
</label>
)}
</For>
</div>
</div>
)}
</For>
</div>
</Show>
</div>
{/* Form actions */}
<div class="flex items-center justify-end gap-3 border-t border-[#F3F4F6] px-6 py-4">
<button
type="button"
onClick={() => { setView('list'); resetForm(); }}
class="h-[40px] rounded-xl border border-[#E5E7EB] bg-white px-5 text-[13px] font-semibold text-[#374151] hover:bg-[#F9FAFB] transition-colors"
>
Cancel
</button>
<button
type="button"
onClick={() => void save()}
class="h-[40px] rounded-xl bg-[#0D0D2A] px-6 text-[13px] font-semibold text-white hover:bg-[#1a1a3e] transition-colors"
>
{editingId() ? 'Update Department' : 'Create Department'}
</button>
</div>
</div>
</Show>
</div>
</AdminShell>
);
}