Files
alla-allaos-fullstack/src/features/crm/leads/components/lead-detail.tsx
phaichayon c2a74b6764 task-d5.5.1
2026-06-25 12:16:41 +07:00

192 lines
7.6 KiB
TypeScript

'use client';
import Link from 'next/link';
import { useState } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { leadByIdOptions } from '../api/queries';
import type { LeadReferenceData } from '../api/types';
import { LeadAssignmentDialog } from './lead-assignment-dialog';
import { LeadFollowupPanel } from './lead-followup-panel';
import { LeadForm } from './lead-form';
function FieldItem({ label, value }: { label: string; value: string | null | undefined }) {
return (
<div className='space-y-1'>
<div className='text-muted-foreground text-xs'>{label}</div>
<div className='text-sm'>{value || '-'}</div>
</div>
);
}
export function LeadDetail({
leadId,
referenceData,
canUpdate,
canDelete,
canAssign
}: {
leadId: string;
referenceData: LeadReferenceData;
canUpdate: boolean;
canDelete: boolean;
canAssign: boolean;
}) {
const { data } = useSuspenseQuery(leadByIdOptions(leadId));
const lead = data.lead;
const [editOpen, setEditOpen] = useState(false);
const [assignOpen, setAssignOpen] = useState(false);
return (
<div className='space-y-6'>
<LeadForm open={editOpen} onOpenChange={setEditOpen} referenceData={referenceData} lead={lead} />
<LeadAssignmentDialog
lead={lead}
referenceData={referenceData}
open={assignOpen}
onOpenChange={setAssignOpen}
/>
<div className='flex items-start justify-between gap-4'>
<div>
<div className='flex items-center gap-3'>
<h2 className='text-2xl font-semibold'>{lead.code}</h2>
<Badge variant='outline'>{lead.statusLabel ?? lead.status}</Badge>
</div>
<p className='text-muted-foreground mt-2 text-sm'>
Lead workspace for marketing qualification before sales opportunity handoff
</p>
</div>
<div className='flex flex-wrap gap-2'>
{canAssign ? (
<Button variant='outline' onClick={() => setAssignOpen(true)}>
{lead.assignedSalesOwnerId ? 'Reassign to Sales' : 'Assign to Sales'}
</Button>
) : null}
{canUpdate ? (
<Button variant='outline' onClick={() => setEditOpen(true)}>
Edit Lead
</Button>
) : null}
</div>
</div>
<div className='grid gap-6 lg:grid-cols-3'>
<div className='space-y-6 lg:col-span-2'>
<Card>
<CardHeader>
<CardTitle>Lead Summary</CardTitle>
</CardHeader>
<CardContent className='grid gap-4 md:grid-cols-2 xl:grid-cols-3'>
<FieldItem label='Customer' value={lead.customerName} />
<FieldItem label='Contact' value={lead.contactName} />
<FieldItem label='Project Name' value={lead.projectName} />
<FieldItem label='Project Location' value={lead.projectLocation} />
<FieldItem label='Awareness' value={lead.awarenessLabel} />
<FieldItem label='Priority' value={lead.priority} />
<FieldItem label='Marketing Owner' value={lead.ownerMarketingName} />
<FieldItem label='Suggested Sales Owner' value={lead.suggestedSalesOwnerName} />
<FieldItem label='Assigned Sales Owner' value={lead.assignedSalesOwnerName} />
<FieldItem
label='Estimated Value'
value={lead.estimatedValue !== null ? lead.estimatedValue.toLocaleString() : null}
/>
<FieldItem label='Assignment Remark' value={lead.assignmentRemark} />
<FieldItem label='Description' value={lead.description} />
</CardContent>
</Card>
<Tabs defaultValue='followups' className='space-y-4'>
<TabsList>
<TabsTrigger value='followups'>Follow-ups</TabsTrigger>
<TabsTrigger value='linked-opportunities'>Linked Opportunities</TabsTrigger>
<TabsTrigger value='activity'>Activity</TabsTrigger>
</TabsList>
<TabsContent value='followups'>
<LeadFollowupPanel leadId={leadId} detail={data} />
</TabsContent>
<TabsContent value='linked-opportunities'>
<Card>
<CardHeader>
<CardTitle>Linked Opportunities</CardTitle>
</CardHeader>
<CardContent className='space-y-3'>
{lead.linkedOpportunities.length === 0 ? (
<div className='text-muted-foreground text-sm'>No linked opportunities yet.</div>
) : (
lead.linkedOpportunities.map((opportunity) => (
<div key={opportunity.id} className='border-border rounded-md border p-3'>
<div className='flex items-center justify-between gap-3'>
<Link
href={`/dashboard/crm/opportunities/${opportunity.id}`}
className='font-medium hover:underline'
>
{opportunity.code}
</Link>
<Badge variant='outline'>{opportunity.status}</Badge>
</div>
<div className='mt-1 text-sm'>{opportunity.title}</div>
<div className='text-muted-foreground mt-2 text-xs'>
Owner: {opportunity.assignedToName ?? '-'}
</div>
</div>
))
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value='activity'>
<Card>
<CardHeader>
<CardTitle>Activity</CardTitle>
</CardHeader>
<CardContent className='space-y-3'>
{lead.activity.length === 0 ? (
<div className='text-muted-foreground text-sm'>No activity yet.</div>
) : (
lead.activity.map((activity) => (
<div key={activity.id} className='border-border rounded-md border p-3'>
<div className='font-medium'>{activity.action}</div>
<div className='text-muted-foreground mt-1 text-xs'>
{activity.actorName ?? activity.userId} -{' '}
{new Date(activity.createdAt).toLocaleString()}
</div>
</div>
))
)}
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
<div className='space-y-6'>
<Card>
<CardHeader>
<CardTitle>Workspace Status</CardTitle>
</CardHeader>
<CardContent className='space-y-3'>
<FieldItem label='Current Workspace' value='Lead' />
<FieldItem label='Related Opportunities' value={String(lead.relatedOpportunityCount)} />
<FieldItem
label='Assigned At'
value={lead.assignedAt ? new Date(lead.assignedAt).toLocaleString() : null}
/>
<FieldItem label='Assigned By' value={lead.assignedByName} />
<FieldItem label='Deleted' value={canDelete ? 'Allowed' : 'Not allowed'} />
</CardContent>
</Card>
</div>
</div>
</div>
);
}