mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 08:18:20 +00:00
Backoffice UAT results addressed, packages and other updates
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Plus, Edit, DollarSign, Clock, Eye } from 'lucide-react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Plus, Edit, Eye } from 'lucide-react';
|
||||
import DataTable from '@/components/ui/DataTable';
|
||||
import ActionButton from '@/components/ui/ActionButton';
|
||||
import Badge from '@/components/ui/Badge';
|
||||
import Modal from '@/components/ui/Modal';
|
||||
import { agentsApi } from '@/lib/api';
|
||||
import { agentsApi, apiClient } from '@/lib/api';
|
||||
import { formatCurrency, formatDateTime } from '@/lib/utils';
|
||||
import { useAuthStore } from '@/lib/auth-store';
|
||||
|
||||
const Field = ({ label, value, mono = false, truncate = false }: { label: string; value: string; mono?: boolean; truncate?: boolean }) => (
|
||||
<div className="bg-muted/40 rounded-lg p-3">
|
||||
@@ -24,8 +25,51 @@ const SectionHeader = ({ title }: { title: string }) => (
|
||||
);
|
||||
|
||||
export default function AgentsPage() {
|
||||
const { user } = useAuthStore();
|
||||
const queryClient = useQueryClient();
|
||||
const [filters, setFilters] = useState({ search: '', active: '' });
|
||||
const [selected, setSelected] = useState<any>(null);
|
||||
const [createModal, setCreateModal] = useState(false);
|
||||
const [createForm, setCreateForm] = useState({ iamUserId: '', agentCode: '', commissionRate: '5' });
|
||||
const [createError, setCreateError] = useState<string | null>(null);
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: (data: any) => apiClient.post('/agents', data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] });
|
||||
setCreateModal(false);
|
||||
setCreateError(null);
|
||||
},
|
||||
onError: (e: any) => setCreateError(e?.response?.data?.message || e?.message || 'Failed to create agent'),
|
||||
});
|
||||
|
||||
const [editModal, setEditModal] = useState(false);
|
||||
const [editForm, setEditForm] = useState({ agentCode: '', commissionRate: '5', active: true });
|
||||
const [editingAgent, setEditingAgent] = useState<any>(null);
|
||||
const [editError, setEditError] = useState<string | null>(null);
|
||||
|
||||
const editMutation = useMutation({
|
||||
mutationFn: ({ id, ...data }: any) => apiClient.patch(`/agents/${id}`, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] });
|
||||
setEditModal(false);
|
||||
setEditError(null);
|
||||
},
|
||||
onError: (e: any) => setEditError(e?.response?.data?.message || e?.message || 'Failed to update agent'),
|
||||
});
|
||||
|
||||
const openCreateModal = () => {
|
||||
setCreateForm({ iamUserId: '', agentCode: '', commissionRate: '5' });
|
||||
setCreateError(null);
|
||||
setCreateModal(true);
|
||||
};
|
||||
|
||||
const openEditModal = (agent: any) => {
|
||||
setEditingAgent(agent);
|
||||
setEditForm({ agentCode: agent.agentCode, commissionRate: String(agent.commissionRate ?? 5), active: agent.active });
|
||||
setEditError(null);
|
||||
setEditModal(true);
|
||||
};
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['agents', filters],
|
||||
@@ -67,39 +111,27 @@ export default function AgentsPage() {
|
||||
|
||||
const actions = [
|
||||
{
|
||||
label: 'View Details',
|
||||
label: 'Edit',
|
||||
onClick: (agent: any) => openEditModal(agent),
|
||||
variant: 'secondary' as const,
|
||||
icon: Edit,
|
||||
},
|
||||
{
|
||||
label: 'Details',
|
||||
onClick: (agent: any) => setSelected(agent),
|
||||
variant: 'secondary' as const,
|
||||
icon: Eye,
|
||||
},
|
||||
{
|
||||
label: 'View Shifts',
|
||||
onClick: (agent: any) => { window.location.href = `/agents/${agent.id}/shifts`; },
|
||||
variant: 'secondary' as const,
|
||||
icon: Clock,
|
||||
},
|
||||
{
|
||||
label: 'View Commissions',
|
||||
onClick: (agent: any) => { window.location.href = `/agents/${agent.id}/commissions`; },
|
||||
variant: 'secondary' as const,
|
||||
icon: DollarSign,
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
onClick: (agent: any) => console.log('Edit', agent),
|
||||
variant: 'secondary' as const,
|
||||
icon: Edit,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Agent Operations</h1>
|
||||
<p className="text-muted-foreground">Manage booking agents and their operations</p>
|
||||
<h1 className="text-2xl font-bold">Agents</h1>
|
||||
<p className="text-muted-foreground">Manage agents and their operations</p>
|
||||
</div>
|
||||
<ActionButton icon={Plus}>Add Agent</ActionButton>
|
||||
<ActionButton icon={Plus} onClick={openCreateModal}>Add Agent</ActionButton>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
@@ -227,6 +259,100 @@ export default function AgentsPage() {
|
||||
);
|
||||
})()}
|
||||
</Modal>
|
||||
{/* Create Agent Modal */}
|
||||
<Modal isOpen={createModal} onClose={() => setCreateModal(false)} title="Add Agent Profile" size="md">
|
||||
<div className="space-y-4">
|
||||
{createError && (
|
||||
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 p-3 rounded-lg text-sm text-red-800 dark:text-red-200">
|
||||
{createError}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label className="label">IAM User ID *</label>
|
||||
<input
|
||||
className="input"
|
||||
value={createForm.iamUserId}
|
||||
onChange={(e) => setCreateForm({ ...createForm, iamUserId: e.target.value })}
|
||||
placeholder="IAM user UUID"
|
||||
/>
|
||||
{user?.id && createForm.iamUserId === user.id && (
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400 mt-1">✓ Pre-filled with your logged-in user ID</p>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground mt-1">Links this agent profile to an IAM back-office user</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="label">Agent Code (optional)</label>
|
||||
<input
|
||||
className="input"
|
||||
value={createForm.agentCode}
|
||||
onChange={(e) => setCreateForm({ ...createForm, agentCode: e.target.value })}
|
||||
placeholder="e.g. AG0002 (auto-generated if empty)"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Commission Rate (%)</label>
|
||||
<input
|
||||
type="number" min="0" max="100" className="input"
|
||||
value={createForm.commissionRate}
|
||||
onChange={(e) => setCreateForm({ ...createForm, commissionRate: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<ActionButton variant="secondary" onClick={() => setCreateModal(false)}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
loading={createMutation.isPending}
|
||||
onClick={() => createMutation.mutate({
|
||||
iamUserId: createForm.iamUserId,
|
||||
agentCode: createForm.agentCode || undefined,
|
||||
commissionRate: parseInt(createForm.commissionRate) || 5,
|
||||
})}
|
||||
>
|
||||
Create Agent
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
{/* Edit Agent Modal */}
|
||||
<Modal isOpen={editModal} onClose={() => setEditModal(false)} title="Edit Agent" size="md">
|
||||
<div className="space-y-4">
|
||||
{editError && (
|
||||
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 p-3 rounded-lg text-sm text-red-800 dark:text-red-200">
|
||||
{editError}
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="label">Agent Code</label>
|
||||
<input className="input" value={editForm.agentCode}
|
||||
onChange={(e) => setEditForm({ ...editForm, agentCode: e.target.value })} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Commission Rate (%)</label>
|
||||
<input type="number" min="0" max="100" className="input" value={editForm.commissionRate}
|
||||
onChange={(e) => setEditForm({ ...editForm, commissionRate: e.target.value })} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="label">Status</label>
|
||||
<select className="input" value={editForm.active ? 'true' : 'false'}
|
||||
onChange={(e) => setEditForm({ ...editForm, active: e.target.value === 'true' })}>
|
||||
<option value="true">Active</option>
|
||||
<option value="false">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<ActionButton variant="secondary" onClick={() => setEditModal(false)}>Cancel</ActionButton>
|
||||
<ActionButton loading={editMutation.isPending} onClick={() => editMutation.mutate({
|
||||
id: editingAgent.id,
|
||||
agentCode: editForm.agentCode,
|
||||
commissionRate: parseInt(editForm.commissionRate) || 5,
|
||||
active: editForm.active,
|
||||
})}>Save Changes</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user