Files
Moonbase/src/components/locations/quick-setup-button.tsx
tonym ddc0a60a0c v0.18.0 — Locations Quick setup + place-based reminders (#4 groundwork)
#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>
2026-06-16 02:33:52 -05:00

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 &ldquo;Yard&rdquo;
</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>
</>
);
}