feat(admin): manual narrative generation
This commit is contained in:
parent
4a278fa56b
commit
ee65f2238d
2 changed files with 49 additions and 1 deletions
31
src/app/actions/narrative.ts
Normal file
31
src/app/actions/narrative.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
import { requireSession } from "@/lib/auth/session";
|
||||
import { narrateProject } from "@/lib/ai/narrate";
|
||||
|
||||
/** See the security note at the top of src/app/actions/cards.ts. */
|
||||
export async function narrateProjectAction(input: {
|
||||
projectId: string;
|
||||
}): Promise<{ written: number; message: string }> {
|
||||
const session = await requireSession();
|
||||
if (session.role !== "owner") throw new Error("Not found");
|
||||
|
||||
const result = await narrateProject(input.projectId);
|
||||
revalidatePath(`/admin/projects/${input.projectId}`);
|
||||
|
||||
if (result.skipped) {
|
||||
return { written: 0, message: result.error ?? "No AI provider is configured." };
|
||||
}
|
||||
if (result.considered === 0) {
|
||||
return { written: 0, message: "Every narrative is up to date." };
|
||||
}
|
||||
|
||||
return {
|
||||
written: result.written,
|
||||
message: `${result.written} of ${result.considered} narratives written${
|
||||
result.error ? ` · ${result.error}` : ""
|
||||
}`,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { Sparkles, RotateCcw } from "lucide-react";
|
||||
import { Sparkles, RotateCcw, BookText } from "lucide-react";
|
||||
|
||||
import {
|
||||
retryFailedSummariesAction,
|
||||
summariseProjectAction,
|
||||
} from "@/app/actions/summarise";
|
||||
import { narrateProjectAction } from "@/app/actions/narrative";
|
||||
|
||||
export function SummaryPanel({
|
||||
projectId,
|
||||
|
|
@ -83,6 +84,22 @@ export function SummaryPanel({
|
|||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
disabled={pending}
|
||||
onClick={() =>
|
||||
startTransition(async () => {
|
||||
setMessage("Writing narratives…");
|
||||
const result = await narrateProjectAction({ projectId });
|
||||
setMessage(result.message);
|
||||
})
|
||||
}
|
||||
className="inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-2.5 py-1.5 text-xs text-text-secondary hover:bg-surface-raised disabled:opacity-50"
|
||||
>
|
||||
<BookText size={13} strokeWidth={2} />
|
||||
Write card narratives
|
||||
</button>
|
||||
|
||||
{message && <span className="text-xs text-text-muted">{message}</span>}
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
Loading…
Reference in a new issue