#4 cleanup + Tony's house structure. - Task.locationId (migration): reminders can attach to a place, not just a plant/item/animal. Add one from a location's menu (e.g. clean gutters on House > Exterior); the Reminders page + tasks service surface the linked location/item/ animal. - Locations "Quick setup": paste/confirm an indented outline → creates the nested tree (find-or-create by name+parent, idempotent), pre-filled with the Moon homestead house layout. Optional "sweep existing garden areas under Yard" moves every top-level place that holds plants under a new Yard parent — the 'move all the yard stuff to a new top level' ask. - Pure outline parser (src/lib/locations/outline.ts, +4 tests); bulk setup service + /api/locations/bulk. Verified end-to-end: nested tree + paths, garden sweep, place reminder. Yard sub-bucketing deferred per Tony. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
|
|
} from "@/components/ui/dialog";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import { Wand2 } from "lucide-react";
|
|
|
|
// Pre-filled with the Moon homestead house layout — Tony just clicks Create.
|
|
const DEFAULT_OUTLINE = `House
|
|
Main floor
|
|
Entrance
|
|
Living room
|
|
Family room
|
|
Dining room
|
|
Kitchen
|
|
Pantry
|
|
Office
|
|
Bedroom
|
|
Guest room
|
|
Bathroom
|
|
Laundry room
|
|
Basement
|
|
Stairs
|
|
Laundry room
|
|
Furnace room
|
|
Workshop
|
|
Studio
|
|
Bathroom
|
|
Garage
|
|
Exterior
|
|
Gutters
|
|
Exterior lights
|
|
Roof
|
|
Outbuildings
|
|
Brown shed
|
|
Green shed
|
|
Wood shed
|
|
Yard`;
|
|
|
|
export function QuickSetupButton() {
|
|
const [open, setOpen] = useState(false);
|
|
const [outline, setOutline] = useState(DEFAULT_OUTLINE);
|
|
const [groupGarden, setGroupGarden] = useState(true);
|
|
const [busy, setBusy] = useState(false);
|
|
const router = useRouter();
|
|
const { toast } = useToast();
|
|
|
|
async function run() {
|
|
setBusy(true);
|
|
const res = await fetch("/api/locations/bulk", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ outline, groupGardenUnder: groupGarden ? "Yard" : undefined }),
|
|
});
|
|
setBusy(false);
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({}));
|
|
toast({ title: "Error", description: err.error ?? "Setup failed.", variant: "destructive" });
|
|
return;
|
|
}
|
|
const s = await res.json();
|
|
toast({
|
|
title: "Places set up!",
|
|
description: `${s.created} created${s.moved ? `, ${s.moved} garden areas grouped under Yard` : ""}.`,
|
|
});
|
|
setOpen(false);
|
|
router.refresh();
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Button size="sm" variant="outline" onClick={() => setOpen(true)}>
|
|
<Wand2 className="h-4 w-4 mr-1" /> Quick setup
|
|
</Button>
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogContent className="max-w-lg max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>Quick setup from an outline</DialogTitle>
|
|
<DialogDescription>
|
|
Each indent is a sub-location. Re-running is safe — existing places are reused, not duplicated.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="space-y-3">
|
|
<Textarea rows={14} value={outline} onChange={(e) => setOutline(e.target.value)} className="font-mono text-xs" />
|
|
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
|
<input type="checkbox" checked={groupGarden} onChange={(e) => setGroupGarden(e.target.checked)} className="accent-[hsl(var(--leaf))]" />
|
|
Move existing garden areas (places that hold plants) under “Yard”
|
|
</label>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => setOpen(false)}>Cancel</Button>
|
|
<Button onClick={run} disabled={busy || !outline.trim()}>{busy ? "Setting up…" : "Create"}</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
}
|