- Implemented BlogBodySanitizer to clean HTML content before saving articles, ensuring security against XSS attacks. - Added tests for BlogBodySanitizer to verify that unsafe tags and attributes are stripped from the content. - Introduced ApiLeastPrivilegeTest to ensure that unauthorized users cannot access sensitive API routes, maintaining strict access control.
74 lines
3.6 KiB
TypeScript
74 lines
3.6 KiB
TypeScript
import { useState } from 'react';
|
|
import { useParams, useNavigate } from 'react-router';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { api } from '../lib/api';
|
|
import type { ApiResponse } from '../lib/api';
|
|
import type { PatientRecord } from '../types';
|
|
import type { SessionCardData } from '../components/SessionServiceCard';
|
|
import CreateStep from '../components/session/CreateStep';
|
|
import PaymentStep from '../components/session/PaymentStep';
|
|
import { CloseModalD } from '../components/icons/FilesServiceIcons';
|
|
import { Breadcrumb } from '../components/PatientCaseBanner';
|
|
|
|
/** ویرایش مراجعهی ثبتشده — دو تب: ویرایش سرویسها + مدیریت پرداختها. */
|
|
export default function EditSessionPage() {
|
|
const { recordUuid = '', sessionUuid = '' } = useParams();
|
|
const nav = useNavigate();
|
|
const [tab, setTab] = useState<'service' | 'payment'>('service');
|
|
|
|
const recordQ = useQuery<ApiResponse<PatientRecord>>({
|
|
queryKey: ['patient-detail', recordUuid],
|
|
queryFn: () => api.get(`/api/v1/patient/${recordUuid}`),
|
|
enabled: !!recordUuid,
|
|
});
|
|
const record = recordQ.data?.data as PatientRecord | undefined;
|
|
const patientName = record?.user_name || record?.profile?.full_name || '—';
|
|
|
|
const sessionsQ = useQuery<ApiResponse<SessionCardData[]>>({
|
|
queryKey: ['patient-sessions', recordUuid, 'all'],
|
|
queryFn: () => api.get(`/api/v1/patient/${recordUuid}/sessions?filter=all`),
|
|
enabled: !!recordUuid,
|
|
});
|
|
const session = (sessionsQ.data?.data ?? []).find((s) => s.uuid === sessionUuid);
|
|
|
|
const walletQ = useQuery<ApiResponse<{ balance_rials: number }>>({
|
|
queryKey: ['patient-wallet', recordUuid],
|
|
queryFn: () => api.get(`/api/v1/patient/${recordUuid}/wallet`),
|
|
enabled: !!recordUuid,
|
|
});
|
|
const walletBalance = (walletQ.data?.data as any)?.balance_rials ?? 0;
|
|
|
|
const finish = () => nav(`/admin/patients/${recordUuid}?tab=services`);
|
|
|
|
return (
|
|
<div className="fade-in" style={{ width: '100%' }}>
|
|
<Breadcrumb name={patientName} backTo={`/admin/patients/${recordUuid}`} />
|
|
|
|
<div style={{ width: '100%', display: 'flex', justifyContent: 'center' }}>
|
|
<div className="bg-[var(--surface)]" style={{ width: 748, maxWidth: '100%', padding: 24 }}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
|
<h2 style={{ fontSize: 16, fontWeight: 700 }}>ویرایش مراجعه</h2>
|
|
<button type="button" aria-label="بستن" onClick={() => nav(-1)}
|
|
style={{ minWidth: 40, height: 40, borderRadius: '50%', border: 'none', background: 'transparent', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<CloseModalD />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="seg" style={{ marginBottom: 16 }}>
|
|
<button className={tab === 'service' ? 'on' : ''} onClick={() => setTab('service')}>سرویسها</button>
|
|
<button className={tab === 'payment' ? 'on' : ''} onClick={() => setTab('payment')}>پرداختها</button>
|
|
</div>
|
|
|
|
{!session ? (
|
|
<div style={{ padding: '40px 0', textAlign: 'center', color: 'var(--text-3)', fontSize: 14 }}>در حال بارگذاری...</div>
|
|
) : tab === 'service' ? (
|
|
<CreateStep recordUuid={recordUuid} profile={record?.profile} editSession={session} onCreated={finish} onCancel={() => nav(-1)} />
|
|
) : (
|
|
<PaymentStep recordUuid={recordUuid} session={session} walletBalance={walletBalance} onContinue={finish} onCancel={() => nav(-1)} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|