feat: AI draft pentru research brief + sinteza AI in briefing
- BriefForm: buton "Generează cu AI" cheama /v1/research-briefs/draft si precompleteaza titlul+rezumatul; textul spune explicit ca nimic nu s-a salvat inca si arata costul real al generarii - Briefing: afiseaza aiExplanation cand exista, cu nota ca prioritizarea ramane deterministica si AI-ul doar explica
This commit is contained in:
parent
60e5befa31
commit
b2260fe839
2 changed files with 53 additions and 9 deletions
|
|
@ -19,17 +19,37 @@ interface BriefFormProps {
|
|||
onClose: () => void;
|
||||
}
|
||||
|
||||
interface DraftResponse {
|
||||
title: string;
|
||||
summary: string;
|
||||
costUsd: number;
|
||||
}
|
||||
|
||||
export function BriefForm({ tenantId, organizationId, organizationName, onClose }: BriefFormProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<BriefForm>({
|
||||
resolver: zodResolver(briefSchema),
|
||||
defaultValues: { title: `Research brief — ${organizationName}` },
|
||||
});
|
||||
|
||||
const generateDraft = useMutation({
|
||||
mutationFn: () =>
|
||||
apiFetch<DraftResponse>('/v1/research-briefs/draft', {
|
||||
method: 'POST',
|
||||
tenantId,
|
||||
body: { organizationId },
|
||||
}),
|
||||
onSuccess: (draft) => {
|
||||
setValue('title', draft.title, { shouldValidate: true });
|
||||
setValue('summary', draft.summary, { shouldValidate: true });
|
||||
},
|
||||
});
|
||||
|
||||
const createBrief = useMutation({
|
||||
mutationFn: (values: BriefForm) =>
|
||||
apiFetch('/v1/research-briefs', {
|
||||
|
|
@ -58,14 +78,32 @@ export function BriefForm({ tenantId, organizationId, organizationName, onClose
|
|||
{errors.title && <p className="mt-1 text-xs text-signal-danger">{errors.title.message}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label className="label" htmlFor="brief-summary">
|
||||
Rezumat
|
||||
</label>
|
||||
<textarea id="brief-summary" rows={5} className="field" {...register('summary')} />
|
||||
<div className="mb-1.5 flex items-center justify-between">
|
||||
<label className="label mb-0" htmlFor="brief-summary">
|
||||
Rezumat
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => generateDraft.mutate()}
|
||||
disabled={generateDraft.isPending}
|
||||
className="text-xs font-medium text-bronze-deep hover:underline disabled:opacity-50"
|
||||
>
|
||||
{generateDraft.isPending ? 'Se generează…' : 'Generează cu AI'}
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="brief-summary" rows={7} className="field" {...register('summary')} />
|
||||
{errors.summary && <p className="mt-1 text-xs text-signal-danger">{errors.summary.message}</p>}
|
||||
<p className="mt-1 text-xs text-ink-faint">
|
||||
Scris de tine — nu există încă un AI Gateway care să genereze acest rezumat automat.
|
||||
</p>
|
||||
{generateDraft.isError && (
|
||||
<p className="mt-1 text-xs text-signal-danger">
|
||||
{generateDraft.error instanceof Error ? generateDraft.error.message : 'Eroare la generare'}
|
||||
</p>
|
||||
)}
|
||||
{generateDraft.isSuccess && (
|
||||
<p className="mt-1 text-xs text-ink-faint">
|
||||
Draft generat de AI din datele Apollo (cost ${generateDraft.data.costUsd.toFixed(4)}).
|
||||
Revizuiește-l înainte să salvezi — nimic nu s-a salvat încă.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{createBrief.isError && (
|
||||
|
|
|
|||
|
|
@ -47,6 +47,13 @@ export default function OverviewPage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{briefing.aiExplanation && (
|
||||
<section className="card border-l-4 border-l-bronze p-5">
|
||||
<p className="label mb-1.5">Sinteza zilei</p>
|
||||
<p className="whitespace-pre-wrap text-sm text-ink-soft">{briefing.aiExplanation}</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{briefing.overdueTasks.length > 0 && (
|
||||
<section>
|
||||
<h2 className="label mb-2">Sarcini restante</h2>
|
||||
|
|
@ -82,8 +89,7 @@ export default function OverviewPage() {
|
|||
</section>
|
||||
|
||||
<p className="text-xs text-ink-faint">
|
||||
Prioritizare deterministă din task-uri. Explicația AI nu e încă disponibilă (AI Gateway
|
||||
neconstruit).
|
||||
Prioritizarea e deterministă; AI-ul doar explică rezultatul, nu decide ordinea.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue