allow format ticket's description

This commit is contained in:
Yinyin Liu 2026-04-30 12:38:50 +02:00
parent 99ba1b947c
commit 5586001b79
2 changed files with 69 additions and 26 deletions

View File

@ -21,6 +21,8 @@ import {
import AttachFileIcon from '@mui/icons-material/AttachFile';
import { FormattedMessage, useIntl } from 'react-intl';
import axiosConfig from 'src/Resources/axiosConfig';
import CommentFormatToolbar from './CommentFormatToolbar';
import { handleListEnter } from './commentMarkdown';
import {
TicketPriority,
TicketCategory,
@ -87,6 +89,7 @@ function CreateTicketModal({ open, onClose, onCreated, defaultInstallationId }:
// File attachments
const fileInputRef = useRef<HTMLInputElement>(null);
const descriptionRef = useRef<HTMLInputElement | null>(null);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const [uploading, setUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
@ -547,17 +550,35 @@ function CreateTicketModal({ open, onClose, onCreated, defaultInstallationId }:
/>
)}
<TextField
label={
<FormattedMessage id="description" defaultMessage="Description" />
}
value={description}
onChange={(e) => setDescription(e.target.value)}
multiline
rows={4}
fullWidth
margin="dense"
/>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 1 }}>
<CommentFormatToolbar
textareaRef={descriptionRef}
value={description}
onChange={setDescription}
disabled={submitting || uploading}
/>
<Typography variant="caption" color="text.disabled">
<FormattedMessage
id="commentMarkdownHint"
defaultMessage="Markdown: **bold**, #, ##"
/>
</Typography>
</Box>
<TextField
label={
<FormattedMessage id="description" defaultMessage="Description" />
}
value={description}
onChange={(e) => setDescription(e.target.value)}
onKeyDown={(e) => handleListEnter(e, description, setDescription)}
inputRef={descriptionRef}
multiline
rows={4}
fullWidth
margin="dense"
/>
</Box>
{/* File attachments */}
<Box sx={{ mt: 1 }}>

View File

@ -50,6 +50,8 @@ import Footer from 'src/components/Footer';
import StatusChip from './StatusChip';
import AiDiagnosisPanel from './AiDiagnosisPanel';
import CommentThread from './CommentThread';
import CommentFormatToolbar from './CommentFormatToolbar';
import { renderCommentBody, handleListEnter } from './commentMarkdown';
import TimelinePanel from './TimelinePanel';
import FileUploadButton from 'src/components/FileUploadButton';
import DocumentList from 'src/components/DocumentList';
@ -98,6 +100,7 @@ function TicketDetailPage() {
const [solveGateOpen, setSolveGateOpen] = useState(false);
const rootCauseRef = useRef<HTMLInputElement | null>(null);
const solutionRef = useRef<HTMLInputElement | null>(null);
const descriptionRef = useRef<HTMLInputElement | null>(null);
// Custom "Other" editing state
const [editCustomSub, setEditCustomSub] = useState('');
@ -411,7 +414,24 @@ function TicketDetailPage() {
<Divider />
<CardContent>
{editingDescription ? (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 1 }}>
<CommentFormatToolbar
textareaRef={descriptionRef}
value={description}
onChange={(next) => {
setDescription(next);
setDescriptionSaved(false);
}}
disabled={savingDescription}
/>
<Typography variant="caption" color="text.disabled">
<FormattedMessage
id="commentMarkdownHint"
defaultMessage="Markdown: **bold**, #, ##"
/>
</Typography>
</Box>
<TextField
multiline
minRows={3}
@ -421,6 +441,13 @@ function TicketDetailPage() {
setDescription(e.target.value);
setDescriptionSaved(false);
}}
onKeyDown={(e) =>
handleListEnter(e, description, (next) => {
setDescription(next);
setDescriptionSaved(false);
})
}
inputRef={descriptionRef}
/>
<Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
{descriptionSaved && (
@ -447,23 +474,18 @@ function TicketDetailPage() {
</Button>
</Box>
</Box>
) : ticket.description ? (
renderCommentBody(ticket.description)
) : (
<Typography
variant="body1"
sx={{ whiteSpace: 'pre-wrap' }}
component="span"
variant="body2"
color="text.secondary"
>
{ticket.description || (
<Typography
component="span"
variant="body2"
color="text.secondary"
>
<FormattedMessage
id="noDescription"
defaultMessage="No description provided."
/>
</Typography>
)}
<FormattedMessage
id="noDescription"
defaultMessage="No description provided."
/>
</Typography>
)}