Files
Investidor-V2/resources/js/pages/crm/lead.tsx
T
nala 250ac35fd1
tests / ci (push) Canceled after 0s
first commit
2026-09-28 10:01:09 +02:00

247 lines
9.4 KiB
TypeScript

import { Link, useForm } from '@inertiajs/react';
import { Management, Field, button, control } from '../management/shared';
import { leadStates, taskStates } from './types';
import type { Lead, Owner, Task } from './types';
function TaskRow({ task, leadId }: { task: Task; leadId: string }) {
const form = useForm({ status: task.status });
return (
<form
className="space-y-3 rounded border p-4"
onSubmit={(e) => {
e.preventDefault();
form.patch(`/management/crm/leads/${leadId}/tasks/${task.id}`);
}}
>
<h3 className="font-semibold">{task.title}</h3>
<p>
{task.due_at
? new Intl.DateTimeFormat('pt-MZ', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Africa/Maputo',
}).format(new Date(task.due_at)) + ' (Maputo)'
: 'Sem prazo'}{' '}
· Responsável #{task.assignee_user_id}
</p>
<Field
label={`Estado da tarefa: ${task.title}`}
error={form.errors.status}
>
<select
className={control}
value={form.data.status}
onChange={(e) => form.setData('status', e.target.value)}
>
{Object.entries(taskStates).map(([key, label]) => (
<option key={key} value={key}>
{label}
</option>
))}
</select>
</Field>
<button className={button} disabled={form.processing}>
Actualizar tarefa
</button>
</form>
);
}
export default function LeadDetail({
lead,
contacts,
owners,
}: {
lead: Lead;
contacts: {
id: string;
full_name: string;
email: string | null;
phone: string | null;
}[];
owners: Owner[];
}) {
const review = useForm({
status: lead.status,
owner_user_id: String(lead.owner_user_id ?? ''),
qualification_reason: '',
});
const task = useForm({
title: '',
due_at: '',
assignee_user_id: String(lead.owner_user_id ?? ''),
});
return (
<Management title={lead.title}>
<Link className="underline" href="/management/crm/leads">
Voltar aos leads
</Link>
<section className="space-y-2">
<h2 className="text-xl font-semibold">
{lead.investor && (
<Link
className="underline"
href={`/management/crm/investors/${lead.investor.id}`}
>
{lead.investor.legal_name}
</Link>
)}
</h2>
<p>
{leadStates[lead.status]} ·{' '}
{lead.owner?.name ?? 'Sem responsável'}
</p>
<p>Origem inicial: {lead.first_source ?? 'Não registada'}</p>
{lead.interest_request_id && (
<Link
className="underline"
href={`/management/interests/${lead.interest_request_id}`}
>
Consultar pedido de origem
</Link>
)}
<div className="space-y-2">
{contacts.map((contact) => (
<p key={contact.id}>
{contact.full_name} · {contact.email ?? '—'} ·{' '}
{contact.phone ?? '—'}
</p>
))}
</div>
</section>
<form
className="max-w-xl space-y-4"
onSubmit={(e) => {
e.preventDefault();
review.patch(`/management/crm/leads/${lead.id}`);
}}
>
<h2 className="text-lg font-semibold">
Qualificação e atribuição
</h2>
<Field label="Estado do lead" error={review.errors.status}>
<select
className={control}
value={review.data.status}
onChange={(e) =>
review.setData('status', e.target.value)
}
>
{Object.entries(leadStates)
.filter(([key]) => key !== 'converted')
.map(([key, label]) => (
<option key={key} value={key}>
{label}
</option>
))}
</select>
</Field>
<Field label="Responsável" error={review.errors.owner_user_id}>
<select
className={control}
value={review.data.owner_user_id}
onChange={(e) =>
review.setData('owner_user_id', e.target.value)
}
>
<option value="">Seleccione</option>
{owners.map((owner) => (
<option key={owner.id} value={owner.id}>
{owner.name}
</option>
))}
</select>
</Field>
<Field
label="Fundamento da alteração"
error={review.errors.qualification_reason}
>
<textarea
className={control}
value={review.data.qualification_reason}
onChange={(e) =>
review.setData(
'qualification_reason',
e.target.value,
)
}
/>
</Field>
{lead.qualification_reason && (
<p className="text-sm whitespace-pre-wrap">
Último fundamento: {lead.qualification_reason}
</p>
)}
<button
className={button}
disabled={review.processing || lead.status === 'converted'}
>
Guardar lead
</button>
</form>
<section className="space-y-4">
<h2 className="text-lg font-semibold">Tarefas</h2>
{lead.tasks.map((item) => (
<TaskRow key={item.id} task={item} leadId={lead.id} />
))}
{!lead.tasks.length && <p>Não existem tarefas.</p>}
</section>
<form
className="max-w-xl space-y-4"
onSubmit={(e) => {
e.preventDefault();
task.transform((data) => ({
...data,
due_at: data.due_at
? new Date(data.due_at).toISOString()
: '',
}));
task.post(`/management/crm/leads/${lead.id}/tasks`, {
onSuccess: () => task.reset('title', 'due_at'),
});
}}
>
<h2 className="text-lg font-semibold">
Registar próxima acção
</h2>
<Field label="Tarefa" error={task.errors.title}>
<input
className={control}
value={task.data.title}
onChange={(e) => task.setData('title', e.target.value)}
/>
</Field>
<Field label="Prazo (hora local)" error={task.errors.due_at}>
<input
type="datetime-local"
className={control}
value={task.data.due_at}
onChange={(e) => task.setData('due_at', e.target.value)}
/>
</Field>
<Field
label="Responsável pela tarefa"
error={task.errors.assignee_user_id}
>
<select
className={control}
value={task.data.assignee_user_id}
onChange={(e) =>
task.setData('assignee_user_id', e.target.value)
}
>
<option value="">Seleccione</option>
{owners.map((owner) => (
<option key={owner.id} value={owner.id}>
{owner.name}
</option>
))}
</select>
</Field>
<button className={button} disabled={task.processing}>
Criar tarefa
</button>
</form>
</Management>
);
}