nxtgauge-admin-solid/src/routes/admin/designation.tsx
Ashwin Kumar ab487ce884 feat(admin): pixel-perfect UI overhaul for department, designation, roles, employees pages
- Rewrote all layout/spacing with inline styles (Tailwind v4 doesn't generate most utility classes)
- AdminSidebar: all 37 modules in 9 groups, scrollable, 220px/64px collapse, no bottom user section
- AdminShell: header height 64px, user avatar top-right (Gmail-style), removed search bar
- Department: orange-only status badges, dark navy table header (white text), edge-to-edge table, View/Create/All tabs, View action in row menu, form with inline styles
- Designation: full rewrite matching department pattern — same tabs, filter bar, table, form
- Roles/Employees: compact filter bar and table cell sizing fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 01:56:55 +01:00

516 lines
33 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { For, Show, createMemo, createSignal, onMount } from 'solid-js';
import AdminShell from '~/components/AdminShell';
import { updateModuleRecord, deleteModuleRecord } from '~/lib/admin/client';
import type { CrudRecord } from '~/lib/admin/types';
type DesignationRecord = CrudRecord & {
code?: string;
department?: string;
level?: string;
description?: string;
totalEmployees?: number;
createdDate?: string;
canManageTeam?: boolean;
canApprove?: boolean;
};
const FALLBACK_DESIGNATIONS: DesignationRecord[] = [
{ id: 'z1', name: 'Senior Software Engineer', code: 'SSE-001', department: 'Engineering', level: 'Senior', totalEmployees: 12, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-15' },
{ id: 'z2', name: 'Marketing Manager', code: 'MM-002', department: 'Marketing', level: 'Manager', totalEmployees: 8, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-01-20' },
{ id: 'z3', name: 'Sales Executive', code: 'SE-003', department: 'Sales', level: 'Executive', totalEmployees: 15, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-01' },
{ id: 'z4', name: 'HR Specialist', code: 'HRS-004', department: 'Human Resources', level: 'Specialist', totalEmployees: 5, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-05' },
{ id: 'z5', name: 'Product Manager', code: 'PM-005', department: 'Product', level: 'Manager', totalEmployees: 6, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-10' },
{ id: 'z6', name: 'Finance Analyst', code: 'FA-006', department: 'Finance', level: 'Analyst', totalEmployees: 9, status: 'INACTIVE', updatedAt: '2026-03-01', createdDate: '2026-02-15' },
];
const LEVELS = ['Intern', 'Junior', 'Mid-Level', 'Senior', 'Lead', 'Manager', 'Director', 'VP', 'C-Level', 'Executive', 'Specialist', 'Analyst'];
const DEPARTMENTS = ['Engineering', 'Marketing', 'Sales', 'Human Resources', 'Finance', 'Operations', 'Product', 'Customer Success'];
function StatusBadge(props: { status: string }) {
const active = () => props.status === 'ACTIVE';
return (
<span style={`display:inline-flex;align-items:center;border-radius:9999px;border:1px solid ${active() ? '#FFD8C2' : '#D1D5DB'};background:${active() ? '#FFF1EB' : '#F3F4F6'};color:${active() ? '#FF5E13' : '#4B5563'};padding:2px 10px;font-size:12px;font-weight:500`}>
<span style={`display:inline-block;width:6px;height:6px;border-radius:50%;background:${active() ? '#FF5E13' : '#9CA3AF'};margin-right:5px;flex-shrink:0`} />
{active() ? 'Active' : 'Inactive'}
</span>
);
}
function FormInput(props: { label: string; required?: boolean; value: string; onInput: (v: string) => void; placeholder?: string; type?: string }) {
return (
<label style="display:block">
<span style="font-size:13px;font-weight:600;color:#374151">
{props.label}{props.required && <span style="margin-left:2px;color:#FF5E13">*</span>}
</span>
<input
type={props.type ?? 'text'}
value={props.value}
onInput={(e) => props.onInput(e.currentTarget.value)}
placeholder={props.placeholder}
style="display:block;margin-top:6px;height:40px;width:100%;border-radius:10px;border:1px solid #E5E7EB;background:white;padding:0 14px;font-size:13px;color:#111827;outline:none;box-sizing:border-box"
/>
</label>
);
}
function FormSelect(props: { label: string; required?: boolean; value: string; onChange: (v: string) => void; children: any }) {
return (
<label style="display:block">
<span style="font-size:13px;font-weight:600;color:#374151">
{props.label}{props.required && <span style="margin-left:2px;color:#FF5E13">*</span>}
</span>
<select
value={props.value}
onChange={(e) => props.onChange(e.currentTarget.value)}
style="display:block;margin-top:6px;height:40px;width:100%;border-radius:10px;border:1px solid #E5E7EB;background:white;padding:0 14px;font-size:13px;color:#111827;outline:none;box-sizing:border-box;appearance:none"
>
{props.children}
</select>
</label>
);
}
export default function DesignationManagementPage() {
const [view, setView] = createSignal<'list' | 'form'>('list');
const [formTab, setFormTab] = createSignal<'general' | 'settings' | 'permissions'>('general');
const [listTab, setListTab] = createSignal<'all' | 'create' | 'view'>('all');
const [search, setSearch] = createSignal('');
const [deptFilter, setDeptFilter] = createSignal('all');
const [statusFilter, setStatusFilter] = createSignal('all');
const [sortMenuOpen, setSortMenuOpen] = createSignal(false);
const [filterMenuOpen, setFilterMenuOpen] = createSignal(false);
const [rows, setRows] = createSignal<DesignationRecord[]>([]);
const [openMenuId, setOpenMenuId] = createSignal<string | null>(null);
const [editingId, setEditingId] = createSignal<string | null>(null);
const [name, setName] = createSignal('');
const [code, setCode] = createSignal('');
const [department, setDepartment] = createSignal('');
const [level, setLevel] = createSignal('');
const [description, setDescription] = createSignal('');
const [status, setStatus] = createSignal<'ACTIVE' | 'INACTIVE'>('ACTIVE');
const [canManageTeam, setCanManageTeam] = createSignal(false);
const [canApprove, setCanApprove] = createSignal(false);
const [isSaving, setIsSaving] = createSignal(false);
const [error, setError] = createSignal('');
const load = async () => {
try {
const res = await fetch(`/api/gateway/api/admin/designations?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 ?? `des-${i + 1}`),
name: String(item.name ?? ''),
code: String(item.code ?? ''),
department: String(item.department ?? item.department_name ?? ''),
level: String(item.level ?? ''),
description: String(item.description ?? ''),
totalEmployees: Number(item.totalEmployees ?? item.total_employees ?? 0),
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 {}
setRows(FALLBACK_DESIGNATIONS);
};
onMount(() => void load());
const filteredRows = createMemo(() => {
let r = rows();
if (statusFilter() !== 'all') r = r.filter((d) => d.status === statusFilter().toUpperCase());
if (deptFilter() !== 'all') r = r.filter((d) => d.department === deptFilter());
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(''); setDepartment('');
setLevel(''); setDescription(''); setStatus('ACTIVE');
setCanManageTeam(false); setCanApprove(false); setFormTab('general'); setError('');
};
const openCreate = () => { resetForm(); setView('form'); };
const openEdit = (row: DesignationRecord) => {
setEditingId(row.id);
setName(row.name || ''); setCode(String(row.code || ''));
setDepartment(String(row.department || '')); setLevel(String(row.level || ''));
setDescription(String(row.description || ''));
setStatus(row.status === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE');
setCanManageTeam(Boolean(row.canManageTeam)); setCanApprove(Boolean(row.canApprove));
setFormTab('general'); setView('form'); setOpenMenuId(null);
};
const save = async () => {
if (!name().trim()) { setError('Designation name is required.'); setFormTab('general'); return; }
setIsSaving(true); setError('');
try {
const payload: Partial<DesignationRecord> = {
name: name().trim(), code: code().trim() || undefined,
department: department().trim(), level: level().trim(),
description: description().trim(), status: status(),
canManageTeam: canManageTeam(), canApprove: canApprove(),
};
if (editingId()) {
await updateModuleRecord<DesignationRecord>('designation', editingId()!, payload);
} else {
const res = await fetch('/api/gateway/api/admin/designations', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Request failed (${res.status})`);
}
setView('list'); resetForm(); await load();
} catch (err: any) {
setError(err?.message || 'Failed to save designation.');
} finally {
setIsSaving(false);
}
};
const formatDate = (v?: string) => {
const s = v || '';
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;
return s.slice(0, 10) || '—';
};
return (
<AdminShell>
<div style="width:100%;padding-bottom:32px">
{/* Page header */}
<div style="margin-bottom:24px">
<h1 style="font-size:28px;font-weight:700;color:#111827;line-height:1.2">Designation Management</h1>
<p style="margin-top:4px;font-size:14px;color:#6B7280">Manage all job designations and position levels</p>
</div>
{/* ── LIST VIEW ── */}
<Show when={view() === 'list'}>
<div>
{/* Tabs */}
<div style="display:flex;align-items:center;gap:24px;border-bottom:1px solid #E5E7EB">
{([
{ key: 'all', label: 'All Designations', action: () => { setListTab('all'); setStatusFilter('all'); void load(); } },
{ key: 'create', label: 'Create Designation', action: () => { setListTab('create'); openCreate(); } },
{ key: 'view', label: 'View Designation', action: () => setListTab('view') },
] as const).map((tab) => (
<button
type="button"
onClick={tab.action}
style={`padding-bottom:12px;font-size:14px;font-weight:500;background:none;border:none;cursor:pointer;${listTab() === tab.key ? 'color:#FF5E13;border-bottom:2px solid #FF5E13;margin-bottom:-1px' : 'color:#6B7280'}`}
>
{tab.label}
</button>
))}
</div>
{/* Table card */}
<div style="margin-top:1.5rem;margin-left:-24px;margin-right:-24px;overflow:hidden;border-top:1px solid #E5E7EB;border-bottom:1px solid #E5E7EB;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.06)">
{/* Filter bar */}
<div style="display:flex;align-items:center;gap:8px;padding:14px 20px;border-bottom:1px solid #F3F4F6">
<input
value={search()}
onInput={(e) => { setSearch(e.currentTarget.value); void load(); }}
placeholder="Search designations..."
style="height:34px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:13px;color:#111827;outline:none"
/>
<select
value={deptFilter()}
onChange={(e) => setDeptFilter(e.currentTarget.value)}
style="height:34px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 10px;font-size:12px;color:#374151;outline:none"
>
<option value="all">All Departments</option>
<For each={DEPARTMENTS}>{(d) => <option value={d}>{d}</option>}</For>
</select>
<div style="position:relative">
<button
type="button"
onClick={() => { setFilterMenuOpen((v) => !v); setSortMenuOpen(false); }}
style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:12px;font-weight:500;color:#374151;cursor:pointer"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 5h18M6 12h12M10 19h4"/></svg>
Filters
</button>
<Show when={filterMenuOpen()}>
<div style="position:absolute;left:0;top:38px;z-index:30;min-width:160px;border-radius:12px;border:1px solid #E5E7EB;background:white;padding:6px;box-shadow:0 4px 16px rgba(0,0,0,0.1)">
{(['all', 'ACTIVE', 'INACTIVE'] as const).map((s) => (
<button type="button" onClick={() => { setStatusFilter(s); setFilterMenuOpen(false); }} style={`display:block;width:100%;border-radius:8px;padding:8px 12px;text-align:left;font-size:13px;border:none;cursor:pointer;color:${statusFilter() === s ? '#FF5E13' : '#374151'};background:${statusFilter() === s ? '#FFF1EB' : 'transparent'}`}>
{s === 'all' ? 'All Status' : s === 'ACTIVE' ? 'Active' : 'Inactive'}
</button>
))}
</div>
</Show>
</div>
<button type="button" style="display:inline-flex;height:34px;align-items:center;gap:6px;border-radius:8px;background:#0D0D2A;padding:0 12px;font-size:12px;font-weight:600;color:white;border:none;cursor:pointer">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export
</button>
</div>
{/* Table */}
<div style="overflow-x:auto">
<table style="min-width:100%">
<thead>
<tr style="background:#0D0D2A;text-align:left">
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Designation Name</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Code</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Department</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Level</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Employees</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Status</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Created Date</th>
<th style="padding:10px 20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.05em;color:#FFFFFF;white-space:nowrap">Actions</th>
</tr>
</thead>
<tbody>
<Show
when={filteredRows().length > 0}
fallback={
<tr>
<td colspan="8" style="padding:64px 24px;text-align:center">
<p style="font-size:15px;font-weight:600;color:#111827">No designations found</p>
<p style="margin-top:4px;font-size:13px;color:#6B7280">Create your first designation to get started.</p>
<button type="button" onClick={openCreate} style="margin-top:16px;display:inline-flex;align-items:center;gap:8px;border-radius:12px;background:#0D0D2A;padding:8px 16px;font-size:13px;font-weight:600;color:white;border:none;cursor:pointer">
Create Designation
</button>
</td>
</tr>
}
>
<For each={filteredRows()}>
{(row) => (
<tr style="border-bottom:1px solid #F3F4F6" class="hover:bg-[#FAFAFA] transition-colors">
<td style="padding:12px 20px">
<p style="font-size:14px;font-weight:600;color:#111827">{row.name}</p>
</td>
<td style="padding:12px 20px">
<span style="font-size:12px;font-family:monospace;color:#6B7280">{String(row.code || '—')}</span>
</td>
<td style="padding:12px 20px;font-size:13px;color:#374151">{String(row.department || '—')}</td>
<td style="padding:12px 20px">
<span style="display:inline-flex;border-radius:9999px;background:#EFF6FF;color:#2563EB;padding:2px 10px;font-size:12px;font-weight:500">{String(row.level || '—')}</span>
</td>
<td style="padding:12px 20px;font-size:14px;font-weight:600;color:#111827">{Number(row.totalEmployees || 0)}</td>
<td style="padding:12px 20px"><StatusBadge status={row.status} /></td>
<td style="padding:12px 20px;font-size:13px;color:#6B7280">{formatDate(String(row.createdDate || row.updatedAt || ''))}</td>
<td style="padding:12px 20px;position:relative">
<button
type="button"
onClick={() => setOpenMenuId(openMenuId() === row.id ? null : row.id)}
style="display:inline-flex;width:32px;height:32px;align-items:center;justify-content:center;border-radius:8px;border:none;background:none;color:#9CA3AF;cursor:pointer"
aria-label="More actions"
>
<svg style="width:16px;height:16px" 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 style="position:absolute;right:20px;top:44px;z-index:20;width:200px;border-radius:12px;border:1px solid #E5E7EB;background:white;padding:6px;box-shadow:0 4px 16px rgba(0,0,0,0.1)">
<button type="button" onClick={() => openEdit(row)} style="display:flex;width:100%;align-items:center;gap:10px;border-radius:8px;padding:8px 12px;font-size:13px;font-weight:500;color:#374151;background:none;border:none;cursor:pointer">
<svg style="width:16px;height:16px;color:#FF5E13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="m16.5 3.5 4 4L7 21H3v-4L16.5 3.5Z"/></svg>
Edit Designation
</button>
<button type="button" onClick={async () => { await updateModuleRecord<DesignationRecord>('designation', row.id, { status: row.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE' }); setOpenMenuId(null); await load(); }} style="display:flex;width:100%;align-items:center;gap:10px;border-radius:8px;padding:8px 12px;font-size:13px;font-weight:500;color:#374151;background:none;border:none;cursor:pointer">
<svg style="width:16px;height:16px;color:#FF5E13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M9 12l2 2 4-4"/></svg>
{row.status === 'ACTIVE' ? 'Deactivate' : 'Activate'}
</button>
<div style="height:1px;background:#F3F4F6;margin:4px 0" />
<button type="button" onClick={async () => { if (!window.confirm(`Delete designation "${row.name}"?`)) return; await deleteModuleRecord('designation', row.id); setOpenMenuId(null); await load(); }} style="display:flex;width:100%;align-items:center;gap:10px;border-radius:8px;padding:8px 12px;font-size:13px;font-weight:500;color:#DC2626;background:none;border:none;cursor:pointer">
<svg style="width:16px;height:16px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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 style="display:flex;align-items:center;justify-content:space-between;border-top:1px solid #F3F4F6;padding:12px 20px">
<p style="font-size:13px;color:#6B7280">
Showing <strong style="font-weight:600;color:#111827">1{filteredRows().length}</strong> of <strong style="font-weight:600;color:#111827">{filteredRows().length}</strong> designations
</p>
<div style="display:flex;align-items:center;gap:4px">
<button type="button" style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#6B7280;cursor:pointer;font-size:15px"></button>
<button type="button" style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;background:#FF5E13;color:white;font-size:13px;font-weight:600;border:none;cursor:pointer">1</button>
<button type="button" style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#374151;font-size:13px;font-weight:500;cursor:pointer">2</button>
<button type="button" style="display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:7px;border:1px solid #E5E7EB;background:white;color:#6B7280;cursor:pointer;font-size:15px"></button>
</div>
</div>
</Show>
</div>
</div>
</Show>
{/* ── FORM VIEW ── */}
<Show when={view() === 'form'}>
{/* Top tabs */}
<div style="display:flex;align-items:center;gap:24px;border-bottom:1px solid #E5E7EB">
<button type="button" onClick={() => { setView('list'); resetForm(); }} style="padding-bottom:12px;font-size:14px;font-weight:500;color:#6B7280;background:none;border:none;cursor:pointer">
All Designations
</button>
<button type="button" style="padding-bottom:12px;font-size:14px;font-weight:500;color:#FF5E13;border:none;border-bottom:2px solid #FF5E13;background:none;cursor:pointer;margin-bottom:-1px">
{editingId() ? 'Edit Designation' : 'Create Designation'}
</button>
</div>
<div style="margin-top:24px;border-radius:16px;border:1px solid #E5E7EB;background:white;box-shadow:0 1px 4px rgba(0,0,0,0.06);overflow:hidden">
{/* Sub-tabs */}
<div style="display:flex;align-items:center;gap:4px;border-bottom:1px solid #E5E7EB;padding:0 24px">
{(['general', 'settings', 'permissions'] as const).map((tab, i) => {
const labels = ['General Information', 'Designation Settings', 'Permissions'];
const active = () => formTab() === tab;
return (
<button
type="button"
onClick={() => setFormTab(tab)}
style={`position:relative;padding:14px 8px;font-size:13px;font-weight:500;background:none;border:none;cursor:pointer;color:${active() ? '#FF5E13' : '#6B7280'}`}
>
{labels[i]}
<Show when={active()}>
<span style="position:absolute;left:0;right:0;bottom:0;height:2px;background:#FF5E13;border-radius:2px 2px 0 0" />
</Show>
</button>
);
})}
</div>
<div style="padding:24px">
<Show when={error()}>
<div style="margin-bottom:20px;border-radius:10px;border:1px solid #FECACA;background:#FEF2F2;padding:12px 16px;font-size:13px;color:#B91C1C">
{error()}
</div>
</Show>
{/* General Information */}
<Show when={formTab() === 'general'}>
<div style="display:flex;flex-direction:column;gap:20px">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
<FormInput label="Designation Name" required value={name()} onInput={setName} placeholder="e.g. Senior Software Engineer" />
<FormInput label="Designation Code" required value={code()} onInput={setCode} placeholder="e.g. SSE-001" />
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
<FormSelect label="Department" required value={department()} onChange={setDepartment}>
<option value="">Select department</option>
<For each={DEPARTMENTS}>{(d) => <option value={d}>{d}</option>}</For>
</FormSelect>
<FormSelect label="Designation Level" required value={level()} onChange={setLevel}>
<option value="">Select level</option>
<For each={LEVELS}>{(l) => <option value={l}>{l}</option>}</For>
</FormSelect>
</div>
<label style="display:block">
<span style="font-size:13px;font-weight:600;color:#374151">Description</span>
<textarea
value={description()}
onInput={(e) => setDescription(e.currentTarget.value)}
placeholder="Brief description of this designation's responsibilities..."
rows="3"
style="display:block;margin-top:6px;width:100%;border-radius:10px;border:1px solid #E5E7EB;background:white;padding:10px 14px;font-size:13px;color:#111827;outline:none;resize:none;box-sizing:border-box;font-family:inherit"
/>
</label>
</div>
</Show>
{/* Designation Settings */}
<Show when={formTab() === 'settings'}>
<div style="display:flex;flex-direction:column;gap:32px">
<div>
<p style="font-size:14px;font-weight:600;color:#111827">Designation Status</p>
<p style="margin-top:2px;font-size:13px;color:#6B7280">Set whether this designation is currently active</p>
<div style="margin-top:12px;display:flex;gap:10px">
{(['ACTIVE', 'INACTIVE'] as const).map((s) => (
<button
type="button"
onClick={() => setStatus(s)}
style={`height:38px;border-radius:10px;padding:0 20px;font-size:13px;font-weight:600;cursor:pointer;border:1px solid ${status() === s ? '#FF5E13' : '#E5E7EB'};background:${status() === s ? '#FFF3EE' : 'white'};color:${status() === s ? '#FF5E13' : '#6B7280'}`}
>
{s === 'ACTIVE' ? 'Active' : 'Inactive'}
</button>
))}
</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;border-radius:12px;border:1px solid #E5E7EB;background:#F9FAFB;padding:14px 16px">
<div>
<p style="font-size:13px;font-weight:600;color:#111827">Can Manage Team</p>
<p style="margin-top:2px;font-size:12px;color:#6B7280">This designation can manage team members</p>
</div>
<button
type="button"
onClick={() => setCanManageTeam((v) => !v)}
style={`position:relative;width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;background:${canManageTeam() ? '#FF5E13' : '#E5E7EB'};transition:background 0.2s;flex-shrink:0`}
>
<span style={`position:absolute;top:2px;width:20px;height:20px;border-radius:50%;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.2);transition:left 0.2s;left:${canManageTeam() ? '22px' : '2px'}`} />
</button>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;border-radius:12px;border:1px solid #E5E7EB;background:#F9FAFB;padding:14px 16px">
<div>
<p style="font-size:13px;font-weight:600;color:#111827">Can Approve Requests</p>
<p style="margin-top:2px;font-size:12px;color:#6B7280">This designation can approve employee requests</p>
</div>
<button
type="button"
onClick={() => setCanApprove((v) => !v)}
style={`position:relative;width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;background:${canApprove() ? '#FF5E13' : '#E5E7EB'};transition:background 0.2s;flex-shrink:0`}
>
<span style={`position:absolute;top:2px;width:20px;height:20px;border-radius:50%;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.2);transition:left 0.2s;left:${canApprove() ? '22px' : '2px'}`} />
</button>
</div>
</div>
</Show>
{/* Permissions */}
<Show when={formTab() === 'permissions'}>
<div style="display:flex;flex-direction:column;gap:16px">
<p style="font-size:13px;color:#6B7280">Select the permissions available to employees with this designation.</p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
{['View Employees', 'Create Employees', 'Edit Employees', 'Delete Employees', 'Assign Roles', 'Approve Requests', 'Manage Team Members'].map((item) => (
<label style="display:flex;align-items:center;gap:10px;border-radius:10px;border:1px solid #E5E7EB;background:#F9FAFB;padding:10px 14px;cursor:pointer">
<input type="checkbox" style="width:14px;height:14px;accent-color:#FF5E13;cursor:pointer" />
<span style="font-size:13px;font-weight:500;color:#374151">{item}</span>
</label>
))}
</div>
</div>
</Show>
</div>
{/* Form actions */}
<div style="display:flex;align-items:center;justify-content:flex-end;gap:10px;border-top:1px solid #E5E7EB;padding:14px 24px">
<button
type="button"
onClick={() => { setView('list'); resetForm(); }}
style="height:38px;border-radius:10px;border:1px solid #E5E7EB;background:white;padding:0 20px;font-size:13px;font-weight:600;color:#374151;cursor:pointer"
>
Cancel
</button>
<button
type="button"
onClick={() => void save()}
disabled={isSaving()}
style="height:38px;border-radius:10px;background:#0D0D2A;padding:0 24px;font-size:13px;font-weight:600;color:white;border:none;cursor:pointer"
>
{isSaving() ? 'Saving...' : editingId() ? 'Update Designation' : 'Create Designation'}
</button>
</div>
</div>
</Show>
</div>
</AdminShell>
);
}