diff --git a/src/components/common/FormattedDescription.tsx b/src/components/common/FormattedDescription.tsx
new file mode 100644
index 0000000..d384ba7
--- /dev/null
+++ b/src/components/common/FormattedDescription.tsx
@@ -0,0 +1,77 @@
+import * as React from "react";
+import { cn } from "@/components/ui/utils";
+
+interface FormattedDescriptionProps {
+ content: string;
+ className?: string;
+}
+
+/**
+ * FormattedDescription Component
+ *
+ * Renders HTML content with proper styling for lists, tables, and other formatted content.
+ * Use this component to display descriptions that may contain HTML formatting.
+ */
+export function FormattedDescription({ content, className }: FormattedDescriptionProps) {
+ const processedContent = React.useMemo(() => {
+ if (!content) return '';
+
+ // Wrap tables that aren't already wrapped in a scrollable container using regex
+ // Match
tags that aren't already inside a .table-wrapper
+ let processed = content;
+
+ // Pattern to match table tags that aren't already wrapped
+ const tablePattern = /]*>[\s\S]*?<\/table>/gi;
+
+ processed = processed.replace(tablePattern, (match) => {
+ // Check if this table is already wrapped
+ if (match.includes('table-wrapper')) {
+ return match;
+ }
+
+ // Wrap the table in a scrollable container
+ return `${match}
`;
+ });
+
+ return processed;
+ }, [content]);
+
+ if (!content) return null;
+
+ return (
+
+ );
+}
+
diff --git a/src/components/ui/ckeditor-wrapper.tsx b/src/components/ui/ckeditor-wrapper.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/ui/rich-text-editor.tsx b/src/components/ui/rich-text-editor.tsx
new file mode 100644
index 0000000..3f4b470
--- /dev/null
+++ b/src/components/ui/rich-text-editor.tsx
@@ -0,0 +1,563 @@
+import * as React from "react";
+import { cn } from "./utils";
+import { Button } from "./button";
+import { Bold, Italic, Underline, List, ListOrdered, Heading1, Heading2, Heading3, AlignLeft, AlignCenter, AlignRight } from "lucide-react";
+
+interface RichTextEditorProps extends Omit, 'onChange'> {
+ value: string;
+ onChange: (value: string) => void;
+ placeholder?: string;
+ className?: string;
+ minHeight?: string;
+}
+
+/**
+ * RichTextEditor Component
+ *
+ * Preserves formatting (lists, tables, etc.) when pasting content.
+ * Uses contentEditable div to maintain HTML structure.
+ */
+export function RichTextEditor({
+ value,
+ onChange,
+ placeholder = "Enter text...",
+ className,
+ minHeight = "120px",
+ ...props
+}: RichTextEditorProps) {
+ const editorRef = React.useRef(null);
+ const [isFocused, setIsFocused] = React.useState(false);
+ const [activeFormats, setActiveFormats] = React.useState>(new Set());
+
+ // Sync external value to editor
+ React.useEffect(() => {
+ if (editorRef.current && editorRef.current.innerHTML !== value) {
+ // Only update if the value actually changed externally
+ const currentValue = editorRef.current.innerHTML;
+ if (currentValue !== value) {
+ editorRef.current.innerHTML = value || '';
+ }
+ }
+ }, [value]);
+
+ // Clean HTML from Word/Office documents - remove style tags, comments, and metadata
+ const cleanWordHTML = React.useCallback((html: string): string => {
+ // Remove HTML comments (like Word style definitions)
+ html = html.replace(//g, '');
+
+ // Remove style tags (Word CSS)
+ html = html.replace(/