mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 09:35:44 +00:00
211 lines
6.7 KiB
TypeScript
211 lines
6.7 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { Trash2 } from 'lucide-react';
|
|
import DataTable from '@/components/ui/DataTable';
|
|
import Badge from '@/components/ui/Badge';
|
|
import ActionButton from '@/components/ui/ActionButton';
|
|
import ConfirmDialog from '@/components/ui/ConfirmDialog';
|
|
import { packageInquiriesApi, packagesApi } from '@/lib/api';
|
|
import { formatDateTime, formatCurrency } from '@/lib/utils';
|
|
import { PermissionGuard } from '@/components/layout/PermissionGuard';
|
|
import { PERMS } from '@/lib/permissions';
|
|
import { useWritePermission, useDeletePermission } from '@/lib/use-permission';
|
|
|
|
const STATUSES = ['NEW', 'CONTACTED', 'CONVERTED', 'CLOSED'];
|
|
|
|
const statusVariant: Record<string, string> = {
|
|
NEW: 'info',
|
|
CONTACTED: 'warning',
|
|
CONVERTED: 'success',
|
|
CLOSED: 'default',
|
|
};
|
|
|
|
function PackageInquiriesPageContent() {
|
|
const [filters, setFilters] = useState({ packageId: '', status: '' });
|
|
const [deleteConfirm, setDeleteConfirm] = useState<any>(null);
|
|
const [deleteError, setDeleteError] = useState<string | null>(null);
|
|
const queryClient = useQueryClient();
|
|
|
|
const canEdit = useWritePermission(PERMS.inquiries.edit, PERMS.inquiries.manage);
|
|
const canDelete = useDeletePermission(PERMS.inquiries.delete);
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['package-inquiries', filters],
|
|
queryFn: () => packageInquiriesApi.getAll({ ...filters, pageSize: 50 }),
|
|
});
|
|
|
|
const { data: packagesData } = useQuery({
|
|
queryKey: ['packages-all-simple'],
|
|
queryFn: () => packagesApi.getAll({ pageSize: 100 }),
|
|
});
|
|
|
|
const packages: any[] = packagesData?.items || [];
|
|
|
|
const statusMutation = useMutation({
|
|
mutationFn: ({ id, status }: { id: string; status: string }) =>
|
|
packageInquiriesApi.updateStatus(id, status),
|
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['package-inquiries'] }),
|
|
});
|
|
|
|
const deleteMutation = useMutation({
|
|
mutationFn: (id: string) => packageInquiriesApi.remove(id),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['package-inquiries'] });
|
|
setDeleteConfirm(null);
|
|
setDeleteError(null);
|
|
},
|
|
onError: (e: any) => setDeleteError(e?.response?.data?.message || e?.message || 'Failed to delete'),
|
|
});
|
|
|
|
const columns = [
|
|
{
|
|
key: 'contact',
|
|
label: 'Contact',
|
|
render: (row: any) => (
|
|
<div>
|
|
<div className="font-semibold">{row.contactName}</div>
|
|
<div className="text-xs text-muted-foreground">{row.contactEmail || row.contactPhone || '—'}</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: 'package',
|
|
label: 'Package',
|
|
render: (row: any) => (
|
|
<div>
|
|
<div className="font-medium">{row.package?.name || '—'}</div>
|
|
<div className="text-xs text-muted-foreground font-mono">{row.package?.code}</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: 'priceTier',
|
|
label: 'Price Tier',
|
|
render: (row: any) => row.priceTier ? (
|
|
<div>
|
|
<div className="text-sm font-medium">{row.priceTier.label}</div>
|
|
<div className="text-xs text-muted-foreground">{formatCurrency(row.priceTier.priceMinor, 'ETB')} / person</div>
|
|
</div>
|
|
) : <span className="text-muted-foreground text-sm">—</span>,
|
|
},
|
|
{
|
|
key: 'travelerCount',
|
|
label: 'Travelers',
|
|
render: (row: any) => (
|
|
<span className="font-semibold">{row.travelerCount}</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'enquiredAt',
|
|
label: 'Enquired At',
|
|
render: (row: any) => (
|
|
<span className="text-sm">{formatDateTime(row.enquiredAt)}</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'notes',
|
|
label: 'Notes',
|
|
render: (row: any) => (
|
|
<span className="text-sm text-muted-foreground line-clamp-2 max-w-xs">{row.notes || '—'}</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'status',
|
|
label: 'Status',
|
|
render: (row: any) => (
|
|
<select
|
|
className="input py-1 text-xs"
|
|
value={row.status}
|
|
disabled={!canEdit}
|
|
title={canEdit ? undefined : 'You do not have permission to change an inquiry status'}
|
|
onChange={(e) => statusMutation.mutate({ id: row.id, status: e.target.value })}
|
|
>
|
|
{STATUSES.map((s) => (
|
|
<option key={s} value={s}>{s}</option>
|
|
))}
|
|
</select>
|
|
),
|
|
},
|
|
];
|
|
|
|
const actions = [
|
|
{
|
|
label: 'Delete',
|
|
show: () => canDelete,
|
|
onClick: (row: any) => { setDeleteConfirm(row); setDeleteError(null); },
|
|
variant: 'danger' as const,
|
|
icon: Trash2,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-foreground">Package Inquiries</h1>
|
|
<p className="text-muted-foreground">Manage incoming package inquiries</p>
|
|
</div>
|
|
|
|
<div className="card">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="label">Package</label>
|
|
<select
|
|
className="input"
|
|
value={filters.packageId}
|
|
onChange={(e) => setFilters({ ...filters, packageId: e.target.value })}
|
|
>
|
|
<option value="">All Packages</option>
|
|
{packages.map((p: any) => (
|
|
<option key={p.id} value={p.id}>{p.name} ({p.code})</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="label">Status</label>
|
|
<select
|
|
className="input"
|
|
value={filters.status}
|
|
onChange={(e) => setFilters({ ...filters, status: e.target.value })}
|
|
>
|
|
<option value="">All Statuses</option>
|
|
{STATUSES.map((s) => (
|
|
<option key={s} value={s}>{s}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<DataTable
|
|
data={data?.items || []}
|
|
columns={columns}
|
|
actions={actions}
|
|
loading={isLoading}
|
|
emptyMessage="No inquiries found"
|
|
/>
|
|
|
|
<ConfirmDialog
|
|
isOpen={!!deleteConfirm}
|
|
onClose={() => { setDeleteConfirm(null); setDeleteError(null); }}
|
|
onConfirm={() => deleteMutation.mutate(deleteConfirm.id)}
|
|
title="Delete Inquiry"
|
|
message={`Delete inquiry from ${deleteConfirm?.contactName}? This cannot be undone.`}
|
|
confirmText="Delete"
|
|
isDanger
|
|
isLoading={deleteMutation.isPending}
|
|
error={deleteError ?? undefined}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function PackageInquiriesPage() {
|
|
return (
|
|
<PermissionGuard permission={PERMS.inquiries.view}>
|
|
<PackageInquiriesPageContent />
|
|
</PermissionGuard>
|
|
);
|
|
}
|