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
@@ -1,11 +1,10 @@
'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 { AppIconButton } from '@/components/common';
import PatientHeader from '@/components/PatientHeader';
import type { Patient } from '@/services/patients/types';
export interface PatientCardProps {
@@ -23,11 +22,17 @@ export interface PatientCardProps {
onArchive: () => void;
editLabel: string;
archiveLabel: string;
/** When provided, tapping the identity area opens the patient's record (E2). The action buttons are unaffected. */
onOpen?: () => void;
/** Accessible label for the tappable identity area (required when `onOpen` is set). */
openLabel?: string;
}
/**
* Patient summary card for the E1 list — relation + name, age/gender, and condition chips,
* with edit and archive actions. All display text is translated by the caller.
* Patient summary card for the E1 list — the shared `PatientHeader` (relation + name, age/gender, condition
* chips) plus edit and archive actions. When `onOpen` is passed the identity area becomes a button that opens
* the E2 record viewer; the edit/archive icon buttons keep their own handlers. All display text is translated
* by the caller.
* @component PatientCard
*/
const PatientCard: FunctionComponent<PatientCardProps> = ({
@@ -41,40 +46,46 @@ const PatientCard: FunctionComponent<PatientCardProps> = ({
onArchive,
editLabel,
archiveLabel,
onOpen,
openLabel,
}) => {
const meta = [ageLabel, genderLabel].filter(Boolean).join(' · ');
const header = (
<PatientHeader
displayName={patient.displayName}
relationLabel={relationLabel}
genderLabel={genderLabel}
ageLabel={ageLabel}
conditionLabels={conditionLabels}
noConditionsLabel={noConditionsLabel}
/>
);
return (
<Paper elevation={0} sx={{ p: 2, border: '1px solid', borderColor: 'divider', borderRadius: 2 }}>
<Stack direction="row" sx={{ alignItems: 'flex-start', gap: 1 }}>
<Stack sx={{ flexGrow: 1, gap: 0.75, minWidth: 0 }}>
<Stack direction="row" sx={{ alignItems: 'center', gap: 1, flexWrap: 'wrap' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
{patient.displayName}
</Typography>
{relationLabel ? (
<Chip size="small" label={relationLabel} sx={{ bgcolor: 'var(--bal-primary-soft)', fontWeight: 600 }} />
) : null}
</Stack>
{meta ? (
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
{meta}
</Typography>
) : null}
{conditionLabels.length > 0 ? (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, mt: 0.25 }}>
{conditionLabels.map((label) => (
<Chip key={label} size="small" variant="outlined" label={label} />
))}
</Box>
) : (
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
{noConditionsLabel}
</Typography>
)}
</Stack>
{onOpen ? (
<Box
component="button"
type="button"
onClick={onOpen}
aria-label={openLabel}
sx={{
flexGrow: 1,
minWidth: 0,
textAlign: 'start',
background: 'none',
border: 'none',
p: 0,
cursor: 'pointer',
font: 'inherit',
color: 'inherit',
}}
>
{header}
</Box>
) : (
<Box sx={{ flexGrow: 1, minWidth: 0 }}>{header}</Box>
)}
<Stack direction="row" sx={{ flexShrink: 0 }}>
<AppIconButton icon="edit" title={editLabel} aria-label={editLabel} size="small" onClick={onEdit} />