feat: wire designation management to real API
Remove FALLBACK_DESIGNATIONS and static DEPARTMENTS array. Fetch departments from API for dropdown, send department_id (UUID) on save, normalize department_name/department_id from backend response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2a3a336114
commit
c911a0c450
1 changed files with 68 additions and 63 deletions
|
|
@ -8,6 +8,7 @@ const API = '/api/gateway';
|
||||||
type DesignationRecord = CrudRecord & {
|
type DesignationRecord = CrudRecord & {
|
||||||
code?: string;
|
code?: string;
|
||||||
department?: string;
|
department?: string;
|
||||||
|
departmentId?: string;
|
||||||
level?: string;
|
level?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
totalEmployees?: number;
|
totalEmployees?: number;
|
||||||
|
|
@ -16,14 +17,7 @@ type DesignationRecord = CrudRecord & {
|
||||||
canApprove?: boolean;
|
canApprove?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FALLBACK_DESIGNATIONS: DesignationRecord[] = [
|
type DepartmentOption = { id: string; name: string };
|
||||||
{ 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 permissionGroups = [
|
const permissionGroups = [
|
||||||
{ title: 'Employee Management', items: ['View Employees', 'Create Employees', 'Edit Employees', 'Delete Employees'] },
|
{ title: 'Employee Management', items: ['View Employees', 'Create Employees', 'Edit Employees', 'Delete Employees'] },
|
||||||
|
|
@ -32,7 +26,6 @@ const permissionGroups = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const LEVELS = ['Intern', 'Junior', 'Mid-Level', 'Senior', 'Lead', 'Manager', 'Director', 'VP', 'C-Level', 'Executive', 'Specialist', 'Analyst'];
|
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'];
|
|
||||||
|
|
||||||
type DesignationListResponse = {
|
type DesignationListResponse = {
|
||||||
designations?: any[];
|
designations?: any[];
|
||||||
|
|
@ -51,16 +44,17 @@ function normalizeDesignation(item: any, idx: number): DesignationRecord {
|
||||||
return {
|
return {
|
||||||
id: String(item.id ?? `des-${idx + 1}`),
|
id: String(item.id ?? `des-${idx + 1}`),
|
||||||
name: String(item.name ?? ''),
|
name: String(item.name ?? ''),
|
||||||
code: String(item.code ?? ''),
|
code: item.code ? String(item.code) : undefined,
|
||||||
department: String(item.department ?? item.department_name ?? ''),
|
department: item.department_name ? String(item.department_name) : undefined,
|
||||||
level: String(item.level ?? ''),
|
departmentId: item.department_id ? String(item.department_id) : undefined,
|
||||||
description: String(item.description ?? ''),
|
level: item.level ? String(item.level) : undefined,
|
||||||
totalEmployees: Number(item.totalEmployees ?? item.total_employees ?? 0),
|
description: item.description ? String(item.description) : undefined,
|
||||||
canManageTeam: Boolean(item.canManageTeam ?? item.can_manage_team ?? false),
|
totalEmployees: Number(item.total_employees ?? 0),
|
||||||
canApprove: Boolean(item.canApprove ?? item.can_approve ?? false),
|
canManageTeam: Boolean(item.can_manage_team ?? false),
|
||||||
|
canApprove: Boolean(item.can_approve ?? false),
|
||||||
status: isActive ? 'ACTIVE' : 'INACTIVE',
|
status: isActive ? 'ACTIVE' : 'INACTIVE',
|
||||||
updatedAt: String(item.updatedAt ?? item.updated_at ?? ''),
|
updatedAt: String(item.updated_at ?? ''),
|
||||||
createdDate: String(item.createdDate ?? item.created_at ?? ''),
|
createdDate: String(item.created_at ?? ''),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +122,7 @@ export default function DesignationManagementPage() {
|
||||||
|
|
||||||
const [name, setName] = createSignal('');
|
const [name, setName] = createSignal('');
|
||||||
const [code, setCode] = createSignal('');
|
const [code, setCode] = createSignal('');
|
||||||
const [department, setDepartment] = createSignal('');
|
const [departmentId, setDepartmentId] = createSignal('');
|
||||||
const [level, setLevel] = createSignal('');
|
const [level, setLevel] = createSignal('');
|
||||||
const [description, setDescription] = createSignal('');
|
const [description, setDescription] = createSignal('');
|
||||||
const [status, setStatus] = createSignal<'ACTIVE' | 'INACTIVE'>('ACTIVE');
|
const [status, setStatus] = createSignal<'ACTIVE' | 'INACTIVE'>('ACTIVE');
|
||||||
|
|
@ -137,52 +131,61 @@ export default function DesignationManagementPage() {
|
||||||
const [isLoading, setIsLoading] = createSignal(false);
|
const [isLoading, setIsLoading] = createSignal(false);
|
||||||
const [isSaving, setIsSaving] = createSignal(false);
|
const [isSaving, setIsSaving] = createSignal(false);
|
||||||
const [error, setError] = createSignal('');
|
const [error, setError] = createSignal('');
|
||||||
|
const [departments, setDepartments] = createSignal<DepartmentOption[]>([]);
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
try {
|
const params = new URLSearchParams({
|
||||||
const params = new URLSearchParams({
|
page: '1',
|
||||||
page: '1',
|
per_page: '100',
|
||||||
per_page: '100',
|
q: search().trim(),
|
||||||
q: search().trim(),
|
});
|
||||||
});
|
if (statusFilter() !== 'all') {
|
||||||
if (statusFilter() !== 'all') {
|
params.set('status', statusFilter());
|
||||||
params.set('status', statusFilter());
|
|
||||||
}
|
|
||||||
if (deptFilter() !== 'all') {
|
|
||||||
params.set('department', deptFilter());
|
|
||||||
}
|
|
||||||
const res = await fetch(`${API}/api/admin/designations?${params.toString()}`);
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`Request failed (${res.status})`);
|
|
||||||
}
|
|
||||||
const payload = (await res.json().catch(() => null)) as DesignationListResponse | null;
|
|
||||||
const list = Array.isArray(payload)
|
|
||||||
? payload
|
|
||||||
: Array.isArray(payload?.designations)
|
|
||||||
? payload.designations
|
|
||||||
: Array.isArray(payload?.data)
|
|
||||||
? payload.data
|
|
||||||
: Array.isArray(payload?.items)
|
|
||||||
? payload.items
|
|
||||||
: [];
|
|
||||||
if (list.length === 0) {
|
|
||||||
setRows(FALLBACK_DESIGNATIONS);
|
|
||||||
} else {
|
|
||||||
setRows(list.map(normalizeDesignation));
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setRows(FALLBACK_DESIGNATIONS);
|
|
||||||
setError('Could not reach designations API, showing fallback data.');
|
|
||||||
}
|
}
|
||||||
|
if (deptFilter() !== 'all') {
|
||||||
|
params.set('department_id', deptFilter());
|
||||||
|
}
|
||||||
|
const res = await fetch(`${API}/api/admin/designations?${params.toString()}`);
|
||||||
|
if (!res.ok) throw new Error(`Request failed (${res.status})`);
|
||||||
|
const payload = (await res.json().catch(() => null)) as DesignationListResponse | null;
|
||||||
|
const list = Array.isArray(payload)
|
||||||
|
? payload
|
||||||
|
: Array.isArray(payload?.designations)
|
||||||
|
? payload.designations
|
||||||
|
: Array.isArray(payload?.data)
|
||||||
|
? payload.data
|
||||||
|
: Array.isArray(payload?.items)
|
||||||
|
? payload.items
|
||||||
|
: [];
|
||||||
|
setRows(list.map(normalizeDesignation));
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(err?.message || 'Could not reach designations API.');
|
||||||
|
setRows([]);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => void load());
|
const loadDepartments = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API}/api/admin/departments?per_page=100`);
|
||||||
|
if (!res.ok) return;
|
||||||
|
const payload = await res.json().catch(() => null);
|
||||||
|
const list: any[] = Array.isArray(payload)
|
||||||
|
? payload
|
||||||
|
: Array.isArray(payload?.departments)
|
||||||
|
? payload.departments
|
||||||
|
: [];
|
||||||
|
setDepartments(list.map((d: any) => ({ id: String(d.id), name: String(d.name) })));
|
||||||
|
} catch {
|
||||||
|
// departments dropdown will just be empty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => { void load(); void loadDepartments(); });
|
||||||
|
|
||||||
const filteredRows = createMemo(() => {
|
const filteredRows = createMemo(() => {
|
||||||
let r = rows();
|
let r = rows();
|
||||||
|
|
@ -209,7 +212,7 @@ export default function DesignationManagementPage() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setEditingId(null); setName(''); setCode(''); setDepartment('');
|
setEditingId(null); setName(''); setCode(''); setDepartmentId('');
|
||||||
setLevel(''); setDescription(''); setStatus('ACTIVE');
|
setLevel(''); setDescription(''); setStatus('ACTIVE');
|
||||||
setCanManageTeam(false); setCanApprove(false); setFormTab('general'); setError('');
|
setCanManageTeam(false); setCanApprove(false); setFormTab('general'); setError('');
|
||||||
};
|
};
|
||||||
|
|
@ -218,9 +221,9 @@ export default function DesignationManagementPage() {
|
||||||
|
|
||||||
const openEdit = (row: DesignationRecord) => {
|
const openEdit = (row: DesignationRecord) => {
|
||||||
setEditingId(row.id);
|
setEditingId(row.id);
|
||||||
setName(row.name || ''); setCode(String(row.code || ''));
|
setName(row.name || ''); setCode(row.code || '');
|
||||||
setDepartment(String(row.department || '')); setLevel(String(row.level || ''));
|
setDepartmentId(row.departmentId || ''); setLevel(row.level || '');
|
||||||
setDescription(String(row.description || ''));
|
setDescription(row.description || '');
|
||||||
setStatus(row.status === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE');
|
setStatus(row.status === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE');
|
||||||
setCanManageTeam(Boolean(row.canManageTeam)); setCanApprove(Boolean(row.canApprove));
|
setCanManageTeam(Boolean(row.canManageTeam)); setCanApprove(Boolean(row.canApprove));
|
||||||
setFormTab('general'); setView('form'); setOpenMenuId(null);
|
setFormTab('general'); setView('form'); setOpenMenuId(null);
|
||||||
|
|
@ -235,16 +238,18 @@ export default function DesignationManagementPage() {
|
||||||
|
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
setError('');
|
setError('');
|
||||||
const payload = {
|
const payload: Record<string, unknown> = {
|
||||||
name: name().trim(),
|
name: name().trim(),
|
||||||
code: code().trim(),
|
code: code().trim(),
|
||||||
department: department().trim(),
|
level: level().trim() || null,
|
||||||
level: level().trim(),
|
|
||||||
description: description().trim() || null,
|
description: description().trim() || null,
|
||||||
status: status(),
|
status: status(),
|
||||||
can_manage_team: canManageTeam(),
|
can_manage_team: canManageTeam(),
|
||||||
can_approve: canApprove(),
|
can_approve: canApprove(),
|
||||||
};
|
};
|
||||||
|
if (departmentId().trim()) {
|
||||||
|
payload.department_id = departmentId().trim();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const endpoint = editingId()
|
const endpoint = editingId()
|
||||||
|
|
@ -594,9 +599,9 @@ export default function DesignationManagementPage() {
|
||||||
<FormInput label="Designation Code" required value={code()} onInput={setCode} placeholder="e.g. SSE-001" />
|
<FormInput label="Designation Code" required value={code()} onInput={setCode} placeholder="e.g. SSE-001" />
|
||||||
</div>
|
</div>
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
|
||||||
<FormSelect label="Department" required value={department()} onChange={setDepartment}>
|
<FormSelect label="Department" value={departmentId()} onChange={setDepartmentId}>
|
||||||
<option value="">Select department</option>
|
<option value="">Select department</option>
|
||||||
<For each={DEPARTMENTS}>{(d) => <option value={d}>{d}</option>}</For>
|
<For each={departments()}>{(d) => <option value={d.id}>{d.name}</option>}</For>
|
||||||
</FormSelect>
|
</FormSelect>
|
||||||
<FormSelect label="Designation Level" required value={level()} onChange={setLevel}>
|
<FormSelect label="Designation Level" required value={level()} onChange={setLevel}>
|
||||||
<option value="">Select level</option>
|
<option value="">Select level</option>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue