Skip to content

Commit afb559e

Browse files
committed
WEB-82 Replace native dialogs with DialogProvider, add toast notifications
1 parent fc24cbe commit afb559e

7 files changed

Lines changed: 48 additions & 23 deletions

File tree

src/app/editor/DocumentList.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { createDocumentAction, renameDocumentAction } from "../../lib/actions";
88
import { runAction } from "./runAction";
99
import { getDocumentName } from "../../lib/documents";
1010
import { ResourceCard, NewResourceCard, ActionButton, formatRelativeTime } from "./ResourceCard";
11+
import { useDialogs } from "@/components/ui/dialog-provider";
1112

1213
function NewDocumentCard() {
1314
const router = useRouter();
1415
const [isCreating, startTransition] = useTransition();
16+
const { prompt, alert } = useDialogs();
1517

1618
async function handleCreateDocument() {
17-
const name = window.prompt("Document name");
19+
const name = await prompt({ title: "Document name" });
1820

1921
if (name === null) {
2022
return;
@@ -32,7 +34,7 @@ function NewDocumentCard() {
3234
});
3335

3436
if (result.success === false) {
35-
alert(result.error);
37+
await alert(result.error);
3638
return;
3739
}
3840

@@ -93,16 +95,17 @@ export function DocumentList({ documents }: {
9395
}) {
9496
const router = useRouter();
9597
const [isPending, startTransition] = useTransition();
98+
const { prompt, alert } = useDialogs();
9699

97-
function handleRename(id: number, currentName: string) {
98-
const newName = window.prompt("Rename document", currentName);
100+
async function handleRename(id: number, currentName: string) {
101+
const newName = await prompt({ title: "Rename document", defaultValue: currentName });
99102
if (newName === null || newName.trim() === "" || newName.trim() === currentName) return;
100103
startTransition(async () => {
101104
const result = await runAction(renameDocumentAction({ id, name: newName.trim() }));
102105
if (result.success) {
103106
router.refresh();
104107
} else {
105-
alert(result.error);
108+
await alert(result.error);
106109
}
107110
});
108111
}

src/app/editor/MediaLibrary.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import { useRef, useState, useTransition } from "react";
44
import { File as FileIcon, Trash2, Copy, Pencil } from "lucide-react";
5+
import { toast } from "sonner";
56
import { uploadMediaAction, deleteMediaAction, renameMediaAction } from "../../lib/actions";
67
import type { Media } from "../../generated/prisma/client";
78
import { runAction } from "./runAction";
89
import { ResourceCard, NewResourceCard, ActionButton, formatRelativeTime } from "./ResourceCard";
10+
import { useDialogs } from "@/components/ui/dialog-provider";
911

1012
type MediaWithUrl = Media & { url: string };
1113

@@ -80,7 +82,7 @@ function MediaCard({
8082
<ActionButton onClick={() => onRename(file)} disabled={disabled} title="Rename">
8183
<Pencil className="h-3 w-3" />
8284
</ActionButton>
83-
<ActionButton onClick={() => navigator.clipboard.writeText(file.url)} title="Copy URL">
85+
<ActionButton onClick={() => { navigator.clipboard.writeText(file.url); toast.success("URL copied"); }} title="Copy URL">
8486
<Copy className="h-3 w-3" />
8587
</ActionButton>
8688
<ActionButton onClick={() => onDelete(file)} disabled={disabled} title="Delete" variant="danger">
@@ -95,6 +97,7 @@ function MediaCard({
9597
export function MediaLibrary({ files: initialFiles }: { files: MediaWithUrl[] }) {
9698
const [isPending, startTransition] = useTransition();
9799
const [files, setFiles] = useState(initialFiles);
100+
const { confirm, prompt, alert } = useDialogs();
98101

99102
function handleUpload(file: File) {
100103
const formData = new FormData();
@@ -104,32 +107,32 @@ export function MediaLibrary({ files: initialFiles }: { files: MediaWithUrl[] })
104107
if (result.success) {
105108
setFiles((prev) => [result.data, ...prev]);
106109
} else {
107-
alert(result.error);
110+
await alert(result.error);
108111
}
109112
});
110113
}
111114

112-
function handleRename(file: MediaWithUrl) {
113-
const newName = window.prompt("Rename file", file.name);
115+
async function handleRename(file: MediaWithUrl) {
116+
const newName = await prompt({ title: "Rename file", defaultValue: file.name });
114117
if (newName === null || newName.trim() === "" || newName.trim() === file.name) return;
115118
startTransition(async () => {
116119
const result = await runAction(renameMediaAction({ id: file.id, name: newName.trim() }));
117120
if (result.success) {
118121
setFiles((prev) => prev.map((f) => (f.id === file.id ? { ...f, name: newName.trim() } : f)));
119122
} else {
120-
alert(result.error);
123+
await alert(result.error);
121124
}
122125
});
123126
}
124127

125-
function handleDelete(file: MediaWithUrl) {
126-
if (!window.confirm(`Delete "${file.name}"?`)) return;
128+
async function handleDelete(file: MediaWithUrl) {
129+
if (!await confirm({ message: `Delete "${file.name}"?`, actionLabel: "Delete" })) return;
127130
startTransition(async () => {
128131
const result = await runAction(deleteMediaAction({ id: file.id }));
129132
if (result.success) {
130133
setFiles((prev) => prev.filter((f) => f.id !== file.id));
131134
} else {
132-
alert(result.error);
135+
await alert(result.error);
133136
}
134137
});
135138
}

src/app/editor/RouteTable.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
deleteRouteAction,
1010
} from "../../lib/actions";
1111
import { runAction } from "./runAction";
12+
import { useDialogs } from "@/components/ui/dialog-provider";
1213

1314
type RouteRow = {
1415
id: number;
@@ -184,6 +185,7 @@ export function RouteTable({
184185
const [isPending, startTransition] = useTransition();
185186
const [creating, setCreating] = useState(false);
186187
const [routes, setRoutes] = useState(initialRoutes);
188+
const { confirm, alert } = useDialogs();
187189

188190
function handleCreate(path: string, documentId: number) {
189191
setCreating(false);
@@ -193,7 +195,7 @@ export function RouteTable({
193195
const docName = documents.find((d) => d.id === documentId)?.name ?? "";
194196
setRoutes((prev) => [...prev, { id: result.data.routeId, path, documentId, documentName: docName }]);
195197
} else {
196-
alert(result.error);
198+
await alert(result.error);
197199
}
198200
});
199201
}
@@ -205,19 +207,19 @@ export function RouteTable({
205207
const docName = documents.find((d) => d.id === documentId)?.name ?? "";
206208
setRoutes((prev) => prev.map((r) => (r.id === id ? { ...r, path, documentId, documentName: docName } : r)));
207209
} else {
208-
alert(result.error);
210+
await alert(result.error);
209211
}
210212
});
211213
}
212214

213-
function handleDelete(route: RouteRow) {
214-
if (!window.confirm(`Delete route "${route.path}"?`)) return;
215+
async function handleDelete(route: RouteRow) {
216+
if (!await confirm({ message: `Delete route "${route.path}"?`, actionLabel: "Delete" })) return;
215217
startTransition(async () => {
216218
const result = await runAction(deleteRouteAction({ id: route.id }));
217219
if (result.success) {
218220
setRoutes((prev) => prev.filter((r) => r.id !== route.id));
219221
} else {
220-
alert(result.error);
222+
await alert(result.error);
221223
}
222224
});
223225
}

src/app/editor/[id]/ActionBarOverride.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useCallback, useRef } from "react";
44
import { ActionBar, createUsePuck } from "@puckeditor/core";
55
import type { ComponentData, Config } from "@puckeditor/core";
66
import { Copy, CopyPlus, ClipboardPaste } from "lucide-react";
7+
import { useDialogs } from "@/components/ui/dialog-provider";
78

89
const usePuck = createUsePuck();
910

@@ -99,6 +100,7 @@ export function ActionBarOverride({
99100
const dispatch = usePuck((s) => s.dispatch);
100101
const getSelectorForId = usePuck((s) => s.getSelectorForId);
101102
const isPastingRef = useRef(false);
103+
const { alert } = useDialogs();
102104

103105
const handleDuplicate = useCallback(() => {
104106
if (!selectedItem) return;
@@ -131,7 +133,7 @@ export function ActionBarOverride({
131133
// Read and validate clipboard
132134
const raw = await readFromClipboard();
133135
if (!raw) {
134-
alert("Invalid clipboard data")
136+
await alert("Invalid clipboard data");
135137
return;
136138
}
137139

@@ -175,7 +177,7 @@ export function ActionBarOverride({
175177
} finally {
176178
isPastingRef.current = false;
177179
}
178-
}, [selectedItem, config, dispatch, getSelectorForId]);
180+
}, [selectedItem, config, dispatch, getSelectorForId, alert]);
179181

180182
return (
181183
<ActionBar label={label}>

src/app/editor/[id]/SaveButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
import { createUsePuck } from "@puckeditor/core";
44
import { useTransition } from "react";
5+
import { toast } from "sonner";
56
import { useDocumentContext } from "./client";
67
import { saveVersionAction } from "../../../lib/actions";
78
import { runAction } from "../runAction";
9+
import { useDialogs } from "@/components/ui/dialog-provider";
810

911
const usePuck = createUsePuck();
1012

1113
export function SaveButton() {
1214
const data = usePuck((s) => s.appState.data);
1315
const dispatch = usePuck((s) => s.dispatch);
1416
const { documentId, addVersion } = useDocumentContext();
17+
const { alert } = useDialogs();
1518

1619
const [isSaving, startTransition] = useTransition();
1720

@@ -20,12 +23,13 @@ export function SaveButton() {
2023
const result = await runAction(saveVersionAction({ documentId, content: data }));
2124

2225
if (result.success === false) {
23-
alert(result.error);
26+
await alert(result.error);
2427
return;
2528
}
2629

2730
dispatch({ type: "setData", data });
2831
addVersion(result.data.version);
32+
toast.success("Saved");
2933
});
3034
};
3135

src/app/editor/[id]/VersionPluginContainer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,26 @@ import { useDocumentContext } from "./client";
66
import { publishVersionAction } from "../../../lib/actions";
77
import { runAction } from "../runAction";
88
import { VersionListPanel } from "./VersionListPanel";
9+
import { useDialogs } from "@/components/ui/dialog-provider";
10+
import { toast } from "sonner";
911

1012
export function VersionPluginContainer() {
1113
const router = useRouter();
1214
const { documentId, versionId, publishedVersionId, versions, setPublishedVersionId } = useDocumentContext();
1315
const [isPublishing, startTransition] = useTransition();
16+
const { alert } = useDialogs();
1417

1518
const handlePublishVersion = (targetVersionId: number) => {
1619
startTransition(async () => {
1720
const result = await runAction(publishVersionAction({ documentId, versionId: targetVersionId }));
1821

1922
if (result.success === false) {
20-
alert(result.error);
23+
await alert(result.error);
2124
return;
2225
}
2326

2427
setPublishedVersionId(targetVersionId);
28+
toast.success("Published");
2529
});
2630
};
2731

src/app/layout.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import "./styles.css";
2+
import { Toaster } from "@/components/ui/sonner";
3+
import { DialogProvider } from "@/components/ui/dialog-provider";
24

35
export default function RootLayout({
46
children,
@@ -7,7 +9,12 @@ export default function RootLayout({
79
}) {
810
return (
911
<html lang="en">
10-
<body>{children}</body>
12+
<body>
13+
<DialogProvider>
14+
{children}
15+
</DialogProvider>
16+
<Toaster />
17+
</body>
1118
</html>
1219
)
1320
}

0 commit comments

Comments
 (0)