- feat: order confirmation PDF generation with VAT support - feat: order confirmation modal with custom item editing - fix: attendance negative duration clamping and switchProject timing - fix: Quill editor locked to Tahoma 14px, PDF heading sizes - fix: invoice/offer PDF font consistency (Tahoma enforcement) - fix: invoice alert cron improvements - fix: NAS financials manager edge cases - refactor: numbering service with unique sequence constraints Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
124 lines
2.3 KiB
TypeScript
124 lines
2.3 KiB
TypeScript
import { useMemo, useRef, useCallback, useEffect } from "react";
|
|
import ReactQuill from "react-quill-new";
|
|
import "react-quill-new/dist/quill.snow.css";
|
|
|
|
const COLORS = [
|
|
"#000000",
|
|
"#1a1a1a",
|
|
"#333333",
|
|
"#555555",
|
|
"#777777",
|
|
"#999999",
|
|
"#bbbbbb",
|
|
"#dddddd",
|
|
"#ffffff",
|
|
"#de3a3a",
|
|
"#e57373",
|
|
"#c62828",
|
|
"#1565c0",
|
|
"#42a5f5",
|
|
"#0d47a1",
|
|
"#2e7d32",
|
|
"#66bb6a",
|
|
"#1b5e20",
|
|
"#f57f17",
|
|
"#ffca28",
|
|
"#e65100",
|
|
"#6a1b9a",
|
|
"#ab47bc",
|
|
"#4a148c",
|
|
"#00695c",
|
|
"#26a69a",
|
|
"#004d40",
|
|
"#37474f",
|
|
"#78909c",
|
|
"#263238",
|
|
];
|
|
|
|
const TOOLBAR = [
|
|
["bold", "italic", "underline", "strike"],
|
|
[{ color: COLORS }, { background: COLORS }],
|
|
[{ list: "ordered" }, { list: "bullet" }],
|
|
[{ indent: "-1" }, { indent: "+1" }],
|
|
[{ align: [] }],
|
|
["link"],
|
|
["clean"],
|
|
];
|
|
|
|
const FORMATS = [
|
|
"bold",
|
|
"italic",
|
|
"underline",
|
|
"strike",
|
|
"color",
|
|
"background",
|
|
"list",
|
|
"indent",
|
|
"align",
|
|
"link",
|
|
];
|
|
|
|
interface RichEditorProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
placeholder?: string;
|
|
minHeight?: string;
|
|
readOnly?: boolean;
|
|
}
|
|
|
|
export default function RichEditor({
|
|
value,
|
|
onChange,
|
|
placeholder = "Obsah...",
|
|
minHeight = "120px",
|
|
readOnly = false,
|
|
}: RichEditorProps) {
|
|
const quillRef = useRef<ReactQuill>(null);
|
|
const lastValueRef = useRef(value);
|
|
|
|
const modules = useMemo(
|
|
() => ({
|
|
toolbar: readOnly ? false : TOOLBAR,
|
|
clipboard: {
|
|
matchVisual: false,
|
|
},
|
|
}),
|
|
[readOnly],
|
|
);
|
|
|
|
const handleChange = useCallback(
|
|
(content: string, _delta: any, source: string) => {
|
|
if (source !== "user") return;
|
|
if (content === lastValueRef.current) return;
|
|
lastValueRef.current = content;
|
|
onChange(content);
|
|
},
|
|
[onChange],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!quillRef.current) return;
|
|
const editor = quillRef.current.getEditor();
|
|
editor.format("font", "tahoma");
|
|
editor.format("size", "14px");
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className="admin-rich-editor"
|
|
style={{ "--re-min-height": minHeight } as React.CSSProperties}
|
|
>
|
|
<ReactQuill
|
|
ref={quillRef}
|
|
theme="snow"
|
|
value={value || ""}
|
|
onChange={handleChange}
|
|
modules={modules}
|
|
formats={FORMATS}
|
|
placeholder={placeholder}
|
|
readOnly={readOnly}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|