add modal window for save compaint text template

This commit is contained in:
smanylov
2026-04-29 17:09:05 +07:00
parent 41c56e026f
commit 4e8289e976
4 changed files with 369 additions and 157 deletions
+115 -2
View File
@@ -994,7 +994,7 @@
right: 0;
background: none;
border: none;
color: white;
color: v.$white;
font-size: 32px;
cursor: pointer;
padding: 8px;
@@ -1003,4 +1003,117 @@
&:hover {
opacity: 0.8;
}
}
}
.template-modal-content {
min-width: 400px;
max-width: 500px;
background: v.$white;
border-radius: 8px;
overflow: hidden;
}
.template-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid v.$border-color-1;
}
.template-modal-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.template-modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.2s;
}
.template-modal-close:hover {
background: v.$border-color-1;
color: v.$text-p;
}
.template-modal-body {
padding: 20px;
}
.template-form-group {
margin-bottom: 20px;
}
.template-form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: v.$text-p;
}
.template-name-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.2s;
}
.template-name-input:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.1);
}
.template-preview {
margin-top: 20px;
}
.template-preview-label {
font-weight: 500;
margin-bottom: 8px;
color: v.$text-p;
}
.template-preview-text {
background: v.$bg-hover;
padding: 12px;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
color: v.$text-s;
}
.template-modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding: 16px 20px;
border-top: 1px solid v.$border-color-1;
}
/* .btn-secondary {
background: #6c757d;
color: v.$white;
border: none;
}
.btn-secondary:hover {
background: #5a6268;
} */
@@ -7,6 +7,7 @@ import { createComplaint, fetchComplainInfo, MatchStatus } from '@/app/actions/v
import { toast } from 'sonner';
import { useQuery } from '@tanstack/react-query';
import { formatDate, formatDateTime } from '@/app/lib/formatDate';
import ModalWindow from '@/app/components/ModalWindow';
interface complaintInfo {
id: number,
@@ -28,6 +29,8 @@ export default function CaseComplaint({ selectedViolation, updateStatusHandler }
const [localViolation, setLocalViolation] = useState<ViolationFileDetail>(selectedViolation);
const [noteText, setNoteText] = useState('');
const [state, formAction, isPending] = useActionState(createComplaint, undefined);
const [isTemplateModalOpen, setIsTemplateModalOpen] = useState(false);
const [templateName, setTemplateName] = useState('');
useEffect(() => {
setLocalViolation(selectedViolation);
@@ -77,178 +80,256 @@ export default function CaseComplaint({ selectedViolation, updateStatusHandler }
}
}
function openTemplateSaveModal() {
if (!noteText.trim()) {
toast.warning(t('please-enter-complaint-text-first'));
return;
}
setTemplateName('');
setIsTemplateModalOpen(true);
}
function saveTemplateHandler() {
if (!templateName.trim()) {
toast.warning(t('please-enter-template-name'));
return;
}
// пока нету эндпоинта оставлю заглушку
console.log('СОХРАНЕНИЕ ШАБЛОНА');
console.log('Название шаблона:', templateName);
console.log('Текст жалобы:', noteText);
console.log('========================');
toast.success(t('template-saved-successfully') || 'Шаблон успешно сохранен');
setIsTemplateModalOpen(false);
setTemplateName('');
}
if (!ENABLED_STATUSES.includes(localViolation.status)) {
return (
<div className="violation-info-case">
<div className="note-form">
<form action={formAction}>
<input type="hidden" name="violationId" value={localViolation.id} />
<section
className="note-form-section"
>
<div
className="note-form-group"
<>
<div className="violation-info-case">
<div className="note-form">
<form action={formAction}>
<input type="hidden" name="violationId" value={localViolation.id} />
<section
className="note-form-section"
>
<label
className="note-case-label"
htmlFor="note"
<div
className="note-form-group"
>
{t('text-of-the-complaint')}
</label>
<label
className="note-case-label"
htmlFor="note"
>
{t('text-of-the-complaint')}
</label>
<textarea
name="note"
className="note-textarea"
placeholder={`${t('text-area-error-fill-complaint-text')}...`}
value={noteText}
onChange={(e) => setNoteText(e.target.value)}
<textarea
name="note"
className="note-textarea"
placeholder={`${t('text-area-error-fill-complaint-text')}...`}
value={noteText}
onChange={(e) => setNoteText(e.target.value)}
>
</textarea>
</div>
{state?.errorMessage?.textArea && (
<p
className="text-sm text-red-500"
>
{
state?.errorMessage?.textArea.split('&').map((e, index) => {
return (
<span key={index}>
{t(e)}
<br />
</span>
)
})
}
</p>
)}
</section>
<div
className="button-actions"
>
<button
type="button"
className="btn-s btn-primary-small"
onClick={openTemplateSaveModal}
>
</textarea>
{t('save-template')}
</button>
<button
type="submit"
className="btn-s btn-primary-small"
disabled={isPending}
>
{t('submit-violation')}
</button>
</div>
</form>
</div>
<div
className="samples-wrapper"
>
<h5>Шаблоны</h5>
{state?.errorMessage?.textArea && (
<p
className="text-sm text-red-500"
>
{
state?.errorMessage?.textArea.split('&').map((e, index) => {
return (
<span key={index}>
{t(e)}
<br />
</span>
)
})
}
</p>
)}
</section>
<div
className="button-actions"
<ul
className="samples-list"
>
<button
type="submit"
className="btn-s btn-primary-small"
disabled={isPending}
<li
className="sample-item"
onClick={(e) => {
e.preventDefault();
templateHandler(e)
}}
>
Сохранить шаблон
</button>
<button
type="submit"
className="btn-s btn-primary-small"
disabled={isPending}
<div
className="sample-item-title"
>
title 1
</div>
<div
className="sample-item-text"
>
1text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
{t('submit-violation')}
<div
className="sample-item-title"
>
title 2
</div>
<div
className="sample-item-text"
>
2text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
</ul>
</div>
</div>
<ModalWindow
state={isTemplateModalOpen}
callBack={setIsTemplateModalOpen}
>
<div className="template-modal-content">
<div className="template-modal-header">
<h3>{t('save-template')}</h3>
<button
className="template-modal-close"
onClick={() => setIsTemplateModalOpen(false)}
>
×
</button>
</div>
</form>
<div className="template-modal-body">
<div className="template-form-group">
<label htmlFor="templateName">
{t('template-name')}:
</label>
<input
id="templateName"
type="text"
className="template-name-input"
placeholder={t('enter-template-name')}
value={templateName}
onChange={(e) => setTemplateName(e.target.value)}
autoFocus
/>
</div>
</div>
<div
className="samples-wrapper"
>
<h5>Шаблоны</h5>
<ul
className="samples-list"
>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 1
<div className="template-preview">
<div className="template-preview-label">
{t('complaint-text-preview')}
</div>
<div className="template-preview-text">
{noteText || t('no-text')}
</div>
</div>
<div
className="sample-item-text"
</div>
<div className="template-modal-footer">
<button
className="btn-s btn-primary-small"
onClick={() => setIsTemplateModalOpen(false)}
>
1text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
{t('cancel')}
</button>
<button
className="btn-s btn-resolve"
onClick={saveTemplateHandler}
>
title 2
</div>
<div
className="sample-item-text"
>
2text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
<li
className="sample-item"
onClick={(e) => {
templateHandler(e)
}}
>
<div
className="sample-item-title"
>
title 3
</div>
<div
className="sample-item-text"
>
3text text text text text text text text text text text text
</div>
</li>
</ul>
</div>
</div>
{t('save')}
</button>
</div>
</div>
</ModalWindow>
</>
);
} else {
if (isLoadingComplain) {
+10 -1
View File
@@ -454,7 +454,16 @@
"select-at-least-one-filter": "Select at least one filter",
"select-document-to-upload": "Select document(s) to upload",
"uploading-id-images": "Uploading ID images",
"tracking": "Tracking"
"tracking": "Tracking",
"save-template": "Save template",
"save": "Save",
"template-name": "Template name",
"enter-template-name": "Enter template name",
"complaint-text-preview": "Complaint text preview",
"no-text": "No text",
"please-enter-complaint-text-first": "Please enter complaint text first",
"please-enter-template-name": "Please enter template name",
"template-saved-successfully": "Template saved successfully"
},
"Login-register-form": {
"and": "and",
+10 -1
View File
@@ -454,7 +454,16 @@
"select-at-least-one-filter": "Выберите хотя бы один фильтр",
"select-document-to-upload": "Выберите документ(ы) для загрузки",
"uploading-id-images": "Загрузка изображений удостоверения личности",
"tracking": "Отслеживание"
"tracking": "Отслеживание",
"save-template": "Сохранить шаблон",
"save": "Сохранить",
"template-name": "Название шаблона",
"enter-template-name": "Введите название шаблона",
"complaint-text-preview": "Предварительный просмотр текста жалобы",
"no-text": "Нет текста",
"please-enter-complaint-text-first": "Пожалуйста, сначала введите текст жалобы",
"please-enter-template-name": "Пожалуйста, введите название шаблона",
"template-saved-successfully": "Шаблон успешно сохранен"
},
"Login-register-form": {
"and": "и",