2026-03-26 06:20:22 +01:00
|
|
|
|
import { For, Show, createMemo, createSignal, onMount } from 'solid-js';
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
import AdminShell from '~/components/AdminShell';
|
2026-03-26 06:20:22 +01:00
|
|
|
|
import { createModuleRecord, deleteModuleRecord, listModuleRecords, updateModuleRecord } from '~/lib/admin/client';
|
|
|
|
|
|
import type { CrudRecord } from '~/lib/admin/types';
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
type DesignationRecord = CrudRecord & {
|
|
|
|
|
|
code?: string;
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
department?: string;
|
2026-03-26 06:20:22 +01:00
|
|
|
|
level?: string;
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
description?: string;
|
2026-03-26 06:20:22 +01:00
|
|
|
|
totalEmployees?: number;
|
|
|
|
|
|
createdDate?: string;
|
|
|
|
|
|
canManageTeam?: boolean;
|
|
|
|
|
|
canApprove?: boolean;
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
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-03-01' },
|
|
|
|
|
|
{ id: 'z2', name: 'Marketing Manager', code: 'MM-002', department: 'Marketing', level: 'Manager', totalEmployees: 8, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-03-01' },
|
|
|
|
|
|
{ id: 'z3', name: 'Sales Executive', code: 'SE-003', department: 'Sales', level: 'Executive', totalEmployees: 15, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-03-01' },
|
|
|
|
|
|
{ id: 'z4', name: 'HR Specialist', code: 'HRS-004', department: 'Human Resources', level: 'Specialist', totalEmployees: 5, status: 'ACTIVE', updatedAt: '2026-03-01', createdDate: '2026-03-01' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
export default function DesignationManagementPage() {
|
|
|
|
|
|
const [mainTab, setMainTab] = createSignal<'all' | 'create'>('all');
|
|
|
|
|
|
const [createTab, setCreateTab] = createSignal<'general' | 'settings' | 'permissions'>('general');
|
|
|
|
|
|
const [search, setSearch] = createSignal('');
|
|
|
|
|
|
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 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 ?? item.designation_id ?? `des-${i + 1}`),
|
|
|
|
|
|
name: String(item.name ?? item.designation_name ?? ''),
|
|
|
|
|
|
code: String(item.code ?? item.designation_code ?? ''),
|
|
|
|
|
|
department: String(item.department ?? item.department_name ?? ''),
|
|
|
|
|
|
level: String(item.level ?? ''),
|
|
|
|
|
|
description: String(item.description ?? ''),
|
|
|
|
|
|
totalEmployees: Number(item.totalEmployees ?? item.total_employees ?? item.employee_count ?? 0),
|
|
|
|
|
|
status: String(item.status ?? 'ACTIVE').toUpperCase() === 'INACTIVE' ? 'INACTIVE' : 'ACTIVE',
|
|
|
|
|
|
updatedAt: String(item.updatedAt ?? item.updated_at ?? new Date().toISOString().slice(0, 10)),
|
|
|
|
|
|
createdDate: String(item.createdDate ?? item.created_at ?? new Date().toISOString().slice(0, 10)),
|
|
|
|
|
|
})),
|
|
|
|
|
|
);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const data = await listModuleRecords<DesignationRecord>('designation', { q: search().trim() || undefined });
|
|
|
|
|
|
setRows(Array.isArray(data) && data.length > 0 ? data : FALLBACK_DESIGNATIONS);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
setRows(FALLBACK_DESIGNATIONS);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
onMount(() => void load());
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
const resetForm = () => {
|
2026-03-26 06:20:22 +01:00
|
|
|
|
setEditingId(null);
|
|
|
|
|
|
setName('');
|
|
|
|
|
|
setCode('');
|
|
|
|
|
|
setDepartment('');
|
|
|
|
|
|
setLevel('');
|
|
|
|
|
|
setDescription('');
|
|
|
|
|
|
setStatus('ACTIVE');
|
|
|
|
|
|
setCanManageTeam(false);
|
|
|
|
|
|
setCanApprove(false);
|
|
|
|
|
|
setCreateTab('general');
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const openCreate = () => {
|
2026-03-26 06:20:22 +01:00
|
|
|
|
resetForm();
|
|
|
|
|
|
setMainTab('create');
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
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));
|
|
|
|
|
|
setMainTab('create');
|
|
|
|
|
|
setCreateTab('general');
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
const saveDesignation = async () => {
|
|
|
|
|
|
const payload: Partial<DesignationRecord> = {
|
|
|
|
|
|
name: name().trim() || 'New Designation',
|
|
|
|
|
|
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 {
|
|
|
|
|
|
await createModuleRecord<DesignationRecord>('designation', payload);
|
|
|
|
|
|
}
|
|
|
|
|
|
setMainTab('all');
|
|
|
|
|
|
setOpenMenuId(null);
|
|
|
|
|
|
resetForm();
|
|
|
|
|
|
await load();
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
const filteredRows = createMemo(() => rows() ?? []);
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
const formatDate = (value?: string) => {
|
|
|
|
|
|
const input = value || '';
|
|
|
|
|
|
if (/^\d{4}-\d{2}-\d{2}$/.test(input)) return input;
|
|
|
|
|
|
return (input || new Date().toISOString().slice(0, 10)).slice(0, 10);
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
return (
|
|
|
|
|
|
<AdminShell>
|
|
|
|
|
|
<div class="space-y-5">
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h1 class="text-[24px] font-semibold leading-[1.1] tracking-[-0.01em] text-[#050026]">Designation Management</h1>
|
|
|
|
|
|
<p class="mt-2 text-[16px] leading-[1.35] text-[#7a8099]">Manage all designations and job positions</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="overflow-hidden rounded-[24px] border border-[#d9dde6] bg-[#f7f7f8]">
|
|
|
|
|
|
<div class="flex items-center gap-2 border-b border-[#e1e5ee] px-5 pt-4">
|
|
|
|
|
|
<button onClick={() => setMainTab('all')} class={`relative px-8 pb-4 pt-2 text-[16px] font-semibold ${mainTab() === 'all' ? 'text-[#0c123f]' : 'text-[#737a96]'}`}>
|
|
|
|
|
|
All Designations
|
|
|
|
|
|
<Show when={mainTab() === 'all'}><span class="absolute inset-x-0 -bottom-[1px] h-[4px] rounded-full bg-[#0a0a50]" /></Show>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</button>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<button onClick={openCreate} class={`relative px-8 pb-4 pt-2 text-[16px] font-semibold ${mainTab() === 'create' ? 'text-[#0c123f]' : 'text-[#737a96]'}`}>
|
|
|
|
|
|
{editingId() ? 'Edit Designation' : 'Create Designation'}
|
|
|
|
|
|
<Show when={mainTab() === 'create'}><span class="absolute inset-x-0 -bottom-[1px] h-[4px] rounded-full bg-[#0a0a50]" /></Show>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<Show when={mainTab() === 'all'}>
|
|
|
|
|
|
<div class="space-y-5 p-5">
|
|
|
|
|
|
<div class="grid gap-3 md:grid-cols-[1fr_190px_130px]">
|
|
|
|
|
|
<label class="flex h-[48px] items-center rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 text-[16px] text-[#8a90a8]">
|
|
|
|
|
|
<svg class="mr-3 h-5 w-5 text-[#8a90a8]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" 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 designations..." class="w-full border-0 bg-transparent text-[16px] text-[#1a2147] outline-none placeholder:text-[#8a90a8]" />
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div class="h-[48px] rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8]" />
|
|
|
|
|
|
<div class="h-[48px] rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8]" />
|
2026-03-26 00:01:15 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
|
|
|
|
|
|
<div class="relative rounded-[18px] border border-[#d8dce6] bg-[#f7f7f8]">
|
|
|
|
|
|
<table class="min-w-full table-fixed text-left">
|
|
|
|
|
|
<thead class="bg-[#030047] text-white">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th class="w-[17%] px-6 py-4 text-[14px] font-semibold">DESIGNATION NAME</th>
|
|
|
|
|
|
<th class="w-[16%] px-6 py-4 text-[14px] font-semibold">DESIGNATION CODE</th>
|
|
|
|
|
|
<th class="w-[18%] px-6 py-4 text-[14px] font-semibold">DEPARTMENT</th>
|
|
|
|
|
|
<th class="w-[12%] px-6 py-4 text-[14px] font-semibold">LEVEL</th>
|
|
|
|
|
|
<th class="w-[11%] px-6 py-4 text-[14px] font-semibold">TOTAL EMPLOYEES</th>
|
|
|
|
|
|
<th class="w-[10%] px-6 py-4 text-[14px] font-semibold">STATUS</th>
|
|
|
|
|
|
<th class="w-[10%] px-6 py-4 text-[14px] font-semibold">CREATED DATE</th>
|
|
|
|
|
|
<th class="w-[6%] px-6 py-4 text-[14px] font-semibold">ACTIONS</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody class="divide-y divide-[#dde1ea] text-[#222948]">
|
|
|
|
|
|
<For each={filteredRows()}>
|
|
|
|
|
|
{(row) => (
|
|
|
|
|
|
<tr class="bg-[#f7f7f8]">
|
|
|
|
|
|
<td class="px-6 py-4 text-[15px] font-semibold">{row.name}</td>
|
|
|
|
|
|
<td class="px-6 py-4 text-[15px] font-medium text-[#505779]">{String(row.code || '')}</td>
|
|
|
|
|
|
<td class="px-6 py-4 text-[14px] font-medium text-[#6b7393]">{String(row.department || '')}</td>
|
|
|
|
|
|
<td class="px-6 py-4 text-[14px] font-medium text-[#6b7393]">{String(row.level || '')}</td>
|
|
|
|
|
|
<td class="px-6 py-4 text-[15px] font-semibold">{Number(row.totalEmployees || 0)}</td>
|
|
|
|
|
|
<td class="px-6 py-4"><span class={`inline-flex rounded-[10px] border px-3 py-1.5 text-[14px] font-semibold ${row.status === 'ACTIVE' ? 'border-[#ffc2aa] bg-[#ffeee6] text-[#fd6116]' : 'border-[#c7ccda] bg-[#eceff6] text-[#101848]'}`}>{row.status === 'ACTIVE' ? 'Active' : 'Inactive'}</span></td>
|
|
|
|
|
|
<td class="px-6 py-4 text-[14px] font-medium text-[#6b7393]">{formatDate(String(row.createdDate || row.updatedAt || ''))}</td>
|
|
|
|
|
|
<td class="relative px-6 py-4">
|
|
|
|
|
|
<button onClick={() => setOpenMenuId(openMenuId() === row.id ? null : row.id)} class="inline-flex h-10 w-10 items-center justify-center rounded-lg text-[#6c7292] hover:bg-[#eceff5]" aria-label="More actions"><span class="text-[20px] leading-none">⋮</span></button>
|
|
|
|
|
|
<Show when={openMenuId() === row.id}>
|
|
|
|
|
|
<div class="absolute right-6 top-14 z-20 w-[220px] rounded-2xl border border-[#d6dbe6] bg-white p-2 shadow-[0_16px_28px_rgba(5,0,38,0.16)]">
|
|
|
|
|
|
<button onClick={() => openEdit(row)} class="flex w-full items-center gap-3 rounded-xl px-3 py-2 text-left text-[16px] font-medium text-[#20284d] hover:bg-[#f5f7fb]"><span class="text-[#fd6116]">✎</span>Edit Designation</button>
|
|
|
|
|
|
<button onClick={async () => { await updateModuleRecord<DesignationRecord>('designation', row.id, { status: row.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE' }); setOpenMenuId(null); await load(); }} class="flex w-full items-center gap-3 rounded-xl px-3 py-2 text-left text-[16px] font-medium text-[#20284d] hover:bg-[#f5f7fb]"><span class="text-[#fd6116]">⊗</span>{row.status === 'ACTIVE' ? 'Deactivate' : 'Activate'}</button>
|
|
|
|
|
|
<button onClick={async () => { await deleteModuleRecord('designation', row.id); setOpenMenuId(null); await load(); }} class="flex w-full items-center gap-3 rounded-xl px-3 py-2 text-left text-[16px] font-medium text-[#20284d] hover:bg-[#f5f7fb]"><span class="text-[#fd6116]">🗑</span>Delete</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Show>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</For>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center justify-between border-t border-[#dde1ea] px-6 py-4">
|
|
|
|
|
|
<p class="text-[14px] text-[#707895]">Showing <span class="font-semibold text-[#283055]">1-{rows().length}</span> of <span class="font-semibold text-[#283055]">{rows().length}</span> designations</p>
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<button class="inline-flex h-10 w-10 items-center justify-center rounded-[12px] border border-[#d5dae6] text-[#8a90a8]">‹</button>
|
|
|
|
|
|
<button class="inline-flex h-10 w-10 items-center justify-center rounded-[12px] bg-[#fd6116] font-semibold text-white">1</button>
|
|
|
|
|
|
<button class="inline-flex h-10 w-10 items-center justify-center rounded-[12px] border border-[#d5dae6] text-[#8a90a8]">›</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-26 00:01:15 +01:00
|
|
|
|
</div>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
</Show>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<Show when={mainTab() === 'create'}>
|
|
|
|
|
|
<div class="space-y-6 p-5">
|
|
|
|
|
|
<div class="flex items-center gap-2 border-b border-[#e1e5ee] pb-3">
|
|
|
|
|
|
<button onClick={() => setCreateTab('general')} class={`relative px-6 pb-3 text-[16px] font-semibold ${createTab() === 'general' ? 'text-[#0b123f]' : 'text-[#767d98]'}`}>General Information<Show when={createTab() === 'general'}><span class="absolute inset-x-0 -bottom-[2px] h-[4px] rounded-full bg-[#0a0a50]" /></Show></button>
|
|
|
|
|
|
<button onClick={() => setCreateTab('settings')} class={`relative px-6 pb-3 text-[16px] font-semibold ${createTab() === 'settings' ? 'text-[#0b123f]' : 'text-[#767d98]'}`}>Designation Settings<Show when={createTab() === 'settings'}><span class="absolute inset-x-0 -bottom-[2px] h-[4px] rounded-full bg-[#0a0a50]" /></Show></button>
|
|
|
|
|
|
<button onClick={() => setCreateTab('permissions')} class={`relative px-6 pb-3 text-[16px] font-semibold ${createTab() === 'permissions' ? 'text-[#0b123f]' : 'text-[#767d98]'}`}>Permissions<Show when={createTab() === 'permissions'}><span class="absolute inset-x-0 -bottom-[2px] h-[4px] rounded-full bg-[#0a0a50]" /></Show></button>
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
</div>
|
2026-03-26 00:01:15 +01:00
|
|
|
|
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<Show when={createTab() === 'general'}>
|
|
|
|
|
|
<div class="space-y-5">
|
|
|
|
|
|
<div class="grid gap-5 md:grid-cols-2">
|
|
|
|
|
|
<label class="block text-[14px] font-semibold text-[#101848]">Designation Name <span class="text-[#fd6116]">*</span><input value={name()} onInput={(e) => setName(e.currentTarget.value)} class="mt-2 h-[48px] w-full rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 text-[16px] text-[#1a2147] outline-none" placeholder="Enter designation name" /></label>
|
|
|
|
|
|
<label class="block text-[14px] font-semibold text-[#101848]">Designation Code <span class="text-[#fd6116]">*</span><input value={code()} onInput={(e) => setCode(e.currentTarget.value)} class="mt-2 h-[48px] w-full rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 text-[16px] text-[#1a2147] outline-none" placeholder="e.g., SSE-001" /></label>
|
2026-03-26 00:01:15 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<div class="grid gap-5 md:grid-cols-2">
|
|
|
|
|
|
<label class="block text-[14px] font-semibold text-[#101848]">Department <span class="text-[#fd6116]">*</span><input value={department()} onInput={(e) => setDepartment(e.currentTarget.value)} class="mt-2 h-[48px] w-full rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 text-[16px] text-[#1a2147] outline-none" /></label>
|
|
|
|
|
|
<label class="block text-[14px] font-semibold text-[#101848]">Designation Level <span class="text-[#fd6116]">*</span><input value={level()} onInput={(e) => setLevel(e.currentTarget.value)} class="mt-2 h-[48px] w-full rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 text-[16px] text-[#1a2147] outline-none" /></label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<label class="block text-[14px] font-semibold text-[#101848]">Description<textarea value={description()} onInput={(e) => setDescription(e.currentTarget.value)} class="mt-2 h-[110px] w-full rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-4 py-3 text-[16px] text-[#1a2147] outline-none" placeholder="Enter designation description" /></label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
|
|
|
|
<Show when={createTab() === 'settings'}>
|
|
|
|
|
|
<div class="space-y-6">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p class="text-[18px] font-semibold text-[#101848]">Designation Status</p>
|
|
|
|
|
|
<div class="mt-3 flex gap-3">
|
|
|
|
|
|
<button onClick={() => setStatus('ACTIVE')} class={`h-[44px] rounded-[12px] border px-6 text-[16px] font-semibold ${status() === 'ACTIVE' ? 'border-[#fd6116] bg-[#fd6116] text-white' : 'border-[#d3d8e4] bg-[#f7f7f8] text-[#1a2147]'}`}>Active</button>
|
|
|
|
|
|
<button onClick={() => setStatus('INACTIVE')} class={`h-[44px] rounded-[12px] border px-6 text-[16px] font-semibold ${status() === 'INACTIVE' ? 'border-[#fd6116] bg-[#fd6116] text-white' : 'border-[#d3d8e4] bg-[#f7f7f8] text-[#1a2147]'}`}>Inactive</button>
|
2026-03-26 00:01:15 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
<div class="space-y-3">
|
|
|
|
|
|
<div class="flex items-center justify-between rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-5 py-4">
|
|
|
|
|
|
<div><p class="text-[16px] font-semibold text-[#101848]">Allow Designation to Manage Team Members</p><p class="text-[14px] text-[#7d849f]">Enable this designation to manage team members</p></div>
|
|
|
|
|
|
<button onClick={() => setCanManageTeam((v) => !v)} class={`relative h-8 w-14 rounded-full transition ${canManageTeam() ? 'bg-[#fd6116]' : 'bg-[#e0e4ec]'}`}><span class={`absolute top-1 h-6 w-6 rounded-full bg-white transition ${canManageTeam() ? 'left-7' : 'left-1'}`} /></button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center justify-between rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-5 py-4">
|
|
|
|
|
|
<div><p class="text-[16px] font-semibold text-[#101848]">Allow Approval Permissions</p><p class="text-[14px] text-[#7d849f]">Enable this designation to approve requests</p></div>
|
|
|
|
|
|
<button onClick={() => setCanApprove((v) => !v)} class={`relative h-8 w-14 rounded-full transition ${canApprove() ? 'bg-[#fd6116]' : 'bg-[#e0e4ec]'}`}><span class={`absolute top-1 h-6 w-6 rounded-full bg-white transition ${canApprove() ? 'left-7' : 'left-1'}`} /></button>
|
|
|
|
|
|
</div>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
|
|
|
|
<Show when={createTab() === 'permissions'}>
|
|
|
|
|
|
<div class="space-y-5">
|
|
|
|
|
|
<p class="text-[16px] text-[#707895]">Select permissions for this designation</p>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h3 class="text-[18px] font-semibold text-[#11194a]">Employee Management</h3>
|
|
|
|
|
|
<div class="mt-3 space-y-3">
|
|
|
|
|
|
<For each={['View Employees', 'Create Employees', 'Edit Employees', 'Delete Employees']}>{(label) => <div class="rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-6 py-3 text-[16px] font-semibold text-[#1c244a]">{label}</div>}</For>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h3 class="text-[18px] font-semibold text-[#11194a]">Additional Permissions</h3>
|
|
|
|
|
|
<div class="mt-3 rounded-[14px] border border-[#d9dde6] bg-[#f7f7f8] px-6 py-3 text-[16px] font-semibold text-[#1c244a]">Assign Roles</div>
|
|
|
|
|
|
</div>
|
2026-03-26 00:01:15 +01:00
|
|
|
|
</div>
|
2026-03-26 06:20:22 +01:00
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex justify-end gap-3 border-t border-[#e1e5ee] pt-4">
|
|
|
|
|
|
<button onClick={() => { setMainTab('all'); resetForm(); }} class="h-[44px] rounded-[12px] border border-[#d2d8e4] bg-[#f7f7f8] px-6 text-[16px] font-semibold text-[#232b4d]">Cancel</button>
|
|
|
|
|
|
<button onClick={() => void saveDesignation()} class="h-[44px] rounded-[12px] bg-[#030047] px-8 text-[16px] font-semibold text-white">{editingId() ? 'Save Designation' : 'Create Designation'}</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Show>
|
|
|
|
|
|
</section>
|
ui(step-1): match reference layout for dept/designation/employees/roles pages
- All pages: white sticky page header + tab bar with orange underline,
-mx-6 -mt-6 negative margin to stretch headers edge-to-edge
- department: full columns (ID, Name, Description, Created By, etc.),
icon-only action buttons, navy Add Department button
- designation: Designations List / Add Designation tabs, status filter
dropdown, inline create/edit form, full columns with status badge
- employees: View/Add tabs, icon-only action buttons, status badges
- roles/index: clean table with Name+code subtext, Description, actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 04:47:05 +01:00
|
|
|
|
</div>
|
feat(admin): build complete admin panel with UI parity and search/filter
- Implement all admin management pages (employees, users, jobs, leads, orders, companies, customers, candidates, approval, invoices, reviews, support, KB, pricing, coupons, credits, discounts, tax, reports, ledger)
- Implement 9 professional vertical pages (developers, designers, tutors, video editors, photographers, makeup artists, graphic designers, social media managers, fitness trainers)
- Implement internal/external dashboard and role management with builder UI
- Fix tab styling: replace inline border-bottom styles with admin-tab CSS class across 8+ pages
- Add search/filter functionality to invoice and review pages
- Add toggle status (activate/deactivate) to employees page with PATCH /api/admin/employees/{id}
- Align UI styling with NextJS admin panel for visual parity
- Add stat cards to approval page showing counts by status
- Implement graceful empty states for all list views
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-19 13:04:10 +01:00
|
|
|
|
</AdminShell>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|