frontend phase 13

This commit is contained in:
hamid
2026-07-10 16:58:15 +03:30
parent 6186f54294
commit 85488bc25b
57 changed files with 3283 additions and 105 deletions
@@ -0,0 +1,45 @@
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '../../theme';
import VisitNoteCard from './VisitNoteCard';
import type { VisitNote } from '@/services/patientRecords/types';
const NOTE: VisitNote = {
id: 8100,
bookingId: 5005,
nurseProfileId: 1,
nurseDisplayName: 'مریم رضایی',
body: 'وضعیت پایدار بود؛ داروها داده شد.',
taskResults: [
{ label: 'دادن متفورمین', done: true },
{ label: 'پیاده‌روی کوتاه', done: false },
],
recordedAt: '2026-06-01T09:00:00Z',
};
function renderCard(note: VisitNote = NOTE) {
return render(
<ThemeProvider>
<VisitNoteCard note={note} dateLabel="۱ خرداد ۱۴۰۵" authorFallback="پرستار" />
</ThemeProvider>,
);
}
describe('<VisitNoteCard/> component', () => {
it('renders the nurse name, date and body', () => {
renderCard();
expect(screen.getByText('مریم رضایی')).toBeInTheDocument();
expect(screen.getByText('۱ خرداد ۱۴۰۵')).toBeInTheDocument();
expect(screen.getByText('وضعیت پایدار بود؛ داروها داده شد.')).toBeInTheDocument();
});
it('renders each ticked task as a chip', () => {
renderCard();
expect(screen.getByText('دادن متفورمین')).toBeInTheDocument();
expect(screen.getByText('پیاده‌روی کوتاه')).toBeInTheDocument();
});
it('falls back to the provided author label when the nurse name is missing', () => {
renderCard({ ...NOTE, nurseDisplayName: null });
expect(screen.getByText('پرستار')).toBeInTheDocument();
});
});
@@ -0,0 +1,68 @@
'use client';
import { FunctionComponent } from 'react';
import Box from '@mui/material/Box';
import Chip from '@mui/material/Chip';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { AppIcon } from '@/components/common';
import type { VisitNote } from '@/services/patientRecords/types';
export interface VisitNoteCardProps {
/** One nurse-authored visit note (read-only from the client's perspective). */
note: VisitNote;
/** Pre-formatted Shamsi date/time — the caller owns locale. */
dateLabel: string;
/** Shown when the note has no recorded nurse name. */
authorFallback: string;
}
/**
* One read-only visit note in the longitudinal history — the nurse's display name, the Shamsi date, the note
* body, and (if any) the checklist the nurse ticked as done/not-done chips. Purely presentational (the caller
* formats the date and supplies the author fallback); reused by the customer سوابق tab and the nurse
* continuity view. Clinical text is rendered verbatim and never logged.
* @component VisitNoteCard
*/
const VisitNoteCard: FunctionComponent<VisitNoteCardProps> = ({ note, dateLabel, authorFallback }) => {
return (
<Paper elevation={0} sx={{ p: 2, border: '1px solid', borderColor: 'divider', borderRadius: 2 }}>
<Stack direction="row" sx={{ alignItems: 'center', gap: 1, mb: 1, flexWrap: 'wrap' }}>
<AppIcon icon="notes" size={18} color="var(--bal-primary)" />
<Typography variant="body2" sx={{ fontWeight: 700 }}>
{note.nurseDisplayName?.trim() || authorFallback}
</Typography>
<Typography variant="caption" sx={{ color: 'text.secondary', marginInlineStart: 'auto' }}>
{dateLabel}
</Typography>
</Stack>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{note.body}
</Typography>
{note.taskResults.length > 0 ? (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, mt: 1 }}>
{note.taskResults.map((task, index) => (
<Chip
key={`${task.label}-${index}`}
size="small"
variant="outlined"
icon={
<AppIcon
icon={task.done ? 'verified' : 'pending'}
size={14}
color={task.done ? 'var(--bal-success)' : 'var(--bal-text-secondary)'}
/>
}
label={task.label}
sx={{ color: task.done ? undefined : 'text.secondary' }}
/>
))}
</Box>
) : null}
</Paper>
);
};
export default VisitNoteCard;
@@ -0,0 +1,4 @@
import VisitNoteCard from './VisitNoteCard';
export type { VisitNoteCardProps } from './VisitNoteCard';
export { VisitNoteCard as default, VisitNoteCard };