33800292aa
- apps/api/Dockerfile: build NestJS, run prisma migrate deploy on start - apps/web/Dockerfile + nginx.conf: build Vite, serve static, proxy /api -> api - docker-compose.coolify.yml: full prod stack (postgres, redis, minio, keycloak, api, web) - .dockerignore / .gitignore / .gitattributes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
97 lines
3.9 KiB
TypeScript
97 lines
3.9 KiB
TypeScript
import { Box, Button, Text, Group } from '@mantine/core';
|
|
import dayjs from 'dayjs';
|
|
import type { Qualification } from '../../../api/types';
|
|
|
|
const font = "'Montserrat', Arial, sans-serif";
|
|
const charcoal = '#58595b';
|
|
const teal = '#008286';
|
|
const border = '#e9ecef';
|
|
|
|
const CAT_LABELS: Record<string, string> = {
|
|
fara: 'Fără categorie',
|
|
cat_II: 'Categoria II',
|
|
cat_I: 'Categoria I',
|
|
superioara: 'Superioară',
|
|
};
|
|
|
|
function expiryStyle(dateStr: string | null): React.CSSProperties {
|
|
if (!dateStr) return {};
|
|
const days = dayjs(dateStr).diff(dayjs(), 'day');
|
|
if (days < 0) return { color: '#b11116', fontWeight: 600 };
|
|
if (days < 30) return { color: '#f15a31', fontWeight: 600 };
|
|
if (days < 90) return { color: '#fbb034', fontWeight: 500 };
|
|
return {};
|
|
}
|
|
|
|
interface Props {
|
|
qualifications: Qualification[];
|
|
onAdd: () => void;
|
|
onEdit: (q: Qualification) => void;
|
|
}
|
|
|
|
export function CalificariTab({ qualifications, onAdd, onEdit }: Props) {
|
|
return (
|
|
<Box>
|
|
<Group justify="flex-end" mb={12}>
|
|
<Button size="xs" onClick={onAdd} style={{ background: teal, fontFamily: font, fontWeight: 500 }}>
|
|
+ Adaugă calificare
|
|
</Button>
|
|
</Group>
|
|
|
|
{qualifications.length === 0 ? (
|
|
<Text c="#adb5bd" ta="center" py={32} style={{ fontFamily: font, fontWeight: 300 }}>
|
|
Nicio calificare înregistrată
|
|
</Text>
|
|
) : (
|
|
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
|
<thead>
|
|
<tr style={{ background: '#fafafa' }}>
|
|
{['Categorie', 'Specialitate', 'Data obținerii', 'Ultima confirmare', 'Expiră', ''].map((h, i) => (
|
|
<th key={i} style={{
|
|
fontFamily: font, fontWeight: 600, fontSize: '0.7rem',
|
|
textTransform: 'uppercase', letterSpacing: '0.06em', color: charcoal,
|
|
padding: '10px 14px', textAlign: 'left', borderBottom: `2px solid ${teal}`,
|
|
}}>
|
|
{h}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{qualifications.map((q) => (
|
|
<tr key={q.id} style={{ borderBottom: `1px solid ${border}` }}>
|
|
<td style={{ padding: '11px 14px' }}>
|
|
<Box style={{
|
|
display: 'inline-flex', padding: '2px 8px', borderRadius: 20,
|
|
background: '#e6f4f4', color: teal,
|
|
fontFamily: font, fontWeight: 600, fontSize: '0.75rem',
|
|
}}>
|
|
{CAT_LABELS[q.categorie] ?? q.categorie}
|
|
</Box>
|
|
</td>
|
|
<td style={{ padding: '11px 14px', fontFamily: font, fontWeight: 300, fontSize: '0.875rem', color: charcoal }}>
|
|
{q.specialitate ?? '—'}
|
|
</td>
|
|
<td style={{ padding: '11px 14px', fontFamily: font, fontWeight: 300, fontSize: '0.875rem', color: charcoal }}>
|
|
{q.dataObtinerii ? dayjs(q.dataObtinerii).format('DD.MM.YYYY') : '—'}
|
|
</td>
|
|
<td style={{ padding: '11px 14px', fontFamily: font, fontWeight: 300, fontSize: '0.875rem', color: charcoal }}>
|
|
{q.dataUltimeiConfirmari ? dayjs(q.dataUltimeiConfirmari).format('DD.MM.YYYY') : '—'}
|
|
</td>
|
|
<td style={{ padding: '11px 14px', fontFamily: font, fontSize: '0.875rem', ...expiryStyle(q.dataExpirarii) }}>
|
|
{q.dataExpirarii ? dayjs(q.dataExpirarii).format('DD.MM.YYYY') : '—'}
|
|
</td>
|
|
<td style={{ padding: '11px 14px', textAlign: 'right' }}>
|
|
<Text size="xs" c={teal} style={{ fontFamily: font, fontWeight: 500, cursor: 'pointer' }} onClick={() => onEdit(q)}>
|
|
Editează
|
|
</Text>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|