fix: use RichEditor with readOnly prop instead of raw HTML for locked/invalidated offers
RichEditor now supports readOnly prop — hides toolbar and disables editing via ReactQuill's built-in readOnly. Content renders with proper Quill CSS (list margins, indentation, fonts) instead of broken browser defaults from dangerouslySetInnerHTML. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,23 +64,25 @@ interface RichEditorProps {
|
|||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
minHeight?: string
|
minHeight?: string
|
||||||
|
readOnly?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RichEditor({
|
export default function RichEditor({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
placeholder = 'Obsah...',
|
placeholder = 'Obsah...',
|
||||||
minHeight = '120px'
|
minHeight = '120px',
|
||||||
|
readOnly = false,
|
||||||
}: RichEditorProps) {
|
}: RichEditorProps) {
|
||||||
const quillRef = useRef<ReactQuill>(null)
|
const quillRef = useRef<ReactQuill>(null)
|
||||||
const lastValueRef = useRef(value)
|
const lastValueRef = useRef(value)
|
||||||
|
|
||||||
const modules = useMemo(() => ({
|
const modules = useMemo(() => ({
|
||||||
toolbar: TOOLBAR,
|
toolbar: readOnly ? false : TOOLBAR,
|
||||||
clipboard: {
|
clipboard: {
|
||||||
matchVisual: false,
|
matchVisual: false,
|
||||||
},
|
},
|
||||||
}), [])
|
}), [readOnly])
|
||||||
|
|
||||||
const handleChange = useCallback((content: string, _delta: any, source: string) => {
|
const handleChange = useCallback((content: string, _delta: any, source: string) => {
|
||||||
if (source !== 'user') return
|
if (source !== 'user') return
|
||||||
@@ -99,6 +101,7 @@ export default function RichEditor({
|
|||||||
modules={modules}
|
modules={modules}
|
||||||
formats={FORMATS}
|
formats={FORMATS}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1097,22 +1097,13 @@ export default function OfferDetail() {
|
|||||||
|
|
||||||
<div style={{ marginTop: '0.5rem' }}>
|
<div style={{ marginTop: '0.5rem' }}>
|
||||||
<label className="admin-form-label">Obsah</label>
|
<label className="admin-form-label">Obsah</label>
|
||||||
{(isInvalidated || isLockedByOther) ? (
|
|
||||||
<div className="rich-editor">
|
|
||||||
<div
|
|
||||||
className="ql-editor"
|
|
||||||
style={{ minHeight: '80px', background: 'var(--bg-primary)', border: '1px solid var(--border-color)', borderRadius: 'var(--border-radius-sm)', cursor: 'default', overflowWrap: 'anywhere', wordBreak: 'break-word' }}
|
|
||||||
dangerouslySetInnerHTML={{ __html: section.content || '<em style="color: var(--text-tertiary)">Prázdný obsah</em>' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<RichEditor
|
<RichEditor
|
||||||
value={section.content}
|
value={section.content}
|
||||||
onChange={(val) => setSections(prev => prev.map((s, i) => i === idx ? { ...s, content: val } : s))}
|
onChange={(val) => setSections(prev => prev.map((s, i) => i === idx ? { ...s, content: val } : s))}
|
||||||
placeholder="Obsah sekce..."
|
placeholder="Obsah sekce..."
|
||||||
minHeight="120px"
|
minHeight="120px"
|
||||||
|
readOnly={isInvalidated || isLockedByOther}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user