allow format ticket's description
This commit is contained in:
parent
99ba1b947c
commit
5586001b79
|
|
@ -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 }}>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue