'use client' import { useEffect, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import Link from 'next/link' import { ADMIN_API_BASE } from '@/lib/api' interface CompanyDetail { id: string name: string slug: string email: string phone: string | null status: string subscriptionPaymentRef: string | null createdAt: string subscription: { plan: string billingPeriod: string status: string currency: string trialStartAt: string | null trialEndAt: string | null currentPeriodStart: string | null currentPeriodEnd: string | null cancelledAt: string | null cancelAtPeriodEnd: boolean } | null brand: { displayName: string tagline: string | null subdomain: string customDomain: string | null publicEmail: string | null publicPhone: string | null publicAddress: string | null publicCity: string | null publicCountry: string | null websiteUrl: string | null whatsappNumber: string | null defaultLocale: string defaultCurrency: string isListedOnMarketplace: boolean } | null contractSettings: { legalName: string | null registrationNumber: string | null taxId: string | null terms: string fuelPolicyType: string lateFeePerHour: number | null taxRate: number | null signatureRequired: boolean showTax: boolean } | null accountingSettings: { reportingPeriod: string fiscalYearStart: number currency: string accountantEmail: string | null accountantName: string | null autoSendReport: boolean reportFormat: string } | null employees?: Array<{ id: string }> vehicles?: Array<{ id: string }> customers?: Array<{ id: string }> reservations?: Array<{ id: string }> _count?: { employees?: number; vehicles?: number; customers?: number; reservations?: number } } interface AuditLog { id: string action: string resource: string resourceId: string | null createdAt: string adminUser: { email: string } | null } interface FormState { company: { name: string slug: string email: string phone: string subscriptionPaymentRef: string } subscription: { plan: string billingPeriod: string status: string currency: string trialStartAt: string trialEndAt: string currentPeriodStart: string currentPeriodEnd: string cancelledAt: string cancelAtPeriodEnd: boolean } brand: { displayName: string tagline: string subdomain: string customDomain: string publicEmail: string publicPhone: string publicAddress: string publicCity: string publicCountry: string websiteUrl: string whatsappNumber: string defaultLocale: string defaultCurrency: string isListedOnMarketplace: boolean } contractSettings: { legalName: string registrationNumber: string taxId: string terms: string fuelPolicyType: string lateFeePerHour: string taxRate: string signatureRequired: boolean showTax: boolean } accountingSettings: { reportingPeriod: string fiscalYearStart: string currency: string accountantEmail: string accountantName: string autoSendReport: boolean reportFormat: string } } const STATUS_COLORS: Record = { ACTIVE: 'text-emerald-400', TRIALING: 'text-sky-400', SUSPENDED: 'text-red-400', PENDING: 'text-orange-400', CANCELLED: 'text-zinc-400', PAST_DUE: 'text-orange-400', } const INPUT_CLASS = 'mt-1 w-full rounded-xl border border-zinc-700 bg-zinc-900 px-3 py-2 text-sm text-zinc-100 outline-none focus:border-emerald-500' const LABEL_CLASS = 'text-xs font-medium uppercase tracking-wide text-zinc-500' function getToken() { return localStorage.getItem('admin_token') ?? '' } function toDateInput(value: string | null | undefined) { return value ? new Date(value).toISOString().slice(0, 10) : '' } function toNullable(value: string) { const trimmed = value.trim() return trimmed ? trimmed : null } function createFormState(company: CompanyDetail): FormState { return { company: { name: company.name ?? '', slug: company.slug ?? '', email: company.email ?? '', phone: company.phone ?? '', subscriptionPaymentRef: company.subscriptionPaymentRef ?? '', }, subscription: { plan: company.subscription?.plan ?? 'STARTER', billingPeriod: company.subscription?.billingPeriod ?? 'MONTHLY', status: company.subscription?.status ?? 'TRIALING', currency: company.subscription?.currency ?? 'MAD', trialStartAt: toDateInput(company.subscription?.trialStartAt), trialEndAt: toDateInput(company.subscription?.trialEndAt), currentPeriodStart: toDateInput(company.subscription?.currentPeriodStart), currentPeriodEnd: toDateInput(company.subscription?.currentPeriodEnd), cancelledAt: toDateInput(company.subscription?.cancelledAt), cancelAtPeriodEnd: company.subscription?.cancelAtPeriodEnd ?? false, }, brand: { displayName: company.brand?.displayName ?? company.name ?? '', tagline: company.brand?.tagline ?? '', subdomain: company.brand?.subdomain ?? company.slug ?? '', customDomain: company.brand?.customDomain ?? '', publicEmail: company.brand?.publicEmail ?? '', publicPhone: company.brand?.publicPhone ?? '', publicAddress: company.brand?.publicAddress ?? '', publicCity: company.brand?.publicCity ?? '', publicCountry: company.brand?.publicCountry ?? '', websiteUrl: company.brand?.websiteUrl ?? '', whatsappNumber: company.brand?.whatsappNumber ?? '', defaultLocale: company.brand?.defaultLocale ?? 'en', defaultCurrency: company.brand?.defaultCurrency ?? 'MAD', isListedOnMarketplace: company.brand?.isListedOnMarketplace ?? true, }, contractSettings: { legalName: company.contractSettings?.legalName ?? '', registrationNumber: company.contractSettings?.registrationNumber ?? '', taxId: company.contractSettings?.taxId ?? '', terms: company.contractSettings?.terms ?? '', fuelPolicyType: company.contractSettings?.fuelPolicyType ?? 'FULL_TO_FULL', lateFeePerHour: company.contractSettings?.lateFeePerHour?.toString() ?? '', taxRate: company.contractSettings?.taxRate?.toString() ?? '', signatureRequired: company.contractSettings?.signatureRequired ?? true, showTax: company.contractSettings?.showTax ?? false, }, accountingSettings: { reportingPeriod: company.accountingSettings?.reportingPeriod ?? 'MONTHLY', fiscalYearStart: company.accountingSettings?.fiscalYearStart?.toString() ?? '1', currency: company.accountingSettings?.currency ?? 'MAD', accountantEmail: company.accountingSettings?.accountantEmail ?? '', accountantName: company.accountingSettings?.accountantName ?? '', autoSendReport: company.accountingSettings?.autoSendReport ?? false, reportFormat: company.accountingSettings?.reportFormat ?? 'PDF', }, } } export default function AdminCompanyDetailPage() { const { id } = useParams<{ id: string }>() const router = useRouter() const [company, setCompany] = useState(null) const [form, setForm] = useState(null) const [auditLogs, setAuditLogs] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [actioning, setActioning] = useState(false) const [saving, setSaving] = useState(false) const [savedMessage, setSavedMessage] = useState(null) const [deleteConfirm, setDeleteConfirm] = useState(false) async function fetchData() { const headers = { Authorization: `Bearer ${getToken()}` } try { const [cRes, aRes] = await Promise.all([ fetch(`${ADMIN_API_BASE}/admin/companies/${id}`, { headers, cache: 'no-store' }), fetch(`${ADMIN_API_BASE}/admin/audit-logs?entityId=${id}&pageSize=10`, { headers, cache: 'no-store' }), ]) const cJson = await cRes.json() const aJson = await aRes.json() if (!cRes.ok) throw new Error(cJson?.message ?? 'Not found') setCompany(cJson.data) setForm(createFormState(cJson.data)) setAuditLogs(aJson.data ?? []) setError(null) } catch (err: any) { setError(err.message) } finally { setLoading(false) } } useEffect(() => { fetchData() }, [id]) function updateSection(section: K, patch: Partial) { setForm((current) => current ? { ...current, [section]: { ...current[section], ...patch } } : current) } async function saveChanges() { if (!form) return setSaving(true) setSavedMessage(null) setError(null) try { const res = await fetch(`${ADMIN_API_BASE}/admin/companies/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getToken()}` }, body: JSON.stringify({ company: { name: form.company.name, slug: form.company.slug, email: form.company.email, phone: toNullable(form.company.phone), subscriptionPaymentRef: toNullable(form.company.subscriptionPaymentRef), }, subscription: { plan: form.subscription.plan, billingPeriod: form.subscription.billingPeriod, status: form.subscription.status, currency: form.subscription.currency, trialStartAt: toNullable(form.subscription.trialStartAt), trialEndAt: toNullable(form.subscription.trialEndAt), currentPeriodStart: toNullable(form.subscription.currentPeriodStart), currentPeriodEnd: toNullable(form.subscription.currentPeriodEnd), cancelledAt: toNullable(form.subscription.cancelledAt), cancelAtPeriodEnd: form.subscription.cancelAtPeriodEnd, }, brand: { displayName: form.brand.displayName, tagline: toNullable(form.brand.tagline), subdomain: form.brand.subdomain, customDomain: toNullable(form.brand.customDomain), publicEmail: toNullable(form.brand.publicEmail), publicPhone: toNullable(form.brand.publicPhone), publicAddress: toNullable(form.brand.publicAddress), publicCity: toNullable(form.brand.publicCity), publicCountry: toNullable(form.brand.publicCountry), websiteUrl: toNullable(form.brand.websiteUrl), whatsappNumber: toNullable(form.brand.whatsappNumber), defaultLocale: form.brand.defaultLocale, defaultCurrency: form.brand.defaultCurrency, isListedOnMarketplace: form.brand.isListedOnMarketplace, }, contractSettings: { legalName: toNullable(form.contractSettings.legalName), registrationNumber: toNullable(form.contractSettings.registrationNumber), taxId: toNullable(form.contractSettings.taxId), terms: form.contractSettings.terms, fuelPolicyType: form.contractSettings.fuelPolicyType, lateFeePerHour: form.contractSettings.lateFeePerHour ? Number(form.contractSettings.lateFeePerHour) : null, taxRate: form.contractSettings.taxRate ? Number(form.contractSettings.taxRate) : null, signatureRequired: form.contractSettings.signatureRequired, showTax: form.contractSettings.showTax, }, accountingSettings: { reportingPeriod: form.accountingSettings.reportingPeriod, fiscalYearStart: Number(form.accountingSettings.fiscalYearStart || 1), currency: form.accountingSettings.currency, accountantEmail: toNullable(form.accountingSettings.accountantEmail), accountantName: toNullable(form.accountingSettings.accountantName), autoSendReport: form.accountingSettings.autoSendReport, reportFormat: form.accountingSettings.reportFormat, }, }), }) const json = await res.json() if (!res.ok) throw new Error(json?.message ?? 'Save failed') setCompany(json.data) setForm(createFormState(json.data)) setSavedMessage('Company settings updated.') await fetchData() } catch (err: any) { setError(err.message) } finally { setSaving(false) } } async function changeStatus(status: string) { setActioning(true) setError(null) try { const res = await fetch(`${ADMIN_API_BASE}/admin/companies/${id}/status`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getToken()}` }, body: JSON.stringify({ status }), }) const json = await res.json() if (!res.ok) throw new Error(json?.message ?? 'Action failed') await fetchData() } catch (err: any) { setError(err.message) } finally { setActioning(false) } } async function deleteCompany() { setActioning(true) try { const res = await fetch(`${ADMIN_API_BASE}/admin/companies/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${getToken()}` }, }) const json = await res.json() if (!res.ok) throw new Error(json?.message ?? 'Delete failed') router.push('/dashboard/companies') } catch (err: any) { setError(err.message) setActioning(false) } } if (loading) return
Loading…
if (error && !company) return
{error}
if (!company || !form) return null const counts = { employees: company._count?.employees ?? company.employees?.length ?? 0, vehicles: company._count?.vehicles ?? company.vehicles?.length ?? 0, customers: company._count?.customers ?? company.customers?.length ?? 0, reservations: company._count?.reservations ?? company.reservations?.length ?? 0, } return (
← Companies

{company.name}

{company.slug}

{company.status}
{error &&
{error}
} {savedMessage &&
{savedMessage}
}
{[ { label: 'Employees', value: counts.employees }, { label: 'Vehicles', value: counts.vehicles }, { label: 'Customers', value: counts.customers }, { label: 'Reservations', value: counts.reservations }, ].map((kpi) => (

{kpi.label}

{kpi.value}

))}

Company

Subscription

Brand and public profile

Operations and finance