From ee6c192ab9d2187aa100498f247a52b949a7e939 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 3 Mar 2026 18:24:55 +0200 Subject: [PATCH] chore(spreadsheet): create new note type --- apps/client/src/entities/fnote.ts | 2 +- apps/client/src/services/note_types.ts | 27 ++++++++++--------- .../src/translations/en/translation.json | 3 ++- apps/client/src/widgets/note_types.tsx | 6 +++++ .../src/widgets/type_widgets/Spreadsheet.tsx | 8 ++++++ apps/server/src/services/note_types.ts | 3 ++- packages/commons/src/lib/notes.ts | 3 ++- packages/commons/src/lib/rows.ts | 3 ++- 8 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 apps/client/src/widgets/type_widgets/Spreadsheet.tsx diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index 07fc60ca31..4082671b87 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -18,7 +18,7 @@ const RELATION = "relation"; * end user. Those types should be used only for checking against, they are * not for direct use. */ -export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap"; +export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "spreadsheet"; export interface NotePathRecord { isArchived: boolean; diff --git a/apps/client/src/services/note_types.ts b/apps/client/src/services/note_types.ts index 48055c0548..7f28f01f69 100644 --- a/apps/client/src/services/note_types.ts +++ b/apps/client/src/services/note_types.ts @@ -1,9 +1,9 @@ -import { t } from "./i18n.js"; -import froca from "./froca.js"; -import server from "./server.js"; -import type { MenuCommandItem, MenuItem, MenuItemBadge, MenuSeparatorItem } from "../menus/context_menu.js"; import type { NoteType } from "../entities/fnote.js"; +import type { MenuCommandItem, MenuItem, MenuItemBadge, MenuSeparatorItem } from "../menus/context_menu.js"; import type { TreeCommandNames } from "../menus/tree_context_menu.js"; +import froca from "./froca.js"; +import { t } from "./i18n.js"; +import server from "./server.js"; export interface NoteTypeMapping { type: NoteType; @@ -26,6 +26,7 @@ export const NOTE_TYPES: NoteTypeMapping[] = [ // The default note type (always the first item) { type: "text", mime: "text/html", title: t("note_types.text"), icon: "bx-note" }, + { type: "spreadsheet", mime: "application/json", title: t("note_types.spreadsheet"), icon: "bx-table" }, // Text notes group { type: "book", mime: "", title: t("note_types.book"), icon: "bx-book" }, @@ -96,9 +97,9 @@ function getBlankNoteTypes(command?: TreeCommandNames): MenuItem = { title: templateNote.title, uiIcon: templateNote.getIcon(), - command: command, + command, type: templateNote.type, templateNoteId: templateNote.noteId }; @@ -159,7 +160,7 @@ async function getBuiltInTemplates(title: string | null, command: TreeCommandNam const items: MenuItem[] = []; if (title) { items.push({ - title: title, + title, kind: "header" }); } else { @@ -175,7 +176,7 @@ async function getBuiltInTemplates(title: string | null, command: TreeCommandNam const item: MenuItem = { title: templateNote.title, uiIcon: templateNote.getIcon(), - command: command, + command, type: templateNote.type, templateNoteId: templateNote.noteId }; @@ -193,7 +194,7 @@ async function isNewTemplate(templateNoteId) { if (rootCreationDate === undefined) { // Retrieve the root note creation date try { - let rootNoteInfo: any = await server.get("notes/root"); + const rootNoteInfo: any = await server.get("notes/root"); if ("dateCreated" in rootNoteInfo) { rootCreationDate = new Date(rootNoteInfo.dateCreated); } @@ -208,7 +209,7 @@ async function isNewTemplate(templateNoteId) { if (creationDate === undefined) { // The creation date isn't available in the cache, try to retrieve it from the server try { - const noteInfo: any = await server.get("notes/" + templateNoteId); + const noteInfo: any = await server.get(`notes/${ templateNoteId}`); if ("dateCreated" in noteInfo) { creationDate = new Date(noteInfo.dateCreated); creationDateCache.set(templateNoteId, creationDate); @@ -230,9 +231,9 @@ async function isNewTemplate(templateNoteId) { const age = (new Date().getTime() - creationDate.getTime()) / DAY_LENGTH; // Return true if the template is at most NEW_TEMPLATE_MAX_AGE days old return (age <= NEW_TEMPLATE_MAX_AGE); - } else { - return false; } + return false; + } export default { diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 4bb4c11dd6..2e228a9825 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1582,7 +1582,8 @@ "ai-chat": "AI Chat", "task-list": "Task List", "new-feature": "New", - "collections": "Collections" + "collections": "Collections", + "spreadsheet": "Spreadsheet" }, "protect_note": { "toggle-on": "Protect the note", diff --git a/apps/client/src/widgets/note_types.tsx b/apps/client/src/widgets/note_types.tsx index b5f4226582..687bfdfe9c 100644 --- a/apps/client/src/widgets/note_types.tsx +++ b/apps/client/src/widgets/note_types.tsx @@ -141,5 +141,11 @@ export const TYPE_MAPPINGS: Record = { view: () => import("./type_widgets/SqlConsole"), className: "sql-console-widget-container", isFullHeight: true + }, + spreadsheet: { + view: () => import("./type_widgets/Spreadsheet"), + className: "note-detail-spreadsheet", + printable: true, + isFullHeight: true } }; diff --git a/apps/client/src/widgets/type_widgets/Spreadsheet.tsx b/apps/client/src/widgets/type_widgets/Spreadsheet.tsx new file mode 100644 index 0000000000..d4550fe790 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/Spreadsheet.tsx @@ -0,0 +1,8 @@ +export default function Spreadsheet() { + return ( +
+

This is a placeholder for the spreadsheet note type.

+

It will be implemented in a future release.

+
+ ); +} diff --git a/apps/server/src/services/note_types.ts b/apps/server/src/services/note_types.ts index 04f14f3756..bc0574dc83 100644 --- a/apps/server/src/services/note_types.ts +++ b/apps/server/src/services/note_types.ts @@ -14,7 +14,8 @@ const noteTypes = [ { type: "launcher", defaultMime: "" }, { type: "doc", defaultMime: "" }, { type: "contentWidget", defaultMime: "" }, - { type: "mindMap", defaultMime: "application/json" } + { type: "mindMap", defaultMime: "application/json" }, + { type: "spreadsheet", defaultMime: "application/json" } ]; function getDefaultMimeForNoteType(typeName: string) { diff --git a/packages/commons/src/lib/notes.ts b/packages/commons/src/lib/notes.ts index f4c16680c5..0a77c5b2fb 100644 --- a/packages/commons/src/lib/notes.ts +++ b/packages/commons/src/lib/notes.ts @@ -20,7 +20,8 @@ export const NOTE_TYPE_ICONS = { launcher: "bx bx-link", doc: "bx bxs-file-doc", contentWidget: "bx bxs-widget", - mindMap: "bx bx-sitemap" + mindMap: "bx bx-sitemap", + spreadsheet: "bx bx-table" }; const FILE_MIME_MAPPINGS = { diff --git a/packages/commons/src/lib/rows.ts b/packages/commons/src/lib/rows.ts index eeae98a8c5..e35d10e05f 100644 --- a/packages/commons/src/lib/rows.ts +++ b/packages/commons/src/lib/rows.ts @@ -119,7 +119,8 @@ export const ALLOWED_NOTE_TYPES = [ "book", "webView", "code", - "mindMap" + "mindMap", + "spreadsheet" ] as const; export type NoteType = (typeof ALLOWED_NOTE_TYPES)[number];