ui(step-3): apply reference layout to 14 more management pages

- pricing, credit, coupon, discount, tax, order, invoice: white header,
  data-table/table-card, navy buttons, inline styles removed
- review, leads, jobs, notifications, support, report, ledger: same
  pattern + orange tab underlines where applicable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar 2026-03-24 05:20:55 +01:00
parent 0bb59b99ef
commit 5cfa4b89be
14 changed files with 1906 additions and 1859 deletions

View file

@ -142,35 +142,35 @@ export default function CouponPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Coupon Management</h1>
<p class="mt-1 text-sm text-gray-500">Reusable coupon codes for package checkout</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Coupon Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Reusable coupon codes for package checkout</p>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={`admin-tab${activeTab() === 'list' ? ' active' : ''}`}
class={activeTab() === 'list' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('list')}
>
Coupons
</button>
<button
type="button"
class={`admin-tab${activeTab() === 'create' ? ' active' : ''}`}
class={activeTab() === 'create' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => { resetForm(); setActiveTab('create'); }}
>
{form().id ? 'Edit Coupon' : 'Create Coupon'}
</button>
</div>
<div class="flex-1 p-6">
<Show when={activeTab() === 'list'}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Code</th>
@ -195,12 +195,12 @@ export default function CouponPage() {
<Show when={!coupons.loading && !coupons.error && (coupons()?.length ?? 0) > 0}>
<For each={coupons()}>
{(item) => (
<tr>
<td style="font-weight:600;color:#0f172a;font-family:monospace">{item.code}</td>
<td style="color:#475569">{item.title || '—'}</td>
<td style="color:#475569">{item.type}</td>
<td style="color:#475569">{item.type === 'PERCENT' ? `${item.value}%` : `${item.value}`}</td>
<td style="color:#475569">{item.usage_limit != null ? item.usage_limit : '—'}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900" style="font-family:monospace">{item.code}</td>
<td class="text-slate-500">{item.title || '—'}</td>
<td class="text-slate-500">{item.type}</td>
<td class="text-slate-500">{item.type === 'PERCENT' ? `${item.value}%` : `${item.value}`}</td>
<td class="text-slate-500">{item.usage_limit != null ? item.usage_limit : '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${item.is_active ? 'active' : ''}`}>
{item.is_active ? 'Active' : 'Inactive'}
@ -225,7 +225,7 @@ export default function CouponPage() {
</tbody>
</table>
</div>
</section>
</div>
</Show>
<Show when={activeTab() === 'create'}>
@ -320,7 +320,7 @@ export default function CouponPage() {
</div>
</div>
<div class="actions">
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={saving()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={saving()}>
{saving() ? 'Saving...' : (form().id ? 'Update Coupon' : 'Save Coupon')}
</button>
<Show when={form().id}>
@ -330,6 +330,8 @@ export default function CouponPage() {
</form>
</section>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -136,20 +136,19 @@ export default function CreditPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Credit Management</h1>
<p class="mt-1 text-sm text-gray-500">Audit TraceCoin balances and adjust credits</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Credit Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Audit TraceCoin balances and adjust credits</p>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<For each={tabs}>
{(tab) => (
<button
type="button"
class={`admin-tab${activeTab() === tab.key ? ' active' : ''}`}
class={activeTab() === tab.key ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab(tab.key)}
>
{tab.label}
@ -158,6 +157,7 @@ export default function CreditPage() {
</For>
</div>
<div class="flex-1 p-6">
{/* Balance & Ledger Tab */}
<Show when={activeTab() === 'ledger'}>
<div style="display:flex;flex-direction:column;gap:24px">
@ -170,10 +170,11 @@ export default function CreditPage() {
value={userId()}
onInput={(e) => setUserId(e.currentTarget.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
style="flex:1;padding:8px 12px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px"
class="rounded-lg border border-gray-200 px-3 py-2 text-sm"
style="flex:1"
/>
<button
class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors"
class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors"
onClick={handleSearch}
disabled={searchLoading()}
>
@ -193,14 +194,14 @@ export default function CreditPage() {
<p style="font-size:12px;color:#bfdbfe;margin:8px 0 0">User: {searchedUserId()}</p>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:20px;overflow:hidden">
<div class="table-card" style="overflow:hidden">
<h3 style="margin:0 0 16px;font-size:15px;font-weight:700;color:#0f172a">TraceCoin Ledger</h3>
<Show when={ledger().length === 0}>
<p style="text-align:center;padding:32px;color:#94a3b8;font-style:italic">No transactions found for this account.</p>
</Show>
<Show when={ledger().length > 0}>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Type</th>
@ -213,7 +214,7 @@ export default function CreditPage() {
<tbody>
<For each={ledger()}>
{(entry) => (
<tr>
<tr class="hover:bg-slate-50">
<td>
<span
style={`display:inline-block;padding:2px 8px;border-radius:999px;font-size:10px;font-weight:700;text-transform:uppercase;background:${entry.transactionType === 'ADD' ? '#dcfce7' : '#fee2e2'};color:${entry.transactionType === 'ADD' ? '#15803d' : '#b91c1c'}`}
@ -221,16 +222,16 @@ export default function CreditPage() {
{entry.transactionType}
</span>
</td>
<td style={`font-weight:700;${entry.transactionType === 'ADD' ? 'color:#16a34a' : 'color:#dc2626'}`}>
<td class={entry.transactionType === 'ADD' ? 'font-semibold text-slate-900' : 'font-semibold text-slate-900'} style={entry.transactionType === 'ADD' ? 'color:#16a34a' : 'color:#dc2626'}>
{entry.transactionType === 'ADD' ? '+' : '-'}{entry.amount}
</td>
<td style="font-size:12px;color:#64748b;font-family:monospace">
<td class="text-slate-500" style="font-size:12px;font-family:monospace">
{entry.referenceId ? entry.referenceId.substring(0, 18) : '—'}
</td>
<td style="font-size:12px;color:#94a3b8">
<td class="text-slate-500" style="font-size:12px">
{entry.expiresAt ? new Date(entry.expiresAt).toLocaleDateString() : '—'}
</td>
<td style="font-size:12px;color:#94a3b8">
<td class="text-slate-500" style="font-size:12px">
{entry.createdAt ? new Date(entry.createdAt).toLocaleString() : '—'}
</td>
</tr>
@ -240,7 +241,7 @@ export default function CreditPage() {
</table>
</div>
</Show>
</section>
</div>
</div>
</Show>
</div>
@ -316,7 +317,7 @@ export default function CreditPage() {
/>
</div>
<div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={adjLoading()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={adjLoading()}>
{adjLoading() ? 'Adjusting...' : 'Apply Adjustment'}
</button>
</div>
@ -356,7 +357,7 @@ export default function CreditPage() {
</div>
</div>
<div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={reconLoading()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={reconLoading()}>
{reconLoading() ? 'Running...' : 'Run Reconciliation'}
</button>
</div>
@ -368,9 +369,9 @@ export default function CreditPage() {
<p style="color:#16a34a;font-weight:600;font-size:14px">No discrepancies found.</p>
</Show>
<Show when={(reconResults()?.length ?? 0) > 0}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>User ID</th>
@ -382,10 +383,10 @@ export default function CreditPage() {
<tbody>
<For each={reconResults()!}>
{(row) => (
<tr>
<td style="font-family:monospace;font-size:13px">{row.userId}</td>
<td>{row.expectedBalance}</td>
<td>{row.actualBalance}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900" style="font-family:monospace;font-size:13px">{row.userId}</td>
<td class="text-slate-500">{row.expectedBalance}</td>
<td class="text-slate-500">{row.actualBalance}</td>
<td style={row.discrepancy !== 0 ? 'color:#dc2626;font-weight:700' : 'color:#16a34a'}>
{row.discrepancy}
</td>
@ -395,11 +396,13 @@ export default function CreditPage() {
</tbody>
</table>
</div>
</section>
</div>
</Show>
</Show>
</div>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -146,35 +146,35 @@ export default function DiscountPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Discount Management</h1>
<p class="mt-1 text-sm text-gray-500">Automatic discounts applied before coupons</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Discount Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Automatic discounts applied before coupons</p>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={`admin-tab${activeTab() === 'list' ? ' active' : ''}`}
class={activeTab() === 'list' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('list')}
>
Discounts
</button>
<button
type="button"
class={`admin-tab${activeTab() === 'create' ? ' active' : ''}`}
class={activeTab() === 'create' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => { resetForm(); setActiveTab('create'); }}
>
{form().id ? 'Edit Discount' : 'Create Discount'}
</button>
</div>
<div class="flex-1 p-6">
<Show when={activeTab() === 'list'}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Title</th>
@ -199,12 +199,12 @@ export default function DiscountPage() {
<Show when={!discounts.loading && !discounts.error && (discounts()?.length ?? 0) > 0}>
<For each={discounts()}>
{(item) => (
<tr>
<td style="font-weight:600;color:#0f172a">{item.title || '—'}</td>
<td style="color:#475569">{item.scope}</td>
<td style="color:#475569">{getTarget(item)}</td>
<td style="color:#475569">{item.type}</td>
<td style="color:#475569">{item.type === 'PERCENT' ? `${item.value}%` : `${item.value}`}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{item.title || '—'}</td>
<td class="text-slate-500">{item.scope}</td>
<td class="text-slate-500">{getTarget(item)}</td>
<td class="text-slate-500">{item.type}</td>
<td class="text-slate-500">{item.type === 'PERCENT' ? `${item.value}%` : `${item.value}`}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${item.is_active ? 'active' : ''}`}>
{item.is_active ? 'Active' : 'Inactive'}
@ -228,7 +228,7 @@ export default function DiscountPage() {
</tbody>
</table>
</div>
</section>
</div>
</Show>
<Show when={activeTab() === 'create'}>
@ -324,7 +324,7 @@ export default function DiscountPage() {
</div>
</div>
<div class="actions">
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={saving()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={saving()}>
{saving() ? 'Saving...' : (form().id ? 'Update Discount' : 'Save Discount')}
</button>
<Show when={form().id}>
@ -334,6 +334,8 @@ export default function DiscountPage() {
</form>
</section>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -32,26 +32,27 @@ export default function InvoicePage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Invoice Management</h1>
<p class="mt-1 text-sm text-gray-500">View and download all platform invoices.</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Invoice Management</h1>
<p class="text-sm text-gray-500 mt-0.5">View and download all platform invoices.</p>
</div>
<div class="flex-1 p-6">
<div style="margin-bottom:16px">
<input
type="text"
placeholder="Search invoices by number, user, package, or status..."
value={search()}
onInput={(e) => setSearch(e.currentTarget.value)}
style="padding:8px 12px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;min-width:320px"
class="rounded-lg border border-gray-200 px-3 py-2 text-sm"
style="min-width:320px"
/>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Invoice #</th>
@ -76,18 +77,18 @@ export default function InvoicePage() {
</Show>
<Show when={!invoices.loading && !invoices.error && filteredInvoices().length > 0}>
{filteredInvoices().map((item) => (
<tr>
<td style="font-weight:600;color:#0f172a;font-family:monospace">{item.invoice_number || item.id}</td>
<td style="color:#475569">{item.user_id || '—'}</td>
<td style="color:#475569">{item.package_name || '—'}</td>
<td style="color:#475569">{item.total != null ? (item.total / 100).toFixed(2) : '—'}</td>
<td style="color:#475569">{item.tax != null ? (item.tax / 100).toFixed(2) : '—'}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900" style="font-family:monospace">{item.invoice_number || item.id}</td>
<td class="text-slate-500">{item.user_id || '—'}</td>
<td class="text-slate-500">{item.package_name || '—'}</td>
<td class="text-slate-500">{item.total != null ? (item.total / 100).toFixed(2) : '—'}</td>
<td class="text-slate-500">{item.tax != null ? (item.tax / 100).toFixed(2) : '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${item.status === 'PAID' || item.status === 'ISSUED' ? 'active' : ''}`}>
{item.status || '—'}
</span>
</td>
<td style="color:#475569">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
<td class="text-slate-500">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
<td>
<div class="flex items-center justify-end gap-1">
<Show when={item.download_url || item.pdf_url}>
@ -112,7 +113,9 @@ export default function InvoicePage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}

View file

@ -63,13 +63,13 @@ export default function JobsPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Jobs Management</h1>
<p class="mt-1 text-sm text-gray-500">Review live company job postings</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Jobs Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Review live company job postings</p>
</div>
<div class="flex-1 p-6">
<div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap">
<input
type="text"
@ -87,9 +87,9 @@ export default function JobsPage() {
</select>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Title</th>
@ -114,25 +114,25 @@ export default function JobsPage() {
<Show when={!jobs.loading && !jobs.error && filtered().length > 0}>
<For each={filtered()}>
{(job) => (
<tr>
<tr class="hover:bg-slate-50">
<td>
<div style="font-weight:600;color:#0f172a">{job.title || '—'}</div>
<div class="font-semibold text-slate-900">{job.title || '—'}</div>
<Show when={job.description}>
<div style="font-size:12px;color:#64748b;margin-top:2px;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
{job.description}
</div>
</Show>
</td>
<td style="color:#475569">
<td class="text-slate-500">
{job.required_skills?.join(', ') || job.experience_level || '—'}
</td>
<td style="color:#475569">{job.client_name || job.company_name || '—'}</td>
<td style="color:#475569">
<td class="text-slate-500">{job.client_name || job.company_name || '—'}</td>
<td class="text-slate-500">
{job.hourly_rate_min != null
? `${job.hourly_rate_min}–₹${job.hourly_rate_max ?? job.hourly_rate_min}/hr`
: '—'}
</td>
<td style="color:#475569">{job.location || '—'}</td>
<td class="text-slate-500">{job.location || '—'}</td>
<td>
<span class={statusChipClass(job.status)}>{job.status || '—'}</span>
</td>
@ -151,7 +151,9 @@ export default function JobsPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}

View file

@ -49,13 +49,13 @@ export default function LeadsPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Leads Management</h1>
<p class="mt-1 text-sm text-gray-500">View all requirements and lead requests from customers.</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Leads Management</h1>
<p class="text-sm text-gray-500 mt-0.5">View all requirements and lead requests from customers.</p>
</div>
<div class="flex-1 p-6">
{/* Filters */}
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px;align-items:center;">
<input
@ -92,9 +92,9 @@ export default function LeadsPage() {
</Show>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Title</th>
@ -118,20 +118,20 @@ export default function LeadsPage() {
<Show when={!leads.loading && !leads.error && filtered().length > 0}>
<For each={filtered()}>
{(item) => (
<tr>
<tr class="hover:bg-slate-50">
<td>
<div style="font-weight:600;color:#0f172a">{item.title || '—'}</div>
<div class="font-semibold text-slate-900">{item.title || '—'}</div>
<Show when={item.description}>
<div style="font-size:12px;color:#64748b;margin-top:2px">
{String(item.description).slice(0, 60)}{String(item.description).length > 60 ? '…' : ''}
</div>
</Show>
</td>
<td style="color:#475569">{item.profession || item.role || '—'}</td>
<td style="color:#475569">
<td class="text-slate-500">{item.profession || item.role || '—'}</td>
<td class="text-slate-500">
{item.budget_range || (item.budget_min != null ? `${item.budget_min}–₹${item.budget_max}` : '—')}
</td>
<td style="color:#475569">{item.location || '—'}</td>
<td class="text-slate-500">{item.location || '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${(item.status === 'ACTIVE' || item.status === 'OPEN') ? 'active' : ''}`}>
{item.status || '—'}
@ -149,7 +149,9 @@ export default function LeadsPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}

View file

@ -56,16 +56,16 @@ export default function LedgerPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Ledger Management</h1>
<p class="mt-1 text-sm text-gray-500">Platform financial ledger</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Ledger Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Platform financial ledger</p>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="flex-1 p-6">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Type</th>
@ -92,18 +92,18 @@ export default function LedgerPage() {
{(item) => {
const entryType = item.entry_type || item.type || '—';
return (
<tr>
<tr class="hover:bg-slate-50">
<td>
<span style={`${typeBadgeStyle(entryType)};padding:2px 10px;border-radius:999px;font-size:12px;font-weight:600;border:1px solid;display:inline-block`}>
{entryType}
</span>
</td>
<td style="color:#475569;font-size:12px;font-family:monospace">{item.order_id || '—'}</td>
<td style="color:#475569;font-size:12px;font-family:monospace">{item.invoice_id || '—'}</td>
<td style="color:#475569;font-size:12px;font-family:monospace">{item.user_id || '—'}</td>
<td style="font-weight:600;color:#0f172a">{formatAmount(item)}</td>
<td style="color:#475569">{item.note || '—'}</td>
<td style="color:#475569">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
<td class="text-slate-500" style="font-size:12px;font-family:monospace">{item.order_id || '—'}</td>
<td class="text-slate-500" style="font-size:12px;font-family:monospace">{item.invoice_id || '—'}</td>
<td class="text-slate-500" style="font-size:12px;font-family:monospace">{item.user_id || '—'}</td>
<td class="font-semibold text-slate-900">{formatAmount(item)}</td>
<td class="text-slate-500">{item.note || '—'}</td>
<td class="text-slate-500">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
</tr>
);
}}
@ -112,7 +112,9 @@ export default function LedgerPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}

View file

@ -83,10 +83,11 @@ export default function NotificationsPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900">Notifications</h1>
<p class="mt-1 text-sm text-gray-500">Approval outcomes and action-required updates</p>
<h1 class="text-xl font-semibold text-gray-900">Notifications</h1>
<p class="text-sm text-gray-500 mt-0.5">Approval outcomes and action-required updates</p>
</div>
<button
class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
@ -98,26 +99,31 @@ export default function NotificationsPage() {
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={`admin-tab${activeTab() === 'all' ? ' active' : ''}`}
class={activeTab() === 'all'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('all')}
>
All
</button>
<button
type="button"
class={`admin-tab${activeTab() === 'unread' ? ' active' : ''}`}
class={activeTab() === 'unread'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('unread')}
>
Unread ({unreadCount()})
</button>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div class="flex-1 p-6">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Title</th>
@ -136,15 +142,15 @@ export default function NotificationsPage() {
</Show>
<For each={visibleRows()}>
{(item) => (
<tr style={item.read_at === null ? 'background:#eff6ff' : ''}>
<td style="font-weight:600;color:#0f172a;min-width:160px">{item.title}</td>
<td style="font-size:13px;color:#475569;max-width:320px">{truncate(item.message, 80)}</td>
<tr class="hover:bg-slate-50" style={item.read_at === null ? 'background:#eff6ff' : ''}>
<td class="font-semibold text-slate-900" style="min-width:160px">{item.title}</td>
<td class="text-slate-500" style="max-width:320px">{truncate(item.message, 80)}</td>
<td>
<span style={`${BADGE_STYLE};${eventTypeBadgeStyle(item.event_type)}`}>
{item.event_type}
</span>
</td>
<td style="font-size:12px;color:#64748b;white-space:nowrap">
<td class="text-slate-500" style="font-size:12px;white-space:nowrap">
{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}
</td>
<td>
@ -166,7 +172,7 @@ export default function NotificationsPage() {
</tbody>
</table>
</div>
</section>
</div>
<Show when={loading() && rows().length > 0}>
<p style="text-align:center;padding:12px;font-size:13px;color:#64748b">Loading...</p>
@ -182,6 +188,8 @@ export default function NotificationsPage() {
</button>
</div>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -70,26 +70,27 @@ export default function OrderPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Order Management</h1>
<p class="mt-1 text-sm text-gray-500">TraceCoin package purchase orders</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Order Management</h1>
<p class="text-sm text-gray-500 mt-0.5">TraceCoin package purchase orders</p>
</div>
<div class="flex-1 p-6">
<div style="margin-bottom:16px">
<input
type="text"
placeholder="Search by order number, user email, or status..."
value={search()}
onInput={(e) => setSearch(e.currentTarget.value)}
style="width:100%;max-width:420px;padding:8px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px"
class="rounded-lg border border-gray-200 px-3 py-2 text-sm"
style="width:100%;max-width:420px"
/>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Order #</th>
@ -115,13 +116,13 @@ export default function OrderPage() {
<Show when={!orders.loading && !orders.error && filtered().length > 0}>
<For each={filtered()}>
{(item) => (
<tr>
<td style="font-weight:600;color:#0f172a;font-family:monospace">{item.order_number || item.id}</td>
<td style="color:#475569">{item.user_name || item.user_email || '—'}</td>
<td style="color:#475569">{item.package_name || '—'}</td>
<td style="color:#475569">{item.tracecoin_amount ?? '—'}</td>
<td style="color:#475569">{item.coupon_code || '—'}</td>
<td style="color:#475569">{formatAmount(item)}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900" style="font-family:monospace">{item.order_number || item.id}</td>
<td class="text-slate-500">{item.user_name || item.user_email || '—'}</td>
<td class="text-slate-500">{item.package_name || '—'}</td>
<td class="text-slate-500">{item.tracecoin_amount ?? '—'}</td>
<td class="text-slate-500">{item.coupon_code || '—'}</td>
<td class="text-slate-500">{formatAmount(item)}</td>
<td>
<span
class="inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600"
@ -130,7 +131,7 @@ export default function OrderPage() {
{item.status || '—'}
</span>
</td>
<td style="color:#475569">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
<td class="text-slate-500">{item.created_at ? new Date(item.created_at).toLocaleString() : '—'}</td>
</tr>
)}
</For>
@ -138,7 +139,9 @@ export default function OrderPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}

View file

@ -150,36 +150,36 @@ export default function PricingPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Pricing Management</h1>
<p class="mt-1 text-sm text-gray-500">Create and manage TraceCoin packages</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Pricing Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Create and manage TraceCoin packages</p>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={`admin-tab${view() === 'packages' ? ' active' : ''}`}
class={view() === 'packages' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setView('packages')}
>
Packages
</button>
<button
type="button"
class={`admin-tab${view() === 'create' ? ' active' : ''}`}
class={view() === 'create' ? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium' : 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setView('create')}
>
Create Package
</button>
</div>
<div class="flex-1 p-6">
{/* Packages list tab */}
<Show when={view() === 'packages'}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Name</th>
@ -205,12 +205,12 @@ export default function PricingPage() {
<For each={packages()}>
{(pkg) => (
<>
<tr>
<td style="font-weight:600;color:#0f172a">{pkg.name}</td>
<td style="color:#475569">{pkg.role}</td>
<td style="color:#475569">{pkg.tracecoin_amount}</td>
<td style="color:#475569">{(pkg.price_inr / 100).toFixed(2)}</td>
<td style="color:#475569">{pkg.bonus_percentage != null ? `${pkg.bonus_percentage}%` : '—'}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{pkg.name}</td>
<td class="text-slate-500">{pkg.role}</td>
<td class="text-slate-500">{pkg.tracecoin_amount}</td>
<td class="text-slate-500">{(pkg.price_inr / 100).toFixed(2)}</td>
<td class="text-slate-500">{pkg.bonus_percentage != null ? `${pkg.bonus_percentage}%` : '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${pkg.is_active ? 'active' : ''}`}>
{pkg.is_active ? 'Active' : 'Inactive'}
@ -220,7 +220,7 @@ export default function PricingPage() {
<div class="flex items-center justify-end gap-1">
<button class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" onClick={() => startEdit(pkg)}>Edit</button>
<button
class={pkg.is_active ? 'inline-flex items-center rounded-lg border border-red-200 bg-red-50 px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-100 transition-colors' : 'inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors'}
class={pkg.is_active ? 'inline-flex items-center rounded-lg border border-red-200 bg-red-50 px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-100 transition-colors' : 'inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors'}
disabled={togglingId() === pkg.id}
onClick={() => toggleActive(pkg)}
>
@ -264,7 +264,7 @@ export default function PricingPage() {
/>
</div>
<div style="display:flex;gap:8px">
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" disabled={editSaving()} onClick={() => saveEdit(pkg.id)}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" disabled={editSaving()} onClick={() => saveEdit(pkg.id)}>
{editSaving() ? 'Saving...' : 'Save'}
</button>
<button class="inline-flex items-center rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors" onClick={cancelEdit}>Cancel</button>
@ -280,7 +280,7 @@ export default function PricingPage() {
</tbody>
</table>
</div>
</section>
</div>
</Show>
{/* Create Package tab */}
@ -349,13 +349,15 @@ export default function PricingPage() {
/>
</div>
<div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={cSaving()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={cSaving()}>
{cSaving() ? 'Creating...' : 'Create Package'}
</button>
</div>
</form>
</section>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -46,13 +46,13 @@ export default function ReportPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Report Management</h1>
<p class="mt-1 text-sm text-gray-500">View platform analytics and generate reports.</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Report Management</h1>
<p class="text-sm text-gray-500 mt-0.5">View platform analytics and generate reports.</p>
</div>
<div class="flex-1 p-6">
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="margin-bottom:16px">
<h2 style="margin:0 0 16px;font-size:16px;font-weight:700">Date Range</h2>
<form onSubmit={handleLoad} style="display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap">
@ -76,7 +76,7 @@ export default function ReportPage() {
style="padding:8px 10px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px"
/>
</div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={loading()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={loading()}>
{loading() ? 'Loading...' : 'Load Report'}
</button>
</form>
@ -115,6 +115,8 @@ export default function ReportPage() {
</div>
</div>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -120,31 +120,35 @@ export default function ReviewPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Review Management</h1>
<p class="mt-1 text-sm text-gray-500">Moderate platform reviews</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Review Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Moderate platform reviews</p>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={`admin-tab${activeTab() === 'list' ? ' active' : ''}`}
class={activeTab() === 'list'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('list')}
>
Reviews
</button>
<button
type="button"
class={`admin-tab${activeTab() === 'create' ? ' active' : ''}`}
class={activeTab() === 'create'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => { resetForm(); setActiveTab('create'); }}
>
Create Review
</button>
</div>
<div class="flex-1 p-6">
<Show when={activeTab() === 'list'}>
<div style="margin-bottom:16px">
<input
@ -155,9 +159,9 @@ export default function ReviewPage() {
style="padding:8px 12px;border:1px solid #e2e8f0;border-radius:6px;font-size:14px;min-width:320px"
/>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Reviewer</th>
@ -184,17 +188,17 @@ export default function ReviewPage() {
const subjectType = item.subject_type || '—';
const isPublished = item.status === 'PUBLISHED';
return (
<tr>
<td style="font-weight:600;color:#0f172a">{item.reviewer_name || item.reviewer_id || '—'}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{item.reviewer_name || item.reviewer_id || '—'}</td>
<td>
<span style={`${typeBadgeStyle(subjectType)};padding:2px 10px;border-radius:999px;font-size:12px;font-weight:600;border:1px solid;display:inline-block`}>
{subjectType}
</span>
</td>
<td style="color:#475569">
<td class="text-slate-500">
{item.rating != null ? `${item.rating}` : '—'}
</td>
<td style="color:#475569">{item.title || '—'}</td>
<td class="text-slate-500">{item.title || '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${isPublished ? 'active' : ''}`}
style={isPublished ? '' : 'background:#f1f5f9;color:#475569;border-color:#e2e8f0'}
@ -215,7 +219,7 @@ export default function ReviewPage() {
</Show>
<Show when={!isPublished}>
<button
class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors"
class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors"
disabled={toggling() === item.id}
onClick={() => handleUpdateStatus(item, 'PUBLISHED')}
>
@ -232,7 +236,7 @@ export default function ReviewPage() {
</tbody>
</table>
</div>
</section>
</div>
</Show>
<Show when={activeTab() === 'create'}>
@ -306,13 +310,15 @@ export default function ReviewPage() {
/>
</div>
<div class="actions">
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={saving()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={saving()}>
{saving() ? 'Saving...' : 'Save Review'}
</button>
</div>
</form>
</section>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -158,13 +158,35 @@ export default function SupportPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-900">Support Management</h1>
<p class="mt-1 text-sm text-gray-500">Handle platform issues and customer queries</p>
</div>
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4">
<h1 class="text-xl font-semibold text-gray-900">Support Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Handle platform issues and customer queries</p>
</div>
{/* Tabs */}
<div class="bg-white border-b border-gray-200 px-6 flex items-center gap-8 sticky top-0 z-10">
<button
type="button"
class={activeTab() === 'queue'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('queue')}
>
Support Queue
</button>
<button
type="button"
class={activeTab() === 'create'
? 'py-3 border-b-2 border-orange-500 text-orange-600 text-sm font-medium'
: 'py-3 border-b-2 border-transparent text-gray-500 hover:text-gray-700 text-sm font-medium transition-colors'}
onClick={() => setActiveTab('create')}
>
Create Case
</button>
</div>
<div class="flex-1 p-6">
{/* Stats bar */}
<div style="display:flex;gap:12px;margin-bottom:16px">
<For each={statCards}>
@ -177,24 +199,6 @@ export default function SupportPage() {
</For>
</div>
{/* Tabs */}
<div style="display:flex;border-bottom:2px solid #e2e8f0;margin-bottom:24px;gap:0;overflow-x:auto;">
<button
type="button"
class={`admin-tab${activeTab() === 'queue' ? ' active' : ''}`}
onClick={() => setActiveTab('queue')}
>
Support Queue
</button>
<button
type="button"
class={`admin-tab${activeTab() === 'create' ? ' active' : ''}`}
onClick={() => setActiveTab('create')}
>
Create Case
</button>
</div>
{/* Support Queue Tab */}
<Show when={activeTab() === 'queue'}>
<div style="display:flex;flex-direction:column;gap:16px">
@ -211,9 +215,9 @@ export default function SupportPage() {
</select>
</div>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding:0;overflow:hidden">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Issue</th>
@ -238,9 +242,9 @@ export default function SupportPage() {
<Show when={!cases.loading && !cases.error && filteredCases().length > 0}>
<For each={filteredCases()}>
{(item) => (
<tr style="cursor:pointer" onClick={() => {}}>
<tr class="hover:bg-slate-50" style="cursor:pointer" onClick={() => {}}>
<td>
<div style="font-weight:600;color:#0f172a">{item.title}</div>
<div class="font-semibold text-slate-900">{item.title}</div>
<div style="font-size:12px;color:#64748b;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{item.description}</div>
</td>
<td>
@ -256,7 +260,7 @@ export default function SupportPage() {
<div style="font-size:13px">{item.requesterName || '—'}</div>
<div style="font-size:11px;color:#64748b">{item.requesterEmail || ''}</div>
</td>
<td style="font-size:12px;color:#64748b">
<td class="text-slate-500" style="font-size:12px">
{item.updatedAt ? new Date(item.updatedAt).toLocaleString() : '—'}
</td>
<td>
@ -271,7 +275,7 @@ export default function SupportPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</Show>
@ -358,13 +362,15 @@ export default function SupportPage() {
</div>
</div>
<div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={createLoading()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={createLoading()}>
{createLoading() ? 'Creating...' : 'Create Support Case'}
</button>
</div>
</form>
</section>
</Show>
</div>
</div>
</AdminShell>
);
}

View file

@ -64,16 +64,18 @@ export default function TaxPage() {
return (
<AdminShell>
<div class="mb-6 flex items-start justify-between gap-4">
<div class="flex flex-col -mx-6 -mt-6 min-h-full">
<div class="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900">Tax Management</h1>
<p class="mt-1 text-sm text-gray-500">Configure tax rates for platform transactions.</p>
<h1 class="text-xl font-semibold text-gray-900">Tax Management</h1>
<p class="text-sm text-gray-500 mt-0.5">Configure tax rates for platform transactions.</p>
</div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" onClick={() => setShowForm(!showForm())}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" onClick={() => setShowForm(!showForm())}>
{showForm() ? 'Cancel' : 'Add Tax'}
</button>
</div>
<div class="flex-1 p-6">
<Show when={showForm()}>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="margin-bottom:16px">
<h2 style="margin:0 0 16px;font-size:16px;font-weight:700">New Tax</h2>
@ -114,7 +116,7 @@ export default function TaxPage() {
/>
</div>
<div>
<button class="inline-flex items-center rounded-lg bg-[#fd6216] px-4 py-2 text-sm font-semibold text-white hover:bg-orange-600 transition-colors" type="submit" disabled={saving()}>
<button class="inline-flex items-center rounded-lg bg-[#0a1d37] px-4 py-2 text-sm font-semibold text-white hover:bg-[#0f2a4e] transition-colors" type="submit" disabled={saving()}>
{saving() ? 'Saving...' : 'Save Tax'}
</button>
</div>
@ -122,9 +124,9 @@ export default function TaxPage() {
</section>
</Show>
<section class="rounded-xl border border-gray-200 bg-white shadow-sm" style="padding: 0; overflow: hidden;">
<div class="table-card">
<div class="overflow-x-auto">
<table class="w-full text-sm">
<table class="data-table w-full text-sm">
<thead>
<tr>
<th>Name</th>
@ -146,10 +148,10 @@ export default function TaxPage() {
</Show>
<Show when={!taxes.loading && !taxes.error && (taxes()?.length ?? 0) > 0}>
{taxes()!.map((item) => (
<tr>
<td style="font-weight:600;color:#0f172a">{item.name}</td>
<td style="color:#475569">{item.rate}%</td>
<td style="color:#475569">{item.description || '—'}</td>
<tr class="hover:bg-slate-50">
<td class="font-semibold text-slate-900">{item.name}</td>
<td class="text-slate-500">{item.rate}%</td>
<td class="text-slate-500">{item.description || '—'}</td>
<td>
<span class={`inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600 ${item.is_active !== false ? 'active' : ''}`}>
{item.is_active !== false ? 'Active' : 'Inactive'}
@ -173,7 +175,9 @@ export default function TaxPage() {
</tbody>
</table>
</div>
</section>
</div>
</div>
</div>
</AdminShell>
);
}