fix: quick add opens create ui
Add createKey to quick-add manifests and client create dialog host. FAB and command palette open in-place create UIs instead of navigating away.
This commit is contained in:
@@ -67,6 +67,8 @@ export type QuickAddAction = {
|
||||
icon?: string;
|
||||
/** Navigation target used by the FAB sheet and command palette. */
|
||||
url: string;
|
||||
/** Client-side create dialog key (see quick-add create registry). */
|
||||
createKey?: string;
|
||||
action?: (ctx: WidgetContext) => void | Promise<void>;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export type SerializedQuickAddItem = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
url: string;
|
||||
createKey?: string;
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
};
|
||||
@@ -95,11 +96,12 @@ export async function fireItemToggleHooks(payload: ItemTogglePayload): Promise<v
|
||||
|
||||
export function getQuickAdds(): SerializedQuickAddItem[] {
|
||||
return [...modules.values()].flatMap((manifest) =>
|
||||
(manifest.quickAdds ?? []).map(({ id, label, icon, url }) => ({
|
||||
(manifest.quickAdds ?? []).map(({ id, label, icon, url, createKey }) => ({
|
||||
id,
|
||||
label,
|
||||
icon,
|
||||
url,
|
||||
createKey,
|
||||
moduleId: manifest.id,
|
||||
moduleName: manifest.name,
|
||||
})),
|
||||
|
||||
@@ -41,6 +41,15 @@ const bangsManifest: ModuleManifest = {
|
||||
render: (props) => <BangWidgetServer {...props} />,
|
||||
},
|
||||
],
|
||||
quickAdds: [
|
||||
{
|
||||
id: "bangs.record",
|
||||
label: "Record bang",
|
||||
icon: "sparkles",
|
||||
url: "/",
|
||||
createKey: "bangs.record",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default bangsManifest;
|
||||
|
||||
@@ -226,12 +226,14 @@ const manifest: ModuleManifest = {
|
||||
label: "New event",
|
||||
icon: "calendar-plus",
|
||||
url: "/calendar",
|
||||
createKey: "calendar.event",
|
||||
},
|
||||
{
|
||||
id: "calendar.new-calendar",
|
||||
label: "New calendar",
|
||||
icon: "calendar-days",
|
||||
url: "/calendar",
|
||||
createKey: "calendar.calendar",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -42,9 +42,17 @@ type Props = {
|
||||
existingPlant?: PlantDetailDto;
|
||||
defaultContainerId?: string | null;
|
||||
containers: ContainerDto[];
|
||||
onSuccess?: (plantId: string) => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
export function PlantForm({ existingPlant, defaultContainerId, containers }: Props) {
|
||||
export function PlantForm({
|
||||
existingPlant,
|
||||
defaultContainerId,
|
||||
containers,
|
||||
onSuccess,
|
||||
onCancel,
|
||||
}: Props) {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
@@ -121,11 +129,19 @@ export function PlantForm({ existingPlant, defaultContainerId, containers }: Pro
|
||||
try {
|
||||
if (existingPlant) {
|
||||
await updatePlant({ id: existingPlant.id, ...input });
|
||||
router.push(`/garden/plants/${existingPlant.id}`);
|
||||
router.refresh();
|
||||
if (onSuccess) {
|
||||
onSuccess(existingPlant.id);
|
||||
} else {
|
||||
router.push(`/garden/plants/${existingPlant.id}`);
|
||||
router.refresh();
|
||||
}
|
||||
} else {
|
||||
const plant = await createPlant(input);
|
||||
router.push(`/garden/plants/${plant.id}`);
|
||||
if (onSuccess) {
|
||||
onSuccess(plant.id);
|
||||
} else {
|
||||
router.push(`/garden/plants/${plant.id}`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
setError("Something went wrong. Please try again.");
|
||||
@@ -352,12 +368,18 @@ export function PlantForm({ existingPlant, defaultContainerId, containers }: Pro
|
||||
{error && <p className="text-sm text-red-500">{error}</p>}
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<a
|
||||
href={existingPlant ? `/garden/plants/${existingPlant.id}` : "/garden"}
|
||||
className="btn btn-ghost"
|
||||
>
|
||||
Cancel
|
||||
</a>
|
||||
{onCancel ? (
|
||||
<button type="button" className="btn btn-ghost" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
href={existingPlant ? `/garden/plants/${existingPlant.id}` : "/garden"}
|
||||
className="btn btn-ghost"
|
||||
>
|
||||
Cancel
|
||||
</a>
|
||||
)}
|
||||
<button type="submit" className="btn btn-primary" disabled={isPending || uploading}>
|
||||
{isPending ? "Saving…" : existingPlant ? "Save" : "Create"}
|
||||
</button>
|
||||
|
||||
@@ -203,18 +203,21 @@ const gardenManifest: ModuleManifest = {
|
||||
label: "Add plant",
|
||||
icon: "leaf",
|
||||
url: "/garden/plants/new",
|
||||
createKey: "garden.plant",
|
||||
},
|
||||
{
|
||||
id: "garden.add-container",
|
||||
label: "Add container",
|
||||
icon: "box",
|
||||
url: "/garden/containers/new",
|
||||
createKey: "garden.container",
|
||||
},
|
||||
{
|
||||
id: "garden.log-care",
|
||||
label: "Log plant care",
|
||||
icon: "droplets",
|
||||
url: "/garden?logCare=1",
|
||||
createKey: "garden.log-care",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use server";
|
||||
|
||||
import { and, desc, eq, lte, sql } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
|
||||
@@ -93,18 +93,21 @@ const manifest: ModuleManifest = {
|
||||
label: "Add to shopping",
|
||||
icon: "shopping-cart",
|
||||
url: "/lists",
|
||||
createKey: "lists.add-shopping",
|
||||
},
|
||||
{
|
||||
id: "lists.add-task",
|
||||
label: "Add to tasks",
|
||||
icon: "list-checks",
|
||||
url: "/lists",
|
||||
createKey: "lists.add-task",
|
||||
},
|
||||
{
|
||||
id: "lists.new-list",
|
||||
label: "New list",
|
||||
icon: "list-plus",
|
||||
url: "/lists",
|
||||
createKey: "lists.new-list",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -106,6 +106,7 @@ const manifest: ModuleManifest = {
|
||||
label: "New note",
|
||||
icon: "file-plus",
|
||||
url: "/notes/new",
|
||||
createKey: "notes.note",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user