Skip to content

Commit 7df9632

Browse files
fix: [ci-fix] address review feedback — error state, Sentry, cast comment, imports
- Add 'error' save status with user-visible indicator and auto-reset - Report Lexical errors and save failures to Sentry instead of console - Add justification comment for unavoidable as-cast on jsonb column - Replace React.ReactNode and React.KeyboardEvent with explicit imports Co-authored-by: Ona <no-reply@ona.com>
1 parent a846f6d commit 7df9632

4 files changed

Lines changed: 20 additions & 10 deletions

File tree

src/app/(app)/[workspaceSlug]/[pageId]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export default async function PageView({
3333
notFound();
3434
}
3535

36+
// Supabase types jsonb columns as Json | null; narrow to Lexical's serialized state
3637
const initialContent = page.content as SerializedEditorState | null;
3738

3839
return (

src/components/editor/editor.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { CodeNode, CodeHighlightNode } from "@lexical/code";
1818
import { LinkNode } from "@lexical/link";
1919
import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode";
2020
import type { EditorState, SerializedEditorState } from "lexical";
21+
import * as Sentry from "@sentry/nextjs";
2122
import { editorTheme } from "@/components/editor/theme";
2223
import { SlashCommandPlugin } from "@/components/editor/slash-command-plugin";
2324
import { FloatingToolbarPlugin } from "@/components/editor/floating-toolbar-plugin";
@@ -42,9 +43,9 @@ function validateUrl(url: string): boolean {
4243
}
4344

4445
export function Editor({ pageId, initialContent }: EditorProps) {
45-
const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved">(
46-
"idle"
47-
);
46+
const [saveStatus, setSaveStatus] = useState<
47+
"idle" | "saving" | "saved" | "error"
48+
>("idle");
4849
const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
4950
const lastSavedRef = useRef<string>(
5051
initialContent ? JSON.stringify(initialContent) : ""
@@ -82,7 +83,8 @@ export function Editor({ pageId, initialContent }: EditorProps) {
8283
lastSavedRef.current = serialized;
8384
setSaveStatus("saved");
8485
} else {
85-
setSaveStatus("idle");
86+
Sentry.captureException(error);
87+
setSaveStatus("error");
8688
}
8789
}, SAVE_DEBOUNCE_MS);
8890
},
@@ -97,12 +99,16 @@ export function Editor({ pageId, initialContent }: EditorProps) {
9799
};
98100
}, []);
99101

100-
// Reset "saved" indicator after 2 seconds
102+
// Reset "saved" indicator after 2 seconds; retry save on error after 3 seconds
101103
useEffect(() => {
102104
if (saveStatus === "saved") {
103105
const timer = setTimeout(() => setSaveStatus("idle"), 2000);
104106
return () => clearTimeout(timer);
105107
}
108+
if (saveStatus === "error") {
109+
const timer = setTimeout(() => setSaveStatus("idle"), 5000);
110+
return () => clearTimeout(timer);
111+
}
106112
}, [saveStatus]);
107113

108114
const initialConfig = {
@@ -119,7 +125,7 @@ export function Editor({ pageId, initialContent }: EditorProps) {
119125
HorizontalRuleNode,
120126
],
121127
onError: (error: Error) => {
122-
console.error("Lexical error:", error);
128+
Sentry.captureException(error);
123129
},
124130
editorState: initialContent
125131
? JSON.stringify(initialContent)
@@ -167,6 +173,9 @@ export function Editor({ pageId, initialContent }: EditorProps) {
167173
<div className="mt-2 h-5 text-xs text-muted-foreground">
168174
{saveStatus === "saving" && "Saving..."}
169175
{saveStatus === "saved" && "Saved"}
176+
{saveStatus === "error" && (
177+
<span className="text-destructive">Save failed</span>
178+
)}
170179
</div>
171180
</div>
172181
);

src/components/editor/floating-link-editor-plugin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import type { JSX } from "react";
3+
import type { JSX, KeyboardEvent as ReactKeyboardEvent } from "react";
44
import { useCallback, useEffect, useRef, useState } from "react";
55
import { createPortal } from "react-dom";
66
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
@@ -144,7 +144,7 @@ export function FloatingLinkEditorPlugin({
144144
}, [editor]);
145145

146146
const handleKeyDown = useCallback(
147-
(e: React.KeyboardEvent) => {
147+
(e: ReactKeyboardEvent) => {
148148
if (e.key === "Enter") {
149149
e.preventDefault();
150150
handleSave();

src/components/editor/floating-toolbar-plugin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import type { JSX } from "react";
3+
import type { JSX, ReactNode } from "react";
44
import { useCallback, useEffect, useRef, useState } from "react";
55
import { createPortal } from "react-dom";
66
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
@@ -219,7 +219,7 @@ function ToolbarButton({
219219
active: boolean;
220220
onClick: () => void;
221221
label: string;
222-
children: React.ReactNode;
222+
children: ReactNode;
223223
}) {
224224
return (
225225
<button

0 commit comments

Comments
 (0)