feat(admin): delete button on request link
The request-links panel now has a trash button at the far right of each row. Click it, the link is gone — cascadeDelete on intake_link_submissions.link removes the daily counters in the same transaction, and revalidatePath re-renders the panel without the row. Hard delete, no confirmation: matches the existing Pause/Resume pattern (no dialog there either). The icon is a Trash2 from lucide-react; the title attribute is the only label, by design owner-only surface. Phase 9 shape: action takes a scope so revalidation hits the client admin page the panel is mounted on.
This commit is contained in:
parent
3a721df094
commit
6cc7ae01f7
2 changed files with 36 additions and 1 deletions
|
|
@ -114,6 +114,22 @@ export async function setIntakeLinkNotesAction(input: {
|
||||||
revalidatePath(adminPathForScope(input.scope));
|
revalidatePath(adminPathForScope(input.scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hard-delete a request link. Cascades to `intake_link_submissions` rows
|
||||||
|
* (those use `cascadeDelete: true` on their `link` field, so they go
|
||||||
|
* with it). The token stops working immediately because there's no row
|
||||||
|
* left to resolve against.
|
||||||
|
*/
|
||||||
|
export async function deleteIntakeLinkAction(input: {
|
||||||
|
linkId: string;
|
||||||
|
scope: IntakeLinkScope;
|
||||||
|
}): Promise<void> {
|
||||||
|
await requireOwner();
|
||||||
|
const pb = await getAdminPb();
|
||||||
|
await pb.collection("intake_links").delete(input.linkId);
|
||||||
|
revalidatePath(adminPathForScope(input.scope));
|
||||||
|
}
|
||||||
|
|
||||||
function intakeUrlFor(token: string): string {
|
function intakeUrlFor(token: string): string {
|
||||||
const base = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
|
const base = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
|
||||||
return `${base}/intake/${token}`;
|
return `${base}/intake/${token}`;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useTransition } from "react";
|
import { useState, useTransition } from "react";
|
||||||
import { Copy, RefreshCw, Power, PowerOff, Plus } from "lucide-react";
|
import { Copy, RefreshCw, Power, PowerOff, Plus, Trash2 } from "lucide-react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createIntakeLinkAction,
|
createIntakeLinkAction,
|
||||||
|
deleteIntakeLinkAction,
|
||||||
rotateIntakeTokenAction,
|
rotateIntakeTokenAction,
|
||||||
setIntakeLinkActiveAction,
|
setIntakeLinkActiveAction,
|
||||||
setIntakeLinkNotesAction,
|
setIntakeLinkNotesAction,
|
||||||
|
|
@ -100,6 +101,15 @@ export function IntakeLinksPanel({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function remove(link: IntakeLinkRecord) {
|
||||||
|
startTransition(async () => {
|
||||||
|
await deleteIntakeLinkAction({
|
||||||
|
linkId: link.id,
|
||||||
|
scope,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const field =
|
const field =
|
||||||
"w-full rounded-lg border border-border-subtle bg-surface px-3 py-2 text-xs text-text-secondary";
|
"w-full rounded-lg border border-border-subtle bg-surface px-3 py-2 text-xs text-text-secondary";
|
||||||
|
|
||||||
|
|
@ -220,6 +230,15 @@ export function IntakeLinksPanel({
|
||||||
{link.active ? <PowerOff size={12} strokeWidth={2} /> : <Power size={12} strokeWidth={2} />}
|
{link.active ? <PowerOff size={12} strokeWidth={2} /> : <Power size={12} strokeWidth={2} />}
|
||||||
{link.active ? "Revoke" : "Re-activate"}
|
{link.active ? "Revoke" : "Re-activate"}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={pending}
|
||||||
|
onClick={() => remove(link)}
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-2 py-1 text-xs text-text-secondary hover:bg-surface-raised disabled:opacity-50 ml-auto"
|
||||||
|
title="Delete this link permanently"
|
||||||
|
>
|
||||||
|
<Trash2 size={12} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NotesEditor link={link} onSave={saveNotes} />
|
<NotesEditor link={link} onSave={saveNotes} />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue