Skip to content

Commit b40c81a

Browse files
author
FalkWolsky
committed
Introduce Rich Text Editor for Ticket Description
1 parent cde7340 commit b40c81a

File tree

1 file changed

+79
-7
lines changed

1 file changed

+79
-7
lines changed

client/packages/lowcoder/src/pages/support/supportDetail.tsx

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { useParams } from "react-router-dom";
66
import { Descriptions, Tag, Avatar, Skeleton, Button, Typography, List, Table, Progress, Input, Upload, message } from "antd";
77
import { UploadOutlined } from '@ant-design/icons';
88
import history from "util/history";
9-
import { getTicket, updateTicketDescription, addComment, uploadAttachment } from '@lowcoder-ee/api/supportApi'; // Placeholder for your API functions
9+
import { getTicket, updateTicketDescription, addComment, uploadAttachment } from '@lowcoder-ee/api/supportApi';
1010
import { Level1SettingPageContent, Level1SettingPageTitle } from "../setting/styled";
1111
import { HeaderBack } from "../setting/permission/styledComponents";
1212
import { SUPPORT_URL } from "constants/routesURL";
1313
import { TacoMarkDown } from "lowcoder-design";
1414
import remarkGfm from 'remark-gfm';
1515
import { contrastColor } from "comps/controls/styleControlConstants";
16+
import ReactQuill from 'react-quill';
17+
import 'react-quill/dist/quill.snow.css';
1618

1719
const { Title, Text } = Typography;
1820
const { TextArea } = Input;
@@ -124,6 +126,67 @@ const convertJiraToMarkdown = (content: string) => {
124126
return content;
125127
};
126128

129+
const convertJiraToHtml = (content : any) => {
130+
// Convert bold text: `*text*` to `<strong>text</strong>`
131+
content = content.replace(/\*(.+?)\*/g, '<strong>$1</strong>');
132+
133+
// Convert italic text: `_text_` to `<em>text</em>`
134+
content = content.replace(/_(.+?)_/g, '<em>$1</em>');
135+
136+
// Convert ordered lists: `# item` to `<ol><li>item</li></ol>`
137+
content = content.replace(/(?:^|\n)#\s+(.*?)(?=\n|$)/g, '<ol><li>$1</li></ol>');
138+
content = content.replace(/<\/ol>\s*<ol>/g, ''); // Merge consecutive lists
139+
140+
// Convert unordered lists: `* item` to `<ul><li>item</li></ul>`
141+
content = content.replace(/(?:^|\n)\*\s+(.*?)(?=\n|$)/g, '<ul><li>$1</li></ul>');
142+
content = content.replace(/<\/ul>\s*<ul>/g, ''); // Merge consecutive lists
143+
144+
// Convert headers: `h1. Header` to `<h1>Header</h1>`
145+
content = content.replace(/^h1\.\s(.*?)(?=\n|$)/gm, '<h1>$1</h1>');
146+
content = content.replace(/^h2\.\s(.*?)(?=\n|$)/gm, '<h2>$1</h2>');
147+
content = content.replace(/^h3\.\s(.*?)(?=\n|$)/gm, '<h3>$1</h3>');
148+
149+
// Convert line breaks
150+
content = content.replace(/\n/g, '<br>');
151+
152+
return content;
153+
};
154+
155+
// Function to convert HTML to Jira syntax
156+
const convertHtmlToJiraMarkdown = (html:any) => {
157+
// Convert bold tags to Jira bold
158+
html = html.replace(/<strong>(.*?)<\/strong>/g, '*$1*');
159+
160+
// Convert italic tags to Jira italic
161+
html = html.replace(/<em>(.*?)<\/em>/g, '_$1_');
162+
163+
// Convert ordered list items to Jira syntax
164+
html = html.replace(/<ol>(.*?)<\/ol>/gs, (match: any, inner: string) => {
165+
return inner.replace(/<li>(.*?)<\/li>/g, '# $1\n');
166+
});
167+
168+
// Convert unordered list items to Jira syntax
169+
html = html.replace(/<ul>(.*?)<\/ul>/gs, (match: any, inner: string) => {
170+
return inner.replace(/<li>(.*?)<\/li>/g, '* $1\n');
171+
});
172+
173+
// Convert headings
174+
html = html.replace(/<h1>(.*?)<\/h1>/g, 'h1. $1');
175+
html = html.replace(/<h2>(.*?)<\/h2>/g, 'h2. $1');
176+
html = html.replace(/<h3>(.*?)<\/h3>/g, 'h3. $1');
177+
178+
// Convert paragraphs
179+
html = html.replace(/<p>(.*?)<\/p>/g, '$1\n');
180+
181+
// Handle line breaks
182+
html = html.replace(/<br\s*\/?>/g, '\n');
183+
184+
// Remove any remaining HTML tags
185+
html = html.replace(/<\/?[^>]+(>|$)/g, "");
186+
187+
return html.trim();
188+
};
189+
127190
// Helper to render the description markdown
128191
const renderMarkdown = (content: string) => {
129192
const convertedContent = convertJiraToMarkdown(content);
@@ -216,7 +279,10 @@ export function SupportDetail() {
216279
const ticketData = await getTicket(ticketId);
217280
if (ticketData && ticketData.length === 1) {
218281
setTicket(ticketData[0]);
219-
setNewDescription(ticketData[0].fields.description || '');
282+
283+
console.log("Description", ticketData[0].fields.description);
284+
285+
setNewDescription(convertJiraToHtml(ticketData[0].fields.description) || '');
220286
} else {
221287
setError(trans("support.ticketNotFound"));
222288
}
@@ -240,7 +306,8 @@ export function SupportDetail() {
240306
const handleSaveDescription = async () => {
241307
setIsSavingDescription(true); // Start loading state
242308
try {
243-
await updateTicketDescription(ticketId, newDescription);
309+
const jiraFormattedDescription = convertHtmlToJiraMarkdown(newDescription);
310+
await updateTicketDescription(ticketId, jiraFormattedDescription);
244311
message.success(trans("support.ticketDescriptionUpdated"));
245312
setIsEditingDescription(false);
246313
} catch (error) {
@@ -309,9 +376,8 @@ export function SupportDetail() {
309376
} else {
310377
message.warning(trans("support.ticketAttachmentEmpty"));
311378
}
312-
};
313-
314-
379+
};
380+
315381

316382
if (loading) {
317383
return (
@@ -376,10 +442,16 @@ export function SupportDetail() {
376442
<DescriptionWrapper>
377443
{isEditingDescription ? (
378444
<>
379-
<TextArea
445+
{/* <TextArea
380446
rows={4}
381447
value={newDescription}
382448
onChange={(e) => setNewDescription(e.target.value)}
449+
/> */}
450+
<ReactQuill
451+
theme="snow"
452+
value={newDescription}
453+
onChange={setNewDescription}
454+
style={{ marginBottom: '8px' }}
383455
/>
384456
<Button
385457
type="primary"

0 commit comments

Comments
 (0)