mirror of
https://github.com/TronoSfera/Law.git
synced 2026-05-18 10:03:45 +03:00
fix client chat v2
This commit is contained in:
parent
7b509f6af3
commit
df80b5cb5f
7 changed files with 214 additions and 149 deletions
|
|
@ -157,6 +157,7 @@ def _serialize_public_service_request(row: RequestServiceRequest) -> PublicServi
|
|||
id=row.id,
|
||||
request_id=row.request_id,
|
||||
client_id=row.client_id,
|
||||
assigned_lawyer_id=row.assigned_lawyer_id,
|
||||
type=str(row.type or ""),
|
||||
status=str(row.status or "NEW"),
|
||||
body=str(row.body or ""),
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ class PublicServiceRequestRead(BaseModel):
|
|||
id: UUID
|
||||
request_id: UUID
|
||||
client_id: Optional[UUID] = None
|
||||
assigned_lawyer_id: Optional[UUID] = None
|
||||
type: str
|
||||
status: str
|
||||
body: str
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ export function RequestWorkspace({
|
|||
const canRequestData = viewerRoleCode === "LAWYER" || viewerRoleCode === "ADMIN";
|
||||
const canFillRequestData = viewerRoleCode === "CLIENT";
|
||||
const canSeeRate = viewerRoleCode !== "CLIENT";
|
||||
const canSeeCreatedUpdatedInCard = viewerRoleCode !== "CLIENT";
|
||||
const safeMessages = Array.isArray(messages) ? messages : [];
|
||||
const safeAttachments = Array.isArray(attachments) ? attachments : [];
|
||||
const safeStatusHistory = Array.isArray(statusHistory) ? statusHistory : [];
|
||||
|
|
@ -424,6 +425,20 @@ export function RequestWorkspace({
|
|||
openPreview(item);
|
||||
};
|
||||
|
||||
const downloadAttachment = (item) => {
|
||||
const url = String(item?.download_url || "").trim();
|
||||
if (!url) return;
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.target = "_blank";
|
||||
link.rel = "noreferrer";
|
||||
const fileName = String(item?.file_name || "").trim();
|
||||
if (fileName) link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
liveCursorRef.current = localActivityCursor || "";
|
||||
}, [localActivityCursor, row?.id]);
|
||||
|
|
@ -1287,14 +1302,18 @@ export function RequestWorkspace({
|
|||
{lawyerLabel}
|
||||
</span>
|
||||
</div>
|
||||
<div className="request-field">
|
||||
<span className="request-field-label">Создана</span>
|
||||
<span className="request-field-value">{fmtShortDateTime(row.created_at)}</span>
|
||||
</div>
|
||||
<div className="request-field">
|
||||
<span className="request-field-label">Изменена</span>
|
||||
<span className="request-field-value">{fmtShortDateTime(row.updated_at)}</span>
|
||||
</div>
|
||||
{canSeeCreatedUpdatedInCard ? (
|
||||
<>
|
||||
<div className="request-field">
|
||||
<span className="request-field-label">Создана</span>
|
||||
<span className="request-field-value">{fmtShortDateTime(row.created_at)}</span>
|
||||
</div>
|
||||
<div className="request-field">
|
||||
<span className="request-field-label">Изменена</span>
|
||||
<span className="request-field-value">{fmtShortDateTime(row.updated_at)}</span>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="request-status-route">
|
||||
<h4>Маршрут статусов</h4>
|
||||
|
|
@ -2372,7 +2391,7 @@ export function RequestWorkspace({
|
|||
{isFile ? (
|
||||
value && typeof value === "object" ? (
|
||||
<div className="request-data-summary-file">
|
||||
<button type="button" className="chat-message-file-chip" onClick={() => openAttachmentFromMessage(value)}>
|
||||
<button type="button" className="chat-message-file-chip" onClick={() => downloadAttachment(value)}>
|
||||
<span className="chat-message-file-icon" aria-hidden="true">📎</span>
|
||||
<span className="chat-message-file-name">{String(value.file_name || "Файл")}</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -37,27 +37,28 @@
|
|||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.client-summary-actions {
|
||||
margin-top: 0.75rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
.client-help-modal {
|
||||
width: min(760px, 100%);
|
||||
}
|
||||
|
||||
.client-service-requests {
|
||||
margin-top: 0.85rem;
|
||||
.client-help-stack {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.client-service-requests h3 {
|
||||
margin: 0 0 0.65rem;
|
||||
.client-help-block {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
padding: 0.7rem;
|
||||
display: grid;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.service-request-modal {
|
||||
width: min(720px, 100%);
|
||||
}
|
||||
|
||||
.service-request-form textarea {
|
||||
min-height: 150px;
|
||||
.client-help-description {
|
||||
margin: 0;
|
||||
color: #dbe6f4;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
#client-page-status {
|
||||
|
|
|
|||
|
|
@ -5,17 +5,6 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
(function () {
|
||||
const { useCallback, useEffect, useMemo, useRef, useState } = React;
|
||||
|
||||
const SERVICE_REQUEST_TYPE_LABELS = {
|
||||
CURATOR_CONTACT: "Запрос к куратору",
|
||||
LAWYER_CHANGE_REQUEST: "Смена юриста",
|
||||
};
|
||||
const SERVICE_REQUEST_STATUS_LABELS = {
|
||||
NEW: "Новый",
|
||||
IN_PROGRESS: "В работе",
|
||||
RESOLVED: "Решен",
|
||||
REJECTED: "Отклонен",
|
||||
};
|
||||
|
||||
function StatusLine({ status }) {
|
||||
return <p className={"status" + (status?.kind ? " " + status.kind : "")}>{status?.message || ""}</p>;
|
||||
}
|
||||
|
|
@ -270,82 +259,93 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
);
|
||||
}
|
||||
|
||||
function ServiceRequestModal({ open, type, body, status, loading, onBodyChange, onClose, onSubmit }) {
|
||||
const title = type === "LAWYER_CHANGE_REQUEST" ? "Запрос на смену юриста" : "Обращение к куратору";
|
||||
function ClientHelpModal({
|
||||
open,
|
||||
status,
|
||||
loadingType,
|
||||
lawyerChangeReason,
|
||||
curatorBlocked,
|
||||
lawyerChangeBlocked,
|
||||
onClose,
|
||||
onReasonChange,
|
||||
onSubmitCurator,
|
||||
onSubmitLawyerChange,
|
||||
}) {
|
||||
return (
|
||||
<div className={"overlay" + (open ? " open" : "")} id="service-request-overlay" onClick={(event) => event.target.id === "service-request-overlay" && onClose()}>
|
||||
<div className="modal service-request-modal" onClick={(event) => event.stopPropagation()}>
|
||||
<div className={"overlay" + (open ? " open" : "")} id="client-help-overlay" onClick={(event) => event.target.id === "client-help-overlay" && onClose()}>
|
||||
<div className="modal client-help-modal" onClick={(event) => event.stopPropagation()}>
|
||||
<div className="modal-head">
|
||||
<div>
|
||||
<h3 id="service-request-title">{title}</h3>
|
||||
<h3 id="client-help-title">Помощь по заявке</h3>
|
||||
</div>
|
||||
<button className="close" type="button" id="service-request-close" onClick={onClose} aria-label="Закрыть">
|
||||
<button className="close" type="button" id="client-help-close" onClick={onClose} aria-label="Закрыть">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<form id="service-request-form" className="stack service-request-form" onSubmit={onSubmit}>
|
||||
<div className="field">
|
||||
<label htmlFor="service-request-body">Сообщение</label>
|
||||
<textarea
|
||||
id="service-request-body"
|
||||
value={body}
|
||||
onChange={onBodyChange}
|
||||
maxLength={4000}
|
||||
placeholder="Опишите обращение"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="modal-actions modal-actions-right">
|
||||
<button className="btn btn-sm" id="service-request-send" type="submit" disabled={loading}>
|
||||
{loading ? "Отправка..." : "Отправить"}
|
||||
<div className="client-help-stack">
|
||||
<section className="client-help-block">
|
||||
<p className="client-help-description">
|
||||
Если нужна дополнительная поддержка по делу, можно обратиться к куратору. Запрос отправляется администратору платформы.
|
||||
</p>
|
||||
<button
|
||||
className="btn secondary btn-sm"
|
||||
id="cabinet-curator-request-open"
|
||||
type="button"
|
||||
disabled={curatorBlocked || loadingType === "CURATOR_CONTACT"}
|
||||
onClick={onSubmitCurator}
|
||||
>
|
||||
{loadingType === "CURATOR_CONTACT" ? "Отправка..." : "Обратиться к куратору"}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="client-help-block">
|
||||
<p className="client-help-description">
|
||||
Если текущий юрист не устраивает, можно запросить его смену. Укажите причину для администратора.
|
||||
</p>
|
||||
<div className="field">
|
||||
<label htmlFor="service-request-body">Причина смены юриста</label>
|
||||
<textarea
|
||||
id="service-request-body"
|
||||
value={lawyerChangeReason}
|
||||
onChange={onReasonChange}
|
||||
maxLength={4000}
|
||||
placeholder="Опишите причину"
|
||||
disabled={lawyerChangeBlocked || loadingType === "LAWYER_CHANGE_REQUEST"}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="btn secondary btn-sm"
|
||||
id="cabinet-lawyer-change-open"
|
||||
type="button"
|
||||
disabled={lawyerChangeBlocked || loadingType === "LAWYER_CHANGE_REQUEST"}
|
||||
onClick={onSubmitLawyerChange}
|
||||
>
|
||||
{loadingType === "LAWYER_CHANGE_REQUEST" ? "Отправка..." : "Запросить смену"}
|
||||
</button>
|
||||
</section>
|
||||
<StatusLine status={status} />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ServiceRequestList({ rows }) {
|
||||
const safeRows = Array.isArray(rows) ? rows : [];
|
||||
return (
|
||||
<ul className="simple-list request-modal-list" id="cabinet-service-requests">
|
||||
{safeRows.length ? (
|
||||
safeRows.map((item) => {
|
||||
const typeCode = String(item?.type || "").toUpperCase();
|
||||
const statusCode = String(item?.status || "").toUpperCase();
|
||||
return (
|
||||
<li key={String(item.id)} className="simple-item">
|
||||
<div>{(SERVICE_REQUEST_TYPE_LABELS[typeCode] || typeCode || "Запрос") + " • " + (SERVICE_REQUEST_STATUS_LABELS[statusCode] || statusCode || "NEW")}</div>
|
||||
<div className="muted request-modal-item-meta">{fmtShortDateTime(item?.created_at)}</div>
|
||||
{item?.body ? <p>{String(item.body)}</p> : null}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<li className="muted">Обращений пока нет</li>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [requestModal, setRequestModal] = useState(createRequestModalState());
|
||||
const [requestsList, setRequestsList] = useState([]);
|
||||
const [activeTrack, setActiveTrack] = useState("");
|
||||
const [status, setStatus] = useState({ message: "", kind: "" });
|
||||
const [serviceRequests, setServiceRequests] = useState([]);
|
||||
const [serviceRequestModal, setServiceRequestModal] = useState({ open: false, type: "", body: "", loading: false, status: { message: "", kind: "" } });
|
||||
const [clientHelpModal, setClientHelpModal] = useState({
|
||||
open: false,
|
||||
loadingType: "",
|
||||
lawyerChangeReason: "",
|
||||
status: { message: "", kind: "" },
|
||||
});
|
||||
|
||||
const setPageStatus = useCallback((message, kind) => {
|
||||
setStatus({ message: String(message || ""), kind: kind || "" });
|
||||
}, []);
|
||||
|
||||
const setServiceStatus = useCallback((message, kind) => {
|
||||
setServiceRequestModal((prev) => ({ ...prev, status: { message: String(message || ""), kind: kind || "" } }));
|
||||
}, []);
|
||||
|
||||
const apiError = (data, fallback) => {
|
||||
if (data && typeof data.detail === "string" && data.detail.trim()) return data.detail;
|
||||
return fallback;
|
||||
|
|
@ -656,60 +656,103 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
[activeTrack, apiJson]
|
||||
);
|
||||
|
||||
const openServiceRequestModal = useCallback((type) => {
|
||||
const normalized = String(type || "").trim().toUpperCase();
|
||||
if (!normalized) return;
|
||||
setServiceRequestModal({
|
||||
const openClientHelpModal = useCallback(() => {
|
||||
setClientHelpModal((prev) => ({
|
||||
...prev,
|
||||
open: true,
|
||||
type: normalized,
|
||||
body: "",
|
||||
loading: false,
|
||||
status: { message: "", kind: "" },
|
||||
});
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const closeServiceRequestModal = useCallback(() => {
|
||||
setServiceRequestModal({ open: false, type: "", body: "", loading: false, status: { message: "", kind: "" } });
|
||||
const closeClientHelpModal = useCallback(() => {
|
||||
setClientHelpModal((prev) => ({
|
||||
...prev,
|
||||
open: false,
|
||||
loadingType: "",
|
||||
status: { message: "", kind: "" },
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const normalizedCurrentLawyerId = String(requestModal.requestData?.assigned_lawyer_id || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
const serviceRequestState = useMemo(() => {
|
||||
const rows = Array.isArray(serviceRequests) ? serviceRequests : [];
|
||||
const curatorRows = rows.filter((item) => String(item?.type || "").toUpperCase() === "CURATOR_CONTACT");
|
||||
const lawyerRows = rows.filter((item) => String(item?.type || "").toUpperCase() === "LAWYER_CHANGE_REQUEST");
|
||||
const latestLawyerChange = lawyerRows[0] || null;
|
||||
const hasCuratorRequest = curatorRows.length > 0;
|
||||
|
||||
let lawyerChangeDisabledByState = false;
|
||||
if (latestLawyerChange) {
|
||||
const hasLawyerSnapshot = Object.prototype.hasOwnProperty.call(latestLawyerChange, "assigned_lawyer_id");
|
||||
if (hasLawyerSnapshot) {
|
||||
const requestedForLawyer = String(latestLawyerChange?.assigned_lawyer_id || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
lawyerChangeDisabledByState = requestedForLawyer === normalizedCurrentLawyerId;
|
||||
} else {
|
||||
const statusCode = String(latestLawyerChange?.status || "").toUpperCase();
|
||||
lawyerChangeDisabledByState = statusCode === "NEW" || statusCode === "IN_PROGRESS";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasCuratorRequest,
|
||||
lawyerChangeDisabledByState,
|
||||
};
|
||||
}, [normalizedCurrentLawyerId, serviceRequests]);
|
||||
|
||||
const canInteract = Boolean(requestModal.requestData && !requestModal.loading);
|
||||
const curatorBlocked = !canInteract || serviceRequestState.hasCuratorRequest;
|
||||
const lawyerChangeBlocked = !canInteract || serviceRequestState.lawyerChangeDisabledByState;
|
||||
|
||||
const submitServiceRequest = useCallback(
|
||||
async (event) => {
|
||||
if (event && typeof event.preventDefault === "function") event.preventDefault();
|
||||
async ({ type, body }) => {
|
||||
const track = String(activeTrack || "").trim();
|
||||
const requestType = String(type || "").trim().toUpperCase();
|
||||
const message = String(body || "").trim();
|
||||
if (!track) {
|
||||
setServiceStatus("Сначала выберите заявку.", "error");
|
||||
setClientHelpModal((prev) => ({ ...prev, status: { message: "Сначала выберите заявку.", kind: "error" } }));
|
||||
return;
|
||||
}
|
||||
const requestType = String(serviceRequestModal.type || "").trim().toUpperCase();
|
||||
const body = String(serviceRequestModal.body || "").trim();
|
||||
if (!requestType) {
|
||||
setServiceStatus("Выберите тип обращения.", "error");
|
||||
setClientHelpModal((prev) => ({ ...prev, status: { message: "Не указан тип запроса.", kind: "error" } }));
|
||||
return;
|
||||
}
|
||||
if (body.length < 3) {
|
||||
setServiceStatus("Сообщение должно содержать минимум 3 символа.", "error");
|
||||
if (message.length < 3) {
|
||||
setClientHelpModal((prev) => ({ ...prev, status: { message: "Сообщение должно содержать минимум 3 символа.", kind: "error" } }));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setServiceRequestModal((prev) => ({ ...prev, loading: true, status: { message: "", kind: "" } }));
|
||||
setClientHelpModal((prev) => ({ ...prev, loadingType: requestType, status: { message: "", kind: "" } }));
|
||||
await apiJson(
|
||||
"/api/public/requests/" + encodeURIComponent(track) + "/service-requests",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ type: requestType, body }),
|
||||
body: JSON.stringify({ type: requestType, body: message }),
|
||||
},
|
||||
"Не удалось отправить обращение"
|
||||
);
|
||||
await loadRequestWorkspace(track, false);
|
||||
setPageStatus("Обращение отправлено.", "ok");
|
||||
closeServiceRequestModal();
|
||||
setClientHelpModal((prev) => ({
|
||||
...prev,
|
||||
loadingType: "",
|
||||
status: { message: "Обращение отправлено.", kind: "ok" },
|
||||
lawyerChangeReason: requestType === "LAWYER_CHANGE_REQUEST" ? "" : prev.lawyerChangeReason,
|
||||
}));
|
||||
} catch (error) {
|
||||
setServiceRequestModal((prev) => ({ ...prev, loading: false }));
|
||||
setServiceStatus(error?.message || "Не удалось отправить обращение", "error");
|
||||
setClientHelpModal((prev) => ({
|
||||
...prev,
|
||||
loadingType: "",
|
||||
status: { message: error?.message || "Не удалось отправить обращение", kind: "error" },
|
||||
}));
|
||||
}
|
||||
},
|
||||
[activeTrack, apiJson, closeServiceRequestModal, loadRequestWorkspace, serviceRequestModal.body, serviceRequestModal.type, setPageStatus, setServiceStatus]
|
||||
[activeTrack, apiJson, loadRequestWorkspace, setPageStatus]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -721,7 +764,6 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
}, [loadMyRequests, setPageStatus]);
|
||||
|
||||
const summary = requestModal.requestData || null;
|
||||
const canInteract = Boolean(summary && !requestModal.loading);
|
||||
|
||||
return (
|
||||
<div className="client-page-shell">
|
||||
|
|
@ -731,9 +773,17 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
<h1>Кабинет клиента</h1>
|
||||
<p className="muted">Работа с заявками: статусы, чат, файлы и обращения.</p>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: "0.45rem", flexWrap: "wrap" }}>
|
||||
<a className="btn secondary btn-sm" href="/">На лендинг</a>
|
||||
</div>
|
||||
<button
|
||||
className="icon-btn workspace-head-icon"
|
||||
id="cabinet-help-open"
|
||||
type="button"
|
||||
data-tooltip="Помощь и обращения"
|
||||
aria-label="Помощь и обращения"
|
||||
disabled={!canInteract}
|
||||
onClick={openClientHelpModal}
|
||||
>
|
||||
?
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section className="section active client-section">
|
||||
|
|
@ -795,14 +845,6 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
<span className="request-field-value" id="cabinet-request-updated">{summary ? fmtShortDateTime(summary.updated_at) : "-"}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="client-summary-actions">
|
||||
<button className="btn secondary btn-sm" id="cabinet-curator-request-open" type="button" disabled={!canInteract} onClick={() => openServiceRequestModal("CURATOR_CONTACT")}>
|
||||
Обратиться к куратору
|
||||
</button>
|
||||
<button className="btn secondary btn-sm" id="cabinet-lawyer-change-open" type="button" disabled={!canInteract} onClick={() => openServiceRequestModal("LAWYER_CHANGE_REQUEST")}>
|
||||
Запросить смену юриста
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RequestWorkspace
|
||||
|
|
@ -848,23 +890,26 @@ import { detectAttachmentPreviewKind, fmtShortDateTime } from "./admin/shared/ut
|
|||
dataRequestSave: "data-request-save",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="block client-service-requests">
|
||||
<h3>Мои обращения</h3>
|
||||
<ServiceRequestList rows={serviceRequests} />
|
||||
</div>
|
||||
</section>
|
||||
<p className="status" id="client-page-status">{status.message}</p>
|
||||
</main>
|
||||
<ServiceRequestModal
|
||||
open={serviceRequestModal.open}
|
||||
type={serviceRequestModal.type}
|
||||
body={serviceRequestModal.body}
|
||||
status={serviceRequestModal.status}
|
||||
loading={serviceRequestModal.loading}
|
||||
onBodyChange={(event) => setServiceRequestModal((prev) => ({ ...prev, body: event.target.value }))}
|
||||
onClose={closeServiceRequestModal}
|
||||
onSubmit={submitServiceRequest}
|
||||
<ClientHelpModal
|
||||
open={clientHelpModal.open}
|
||||
status={clientHelpModal.status}
|
||||
loadingType={clientHelpModal.loadingType}
|
||||
lawyerChangeReason={clientHelpModal.lawyerChangeReason}
|
||||
curatorBlocked={curatorBlocked}
|
||||
lawyerChangeBlocked={lawyerChangeBlocked}
|
||||
onClose={closeClientHelpModal}
|
||||
onReasonChange={(event) =>
|
||||
setClientHelpModal((prev) => ({
|
||||
...prev,
|
||||
lawyerChangeReason: event.target.value,
|
||||
status: { message: "", kind: "" },
|
||||
}))
|
||||
}
|
||||
onSubmitCurator={() => submitServiceRequest({ type: "CURATOR_CONTACT", body: "Прошу подключить куратора к текущей заявке." })}
|
||||
onSubmitLawyerChange={() => submitServiceRequest({ type: "LAWYER_CHANGE_REQUEST", body: clientHelpModal.lawyerChangeReason })}
|
||||
/>
|
||||
<GlobalTooltipLayer />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ test("request data file field flow via UI: lawyer requests file -> client upload
|
|||
});
|
||||
|
||||
await page.locator("#data-request-save").click();
|
||||
await expect(page.locator("#data-request-status")).toContainText("Данные сохранены.");
|
||||
await expect(page.locator("#data-request-overlay")).not.toHaveClass(/open/);
|
||||
await expect(page.locator("#cabinet-messages .request-data-item.done").last()).toBeVisible();
|
||||
|
||||
await page.goto("/admin");
|
||||
|
|
|
|||
|
|
@ -35,23 +35,21 @@ test("service requests UI flow: client creates requests -> admin sees them in Re
|
|||
trackCleanupTrack(testInfo, trackNumber);
|
||||
await openPublicCabinet(page, trackNumber);
|
||||
|
||||
await page.locator("#cabinet-curator-request-open").click();
|
||||
await expect(page.locator("#service-request-overlay")).toHaveClass(/open/);
|
||||
await page.locator("#service-request-body").fill("Нужна консультация куратора по делу.");
|
||||
await page.locator("#service-request-send").click();
|
||||
await expect(page.locator("#client-page-status")).toContainText("Обращение отправлено.");
|
||||
await expect(page.locator("#cabinet-service-requests")).toContainText("Запрос к куратору");
|
||||
await page.locator("#cabinet-help-open").click();
|
||||
await expect(page.locator("#client-help-overlay")).toHaveClass(/open/);
|
||||
|
||||
await page.locator("#cabinet-lawyer-change-open").click();
|
||||
await expect(page.locator("#service-request-overlay")).toHaveClass(/open/);
|
||||
await page.locator("#service-request-body").fill("Прошу рассмотреть смену юриста.");
|
||||
await page.locator("#service-request-send").click();
|
||||
await page.locator("#cabinet-curator-request-open").click();
|
||||
await expect(page.locator("#client-page-status")).toContainText("Обращение отправлено.");
|
||||
await expect(page.locator("#cabinet-service-requests")).toContainText("Смена юриста");
|
||||
await expect(page.locator("#cabinet-curator-request-open")).toBeDisabled();
|
||||
|
||||
await page.locator("#service-request-body").fill("Прошу рассмотреть смену юриста.");
|
||||
await page.locator("#cabinet-lawyer-change-open").click();
|
||||
await expect(page.locator("#client-page-status")).toContainText("Обращение отправлено.");
|
||||
await expect(page.locator("#cabinet-lawyer-change-open")).toBeDisabled();
|
||||
|
||||
await loginAdminPanel(page, { email: "admin@example.com", password: "admin123" });
|
||||
await page.locator("aside .menu button[data-section='serviceRequests']").click();
|
||||
await expect(page.locator("#section-service-requests h2")).toHaveText("Запросы");
|
||||
await expect(page.locator("#section-service-requests table")).toContainText("Нужна консультация куратора");
|
||||
await expect(page.locator("#section-service-requests table")).toContainText("Прошу подключить куратора к текущей заявке.");
|
||||
await expect(page.locator("#section-service-requests table")).toContainText("Прошу рассмотреть смену юриста");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue