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 AttachFileIcon from '@mui/icons-material/AttachFile';
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
import axiosConfig from 'src/Resources/axiosConfig';
|
import axiosConfig from 'src/Resources/axiosConfig';
|
||||||
|
import CommentFormatToolbar from './CommentFormatToolbar';
|
||||||
|
import { handleListEnter } from './commentMarkdown';
|
||||||
import {
|
import {
|
||||||
TicketPriority,
|
TicketPriority,
|
||||||
TicketCategory,
|
TicketCategory,
|
||||||
|
|
@ -87,6 +89,7 @@ function CreateTicketModal({ open, onClose, onCreated, defaultInstallationId }:
|
||||||
|
|
||||||
// File attachments
|
// File attachments
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const descriptionRef = useRef<HTMLInputElement | null>(null);
|
||||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
|
|
@ -547,17 +550,35 @@ function CreateTicketModal({ open, onClose, onCreated, defaultInstallationId }:
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TextField
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
|
||||||
label={
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 1 }}>
|
||||||
<FormattedMessage id="description" defaultMessage="Description" />
|
<CommentFormatToolbar
|
||||||
}
|
textareaRef={descriptionRef}
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={setDescription}
|
||||||
multiline
|
disabled={submitting || uploading}
|
||||||
rows={4}
|
/>
|
||||||
fullWidth
|
<Typography variant="caption" color="text.disabled">
|
||||||
margin="dense"
|
<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 */}
|
{/* File attachments */}
|
||||||
<Box sx={{ mt: 1 }}>
|
<Box sx={{ mt: 1 }}>
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ import Footer from 'src/components/Footer';
|
||||||
import StatusChip from './StatusChip';
|
import StatusChip from './StatusChip';
|
||||||
import AiDiagnosisPanel from './AiDiagnosisPanel';
|
import AiDiagnosisPanel from './AiDiagnosisPanel';
|
||||||
import CommentThread from './CommentThread';
|
import CommentThread from './CommentThread';
|
||||||
|
import CommentFormatToolbar from './CommentFormatToolbar';
|
||||||
|
import { renderCommentBody, handleListEnter } from './commentMarkdown';
|
||||||
import TimelinePanel from './TimelinePanel';
|
import TimelinePanel from './TimelinePanel';
|
||||||
import FileUploadButton from 'src/components/FileUploadButton';
|
import FileUploadButton from 'src/components/FileUploadButton';
|
||||||
import DocumentList from 'src/components/DocumentList';
|
import DocumentList from 'src/components/DocumentList';
|
||||||
|
|
@ -98,6 +100,7 @@ function TicketDetailPage() {
|
||||||
const [solveGateOpen, setSolveGateOpen] = useState(false);
|
const [solveGateOpen, setSolveGateOpen] = useState(false);
|
||||||
const rootCauseRef = useRef<HTMLInputElement | null>(null);
|
const rootCauseRef = useRef<HTMLInputElement | null>(null);
|
||||||
const solutionRef = useRef<HTMLInputElement | null>(null);
|
const solutionRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
const descriptionRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
// Custom "Other" editing state
|
// Custom "Other" editing state
|
||||||
const [editCustomSub, setEditCustomSub] = useState('');
|
const [editCustomSub, setEditCustomSub] = useState('');
|
||||||
|
|
@ -411,7 +414,24 @@ function TicketDetailPage() {
|
||||||
<Divider />
|
<Divider />
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{editingDescription ? (
|
{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
|
<TextField
|
||||||
multiline
|
multiline
|
||||||
minRows={3}
|
minRows={3}
|
||||||
|
|
@ -421,6 +441,13 @@ function TicketDetailPage() {
|
||||||
setDescription(e.target.value);
|
setDescription(e.target.value);
|
||||||
setDescriptionSaved(false);
|
setDescriptionSaved(false);
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(e) =>
|
||||||
|
handleListEnter(e, description, (next) => {
|
||||||
|
setDescription(next);
|
||||||
|
setDescriptionSaved(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
inputRef={descriptionRef}
|
||||||
/>
|
/>
|
||||||
<Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
|
<Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
|
||||||
{descriptionSaved && (
|
{descriptionSaved && (
|
||||||
|
|
@ -447,23 +474,18 @@ function TicketDetailPage() {
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
) : ticket.description ? (
|
||||||
|
renderCommentBody(ticket.description)
|
||||||
) : (
|
) : (
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
component="span"
|
||||||
sx={{ whiteSpace: 'pre-wrap' }}
|
variant="body2"
|
||||||
|
color="text.secondary"
|
||||||
>
|
>
|
||||||
{ticket.description || (
|
<FormattedMessage
|
||||||
<Typography
|
id="noDescription"
|
||||||
component="span"
|
defaultMessage="No description provided."
|
||||||
variant="body2"
|
/>
|
||||||
color="text.secondary"
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id="noDescription"
|
|
||||||
defaultMessage="No description provided."
|
|
||||||
/>
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue