refactor(guides): replace NewGuide with unified GuideForm
- Merge useNewGuide and NewGuide into useGuideForm and GuideForm to handle both create and edit flows - Add /edit-guide/:id route pointing to GuideForm - Replace Edit button in Guide view with Link to edit route - Add defaultValue to Frame title input for edit pre-population - Delete NewGuide.tsx, NewGuide.css, useNewGuide.ts
This commit is contained in:
parent
4dd8ce8b17
commit
ea6adc236e
6 changed files with 79 additions and 58 deletions
|
|
@ -52,6 +52,7 @@ export default function Frame({
|
|||
Title
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={frame.title}
|
||||
onChange={(e) => updateTitle(frame.id, e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -7,19 +7,36 @@ import {
|
|||
import { useIDB } from "../../../lib/contexts/dbinit.context";
|
||||
import { useGuides } from "../../../lib/contexts/guide.context";
|
||||
|
||||
export default function useNewGuide() {
|
||||
const { db } = useIDB();
|
||||
const { setGuides } = useGuides();
|
||||
|
||||
const [guide, setGuide] = useState<GuideType>({
|
||||
function makeEmptyGuide(): GuideType {
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
title: "",
|
||||
description: "",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: undefined,
|
||||
});
|
||||
const [frames, setFrames] = useState<FrameType[]>([]);
|
||||
};
|
||||
}
|
||||
|
||||
export default function useGuideForm(initial?: CompiledGuideType) {
|
||||
const { db } = useIDB();
|
||||
const { setGuides } = useGuides();
|
||||
const isEditing = Boolean(initial);
|
||||
|
||||
const [guide, setGuide] = useState<GuideType>(
|
||||
initial
|
||||
? {
|
||||
id: initial.id,
|
||||
title: initial.title,
|
||||
description: initial.description,
|
||||
createdAt: initial.createdAt,
|
||||
updatedAt: initial.updatedAt,
|
||||
deletedAt: initial.deletedAt,
|
||||
}
|
||||
: makeEmptyGuide(),
|
||||
);
|
||||
|
||||
const [frames, setFrames] = useState<FrameType[]>(initial?.frames ?? []);
|
||||
|
||||
const handleUpdateTitle = (title: string) => {
|
||||
setGuide((prev) => ({ ...prev, title }));
|
||||
|
|
@ -32,20 +49,16 @@ export default function useNewGuide() {
|
|||
const handleAddFrames = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newFiles = e.target.files;
|
||||
if (!newFiles) return;
|
||||
|
||||
const newFilesArray = Array.from(newFiles);
|
||||
|
||||
const newFrameObjects = newFilesArray.map((file) => {
|
||||
const newFrameObjects = Array.from(newFiles).map((file) => {
|
||||
const frameId = crypto.randomUUID();
|
||||
return {
|
||||
id: frameId,
|
||||
file: file,
|
||||
file,
|
||||
title: "",
|
||||
guideId: guide.id,
|
||||
steps: [{ id: crypto.randomUUID(), text: "", frameId: frameId }],
|
||||
steps: [{ id: crypto.randomUUID(), text: "", frameId }],
|
||||
};
|
||||
});
|
||||
|
||||
setFrames((prev) => [...prev, ...newFrameObjects]);
|
||||
};
|
||||
|
||||
|
|
@ -76,7 +89,7 @@ export default function useNewGuide() {
|
|||
...frame,
|
||||
steps: [
|
||||
...frame.steps,
|
||||
{ id: crypto.randomUUID(), text: "", frameId: frameId },
|
||||
{ id: crypto.randomUUID(), text: "", frameId },
|
||||
],
|
||||
}
|
||||
: frame,
|
||||
|
|
@ -86,58 +99,45 @@ export default function useNewGuide() {
|
|||
|
||||
const handleUpdateStep = (stepId: string, text: string) => {
|
||||
setFrames((prev) =>
|
||||
prev.map((frame) =>
|
||||
frame.steps
|
||||
? {
|
||||
prev.map((frame) => ({
|
||||
...frame,
|
||||
steps: frame.steps.map((step) =>
|
||||
step.id === stepId ? { ...step, text } : step,
|
||||
),
|
||||
}
|
||||
: frame,
|
||||
),
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
||||
const handleRemoveStep = (stepId: string) => {
|
||||
setFrames((prev) =>
|
||||
prev.map((frame) =>
|
||||
frame.steps
|
||||
? {
|
||||
prev.map((frame) => ({
|
||||
...frame,
|
||||
steps: frame.steps.filter((step) => step.id !== stepId),
|
||||
}
|
||||
: frame,
|
||||
),
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
||||
const handleSaveGuide = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
||||
const handleSaveGuide = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (!db) return;
|
||||
|
||||
const compiledGuide: CompiledGuideType = {
|
||||
id: guide.id,
|
||||
title: guide.title,
|
||||
description: guide.description,
|
||||
createdAt: guide.createdAt,
|
||||
updatedAt: guide.updatedAt,
|
||||
deletedAt: guide.deletedAt,
|
||||
...guide,
|
||||
updatedAt: new Date(),
|
||||
frames,
|
||||
};
|
||||
|
||||
if (!db) return;
|
||||
await db.put("guides", compiledGuide);
|
||||
setGuide({
|
||||
id: crypto.randomUUID(),
|
||||
title: "",
|
||||
description: "",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: undefined,
|
||||
});
|
||||
setFrames([]);
|
||||
|
||||
if (isEditing) {
|
||||
setGuides((prev) =>
|
||||
prev.map((g) => (g.id === compiledGuide.id ? compiledGuide : g)),
|
||||
);
|
||||
} else {
|
||||
setGuides((prev) => [compiledGuide, ...prev]);
|
||||
setGuide(makeEmptyGuide());
|
||||
setFrames([]);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
@ -145,6 +145,7 @@ export default function useNewGuide() {
|
|||
setGuide,
|
||||
frames,
|
||||
setFrames,
|
||||
isEditing,
|
||||
handleUpdateTitle,
|
||||
handleUpdateDescription,
|
||||
handleAddFrames,
|
||||
|
|
@ -157,3 +158,7 @@ export default function useNewGuide() {
|
|||
handleSaveGuide,
|
||||
};
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Figure out how to use this hook for both editing and creating. */
|
||||
/* */
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { useParams } from "react-router";
|
||||
import { useGuides } from "../../../lib/contexts/guide.context";
|
||||
import useGuide from "../hooks/useGuide";
|
||||
import { Link } from "react-router";
|
||||
|
||||
export default function Guide() {
|
||||
const { id } = useParams();
|
||||
|
|
@ -31,7 +32,7 @@ export default function Guide() {
|
|||
</div>
|
||||
|
||||
<button onClick={() => deleteGuide(guide.id)}>Delete</button>
|
||||
<button>Edit</button>
|
||||
<Link to={`/edit-guide/${guide.id}`}>Edit</Link>
|
||||
<button onClick={exportGuide}>Export PDF</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
import "./NewGuide.css";
|
||||
import "./GuideForm.css";
|
||||
import FileUpload from "../components/FileUpload";
|
||||
import useNewGuide from "../hooks/useNewGuide";
|
||||
import useGuideForm from "../hooks/useGuideForm";
|
||||
import Frame from "../components/Frame";
|
||||
import { useParams } from "react-router";
|
||||
import { useGuides } from "../../../lib/contexts/guide.context";
|
||||
|
||||
export default function GuideForm() {
|
||||
const { id } = useParams();
|
||||
const { guides } = useGuides();
|
||||
const g = guides.find((g) => g.id === id);
|
||||
|
||||
export default function NewGuide() {
|
||||
const {
|
||||
guide,
|
||||
frames,
|
||||
handleUpdateTitle,
|
||||
handleUpdateDescription,
|
||||
|
|
@ -16,7 +23,8 @@ export default function NewGuide() {
|
|||
handleRemoveStep,
|
||||
handleUpdateStep,
|
||||
handleSaveGuide,
|
||||
} = useNewGuide();
|
||||
isEditing,
|
||||
} = useGuideForm(g);
|
||||
|
||||
return (
|
||||
<div className="new-guide">
|
||||
|
|
@ -26,6 +34,7 @@ export default function NewGuide() {
|
|||
<input
|
||||
type="text"
|
||||
name="title"
|
||||
defaultValue={guide?.title}
|
||||
onChange={(e) => handleUpdateTitle(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
|
@ -33,6 +42,7 @@ export default function NewGuide() {
|
|||
<span>Description</span>
|
||||
<textarea
|
||||
name="description"
|
||||
defaultValue={guide?.description}
|
||||
onChange={(e) => handleUpdateDescription(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createBrowserRouter } from "react-router";
|
||||
import App from "./App.tsx";
|
||||
import NewGuide from "./features/guides/routes/NewGuide.tsx";
|
||||
import GuideForm from "./features/guides/routes/GuideForm.tsx";
|
||||
import NotFound from "./components/not-found/NotFound.tsx";
|
||||
import Guide from "./features/guides/routes/Guide.tsx";
|
||||
|
||||
|
|
@ -11,12 +11,16 @@ export const router = createBrowserRouter([
|
|||
children: [
|
||||
{
|
||||
path: "/new-guide",
|
||||
Component: NewGuide,
|
||||
Component: GuideForm,
|
||||
},
|
||||
{
|
||||
path: "/guide/:id",
|
||||
Component: Guide,
|
||||
},
|
||||
{
|
||||
path: "/edit-guide/:id",
|
||||
Component: GuideForm,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue