From f97d6dc9d2c261c487e63e5f4ed546bf26f896e1 Mon Sep 17 00:00:00 2001 From: Gerhard Scheikl Date: Sat, 9 May 2026 16:11:49 +0200 Subject: [PATCH] feat(email): text colour menu in WYSIWYG (LinumIQ blue + presets) --- app/components/RichTextEditor.tsx | 60 ++++++++++++++++++++++++++ app/services/invoice/emailTemplates.ts | 20 ++++----- package-lock.json | 28 ++++++++++++ package.json | 2 + 4 files changed, 100 insertions(+), 10 deletions(-) diff --git a/app/components/RichTextEditor.tsx b/app/components/RichTextEditor.tsx index fc2c6f8..c9f9766 100644 --- a/app/components/RichTextEditor.tsx +++ b/app/components/RichTextEditor.tsx @@ -2,8 +2,19 @@ import { useEditor, EditorContent } from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import Link from "@tiptap/extension-link"; import Image from "@tiptap/extension-image"; +import { TextStyle } from "@tiptap/extension-text-style"; +import { Color } from "@tiptap/extension-color"; import { useEffect, useState } from "react"; +const PRESET_COLORS = [ + { name: "Default", value: null as string | null }, + { name: "LinumIQ blue", value: "#0883DA" }, + { name: "Black", value: "#000000" }, + { name: "Grey", value: "#6d7175" }, + { name: "Red", value: "#d72c0d" }, + { name: "Green", value: "#1a7e3a" }, +]; + interface RichTextEditorProps { /** Hidden form field name; the rendered HTML is mirrored into it. */ name: string; @@ -70,6 +81,8 @@ export function RichTextEditor({ }; }, }).configure({ inline: false, allowBase64: true }), + TextStyle, + Color, ], content: swapCidToLogo(defaultValue || "

", logoDataUrl), editorProps: { @@ -190,6 +203,8 @@ export function RichTextEditor({ title="Insert/remove link" /> + + editor?.chain().focus().undo().run()} active={false} @@ -276,6 +291,51 @@ function Sep() { return ; } +function ColorMenu({ editor }: { editor: ReturnType | null }) { + if (!editor) return null; + return ( + + {PRESET_COLORS.map((c) => ( + + ))} + + ); +} + /** * Replaces `src="cid:invoice-logo"` with the supplied URL so the editor * can display the actual logo. Done as a string replace because TipTap diff --git a/app/services/invoice/emailTemplates.ts b/app/services/invoice/emailTemplates.ts index b3a00b4..4d1f8e0 100644 --- a/app/services/invoice/emailTemplates.ts +++ b/app/services/invoice/emailTemplates.ts @@ -9,8 +9,8 @@ */ const DE_HTML = `\ -

{{companyName}}

-

Danke fΓΌr deinen Einkauf!

+

{{companyName}}

+

Danke fΓΌr deinen Einkauf!

Die Rechnung befindet sich im Anhang.

@@ -22,14 +22,14 @@ Besten Dank!

{{companyName}}

-

-βœ‰ Kontakt
-🌐 {{shopWebsite}} +

+βœ‰ Kontakt
+🌐 {{shopWebsite}}

`; const EN_HTML = `\ -

{{companyName}}

-

Thank you for your purchase!

+

{{companyName}}

+

Thank you for your purchase!

Please find the invoice attached.

@@ -41,9 +41,9 @@ Thanks a lot!

{{companyName}}

-

-βœ‰ Contact
-🌐 {{shopWebsite}} +

+βœ‰ Contact
+🌐 {{shopWebsite}}

`; export const DEFAULT_EMAIL_SUBJECT_DE = "Rechnung {{invoiceNumber}} – {{companyName}}"; diff --git a/package-lock.json b/package-lock.json index a4031f8..199471e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,10 @@ "@shopify/app-bridge-react": "^4.2.4", "@shopify/shopify-app-react-router": "^1.1.0", "@shopify/shopify-app-session-storage-prisma": "^8.0.0", + "@tiptap/extension-color": "^3.23.1", "@tiptap/extension-image": "^3.23.1", "@tiptap/extension-link": "^3.23.1", + "@tiptap/extension-text-style": "^3.23.1", "@tiptap/pm": "^3.23.1", "@tiptap/react": "^3.23.1", "@tiptap/starter-kit": "^3.23.1", @@ -4262,6 +4264,19 @@ "@tiptap/pm": "3.23.1" } }, + "node_modules/@tiptap/extension-color": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-3.23.1.tgz", + "integrity": "sha512-OYk/fT3h8Bz4B6GUVTQDvKGpPnpI5d6QHkuqjVhdFsgH3oo58PdLE1TdIGgeavuYPLaFxgBtEXmm3oTY9jPWxw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-text-style": "3.23.1" + } + }, "node_modules/@tiptap/extension-document": { "version": "3.23.1", "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.23.1.tgz", @@ -4492,6 +4507,19 @@ "@tiptap/core": "3.23.1" } }, + "node_modules/@tiptap/extension-text-style": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.23.1.tgz", + "integrity": "sha512-q3GQQo+lBhrtNkqdbhYWnv/byG/RYAxVnNhYPQMubRzavGdXBU8NhpJ/47YYjPimG1sahzcs2aqy7amVd8ri/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, "node_modules/@tiptap/extension-underline": { "version": "3.23.1", "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.23.1.tgz", diff --git a/package.json b/package.json index 11cfb07..387aaf3 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,10 @@ "@shopify/app-bridge-react": "^4.2.4", "@shopify/shopify-app-react-router": "^1.1.0", "@shopify/shopify-app-session-storage-prisma": "^8.0.0", + "@tiptap/extension-color": "^3.23.1", "@tiptap/extension-image": "^3.23.1", "@tiptap/extension-link": "^3.23.1", + "@tiptap/extension-text-style": "^3.23.1", "@tiptap/pm": "^3.23.1", "@tiptap/react": "^3.23.1", "@tiptap/starter-kit": "^3.23.1",