From 0ca50cc5e0fa51be26a3766b2472d1533558134e Mon Sep 17 00:00:00 2001 From: aegis301 Date: Fri, 10 Apr 2026 09:51:20 +0200 Subject: [PATCH 1/5] fix sorting --- backend/api/query/adapters/patient.py | 10 ++++++---- web/utils/virtualDerivedTableState.ts | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/api/query/adapters/patient.py b/backend/api/query/adapters/patient.py index d153e613..1ea09471 100644 --- a/backend/api/query/adapters/patient.py +++ b/backend/api/query/adapters/patient.py @@ -275,10 +275,12 @@ def apply_patient_sorts( else models.Patient.lastname.asc() ) elif key == "name": - expr = patient_display_name_expr(models.Patient) - order_parts.append( - expr.desc().nulls_last() if desc_order else expr.asc().nulls_first() - ) + if desc_order: + order_parts.append(models.Patient.lastname.desc().nulls_last()) + order_parts.append(models.Patient.firstname.desc().nulls_last()) + else: + order_parts.append(models.Patient.lastname.asc().nulls_first()) + order_parts.append(models.Patient.firstname.asc().nulls_first()) elif key == "state": order_parts.append( _state_order_case().desc() diff --git a/web/utils/virtualDerivedTableState.ts b/web/utils/virtualDerivedTableState.ts index fc524f76..7922ff44 100644 --- a/web/utils/virtualDerivedTableState.ts +++ b/web/utils/virtualDerivedTableState.ts @@ -392,7 +392,9 @@ function comparePatientBySortId( const cmp = (x: number) => x * dir if (sortId === 'name') { - return cmp(a.name.localeCompare(b.name)) + const byLast = a.lastname.localeCompare(b.lastname) + if (byLast !== 0) return cmp(byLast) + return cmp(a.firstname.localeCompare(b.firstname)) } if (sortId === 'state') { return cmp(a.state.localeCompare(b.state)) From 71342e6b28f721987a61cfc59201549a348cda6e Mon Sep 17 00:00:00 2001 From: aegis301 Date: Fri, 10 Apr 2026 11:06:00 +0200 Subject: [PATCH 2/5] fix packages --- web/next.config.js | 8 ++++++++ web/package-lock.json | 16 ++++------------ web/package.json | 4 +--- web/postcss.config.mjs | 4 ---- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/web/next.config.js b/web/next.config.js index e9903218..0822ed72 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -1,8 +1,16 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + /** @type {import('next').NextConfig} */ const nextConfig = { distDir: 'build', reactStrictMode: true, output: 'standalone', + turbopack: { + root: __dirname, + }, images: { dangerouslyAllowSVG: true, remotePatterns: [ diff --git a/web/package-lock.json b/web/package-lock.json index bf46f8d9..9976fe28 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -19,6 +19,7 @@ "@tanstack/react-query-devtools": "5.91.2", "@tanstack/react-table": "8.21.3", "@types/formidable": "3.4.6", + "autoprefixer": "10.4.23", "clsx": "2.1.1", "dotenv": "17.2.3", "formidable": "3.5.4", @@ -28,6 +29,7 @@ "next-runtime-env": "2.0.1", "oidc-client-ts": "3.4.1", "postcss": "8.5.3", + "postcss-flexbugs-fixes": "5.0.2", "react": "19.2.3", "react-dom": "19.2.3", "sharp": "0.34.5", @@ -46,12 +48,10 @@ "@types/node": "20.17.10", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", - "autoprefixer": "10.4.23", "baseline-browser-mapping": "^2.9.19", "eslint": "9.39.1", "graphql": "16.12.0", - "graphql-request": "7.3.5", - "postcss-flexbugs-fixes": "5.0.2" + "graphql-request": "7.3.5" } }, "node_modules/@alloc/quick-lru": { @@ -5259,7 +5259,6 @@ "version": "10.4.23", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5351,7 +5350,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -6074,7 +6072,6 @@ "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", - "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { @@ -6311,7 +6308,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6815,7 +6811,6 @@ "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, "license": "MIT", "engines": { "node": "*" @@ -6829,6 +6824,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -9131,7 +9127,6 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, "license": "MIT" }, "node_modules/normalize-path": { @@ -9604,7 +9599,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "dev": true, "license": "MIT", "peerDependencies": { "postcss": "^8.1.4" @@ -9614,7 +9608,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -10796,7 +10789,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, "funding": [ { "type": "opencollective", diff --git a/web/package.json b/web/package.json index 2dc92562..e9ecc7cc 100644 --- a/web/package.json +++ b/web/package.json @@ -58,11 +58,9 @@ "@types/node": "20.17.10", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", - "autoprefixer": "10.4.23", "baseline-browser-mapping": "^2.9.19", "eslint": "9.39.1", "graphql": "16.12.0", - "graphql-request": "7.3.5", - "postcss-flexbugs-fixes": "5.0.2" + "graphql-request": "7.3.5" } } diff --git a/web/postcss.config.mjs b/web/postcss.config.mjs index 47994e4a..ae85b2fe 100644 --- a/web/postcss.config.mjs +++ b/web/postcss.config.mjs @@ -1,10 +1,6 @@ const config = { plugins: { '@tailwindcss/postcss': {}, - 'postcss-flexbugs-fixes': {}, - 'autoprefixer': { - flexbox: 'no-2009', - }, }, } From 5bc66b59c83f886cc29a6ab45a0586f25de77348 Mon Sep 17 00:00:00 2001 From: aegis301 Date: Tue, 21 Apr 2026 11:50:34 +0200 Subject: [PATCH 3/5] configs --- web/next.config.js | 8 ++++++++ web/package-lock.json | 1 + 2 files changed, 9 insertions(+) diff --git a/web/next.config.js b/web/next.config.js index e9903218..968c28fc 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -1,8 +1,16 @@ +import path from 'path' +import { fileURLToPath } from 'url' + +const turbopackRoot = path.dirname(fileURLToPath(import.meta.url)) + /** @type {import('next').NextConfig} */ const nextConfig = { distDir: 'build', reactStrictMode: true, output: 'standalone', + turbopack: { + root: turbopackRoot, + }, images: { dangerouslyAllowSVG: true, remotePatterns: [ diff --git a/web/package-lock.json b/web/package-lock.json index bf46f8d9..4c0eea1e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -6829,6 +6829,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, From 1b810f9ab89d5b30306c80e66b47761357402c45 Mon Sep 17 00:00:00 2001 From: aegis301 Date: Tue, 21 Apr 2026 13:05:35 +0200 Subject: [PATCH 4/5] fix patient clinic source --- AGENTS.md | 277 ++++++++++++++++++ backend/api/query/adapters/patient.py | 35 ++- web/components/tables/PatientList.tsx | 36 ++- .../views/TaskViewPatientsPanel.tsx | 1 + web/data/mockPatients.ts | 5 + ...overviewRecentPatientToPatientViewModel.ts | 1 + web/utils/virtualDerivedTableState.ts | 6 + 7 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..e62f67af --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,277 @@ +# AGENTS.md + +This document is the operational guide for AI agents contributing to `helpwave/tasks`. + +Use it as the source of truth for how to understand the repository, make safe changes, and deliver high-quality contributions with minimal back-and-forth. + +## 1) Mission and Product Context + +`helpwave/tasks` is an open-source healthcare operations platform for ward and task management. + +Primary goals: +- Organize clinical tasks and patient workflows +- Reflect ward/location hierarchy and team context +- Support real-time collaborative updates +- Maintain auditability and operational reliability + +Core architecture: +- `backend/`: FastAPI + Strawberry GraphQL API +- `web/`: Next.js (Pages Router) frontend +- `tests/`: Playwright E2E package +- `simulator/`: traffic/workflow simulator +- `proxy/`: Nginx reverse proxy for production-style routing +- `keycloak/`: identity provider config +- `scaffold/`: initial location tree and seed-like structure + +## 2) Ground Rules for All Agents + +1. Prefer precise, minimal changes over broad refactors unless explicitly requested. +2. Preserve existing behavior unless the task requires changing it. +3. Never commit secrets (`.env`, credentials, tokens, private keys). +4. Never run destructive git commands unless explicitly instructed. +5. Do not edit generated files manually unless the task explicitly asks for it. +6. Keep code clean, explicit, and reusable. +7. Do not add code comments unless explicitly requested. +8. Validate changes locally with relevant checks before finalizing. + +## 3) Repository Map and Ownership Boundaries + +### Backend (`backend/`) +- Language: Python (FastAPI, Strawberry GraphQL, SQLAlchemy async, Alembic) +- Responsibilities: + - GraphQL schema and resolvers + - Auth and request context + - Authorization logic (location-scoped visibility/access) + - Database persistence and migrations + - Redis-backed pub/sub notifications + - InfluxDB-backed audit logging + +Important paths: +- `backend/main.py` +- `backend/config.py` +- `backend/auth.py` +- `backend/api/context.py` +- `backend/api/resolvers/` +- `backend/api/services/authorization.py` +- `backend/database/models/` +- `backend/database/migrations/` + +### Frontend (`web/`) +- Language: TypeScript (Next.js + React) +- Responsibilities: + - UI routes/pages and app shell + - OIDC session handling and auth redirects + - GraphQL operations and cache synchronization + - Realtime subscriptions and optimistic updates + - Localization and theme/UX settings + +Important paths: +- `web/pages/_app.tsx` +- `web/hooks/useAuth.tsx` +- `web/api/auth/authService.ts` +- `web/providers/ApolloProviderWithData.tsx` +- `web/data/` +- `web/components/layout/Page.tsx` +- `web/utils/config.ts` +- `web/api/graphql/` +- `web/api/gql/generated.ts` (generated) +- `web/locales/` and `web/i18n/translations.ts` (generated output) + +### End-to-End Tests (`tests/`) +- Playwright tests and config for full user-flow verification. + +### Simulator (`simulator/`) +- Python tool to simulate realistic workflow traffic in development/testing. + +## 4) Local Development Workflow + +## Start infrastructure +From repository root: + +```bash +docker compose -f docker-compose.dev.yml up -d postgres redis keycloak influxdb +``` + +## Backend +```bash +cd backend +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +alembic upgrade head +uvicorn main:app --reload +``` + +## Frontend +```bash +cd web +npm install +npm run dev +``` + +## Optional simulator +```bash +cd simulator +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +python main.py +``` + +## 5) Required Generation Steps + +These are mandatory when relevant files change: + +1. If any `*.graphql` file changed in `web/`: +```bash +cd web +npm run generate-graphql +``` + +2. If any `*.arb` file changed in `web/locales/`: +```bash +cd web +npm run build-intl +``` + +Never skip these steps when applicable. + +## 6) Validation Checklist Before Finalizing + +Run only what is relevant to your change scope. + +### Frontend changes +```bash +cd web +npm run lint +npm run check-translations +``` + +### Backend changes +```bash +cd backend +pytest tests/unit -v +pytest tests/integration -v +``` + +### E2E-impacting changes +```bash +cd tests +npm install +npx playwright test +``` + +If you cannot run a check, explicitly state what you could not run and why. + +## 7) Change Strategy by Task Type + +### Feature work +- Follow existing domain patterns. +- Prefer extending existing service/hook/provider layers over introducing parallel systems. + +### Bug fixes +- Reproduce mentally or with tests. +- Fix at the narrowest reliable layer. +- Verify no regression in adjacent flows. + +### Refactors +- Keep behavior stable. +- Make incremental commits when requested. +- Avoid mixing refactor and feature changes unless required. + +### API and schema changes +- Update backend schema/resolvers/types coherently. +- Regenerate frontend GraphQL types/documents. +- Update consuming hooks/components. + +## 8) Architecture-Specific Guidance + +### Backend request flow +Standard flow: +1. Auth token/cookie extraction +2. User/context hydration +3. Authorization checks (location-scope) +4. Resolver/service execution +5. DB changes and optional audit emission +6. Redis pub/sub notifications for subscribers + +When changing behavior, identify which stage is affected and keep boundaries clear. + +### Frontend data flow +Standard flow: +1. UI event from page/component +2. Hook/provider mutation or query execution +3. Auth header attachment +4. GraphQL request over HTTP or subscription over WS +5. Cache update, optimistic reconciliation, and UI refresh + +When adding data access: +- Prefer existing patterns in `web/data/hooks/` and current provider setup. +- Keep cache consistency and subscription behavior in mind. + +## 9) Environment and Configuration Rules + +### Backend +- Review `backend/config.py` for canonical env contracts. +- Ensure local Redis URL matches compose password settings. +- Do not hardcode environment-specific URLs in source. + +### Frontend +- Use runtime config through `web/utils/config.ts`. +- Treat `RUNTIME_*` values as public runtime configuration. +- Keep local defaults and production runtime injection behavior intact. + +## 10) Code Quality and Style Expectations + +1. Use explicit typing where practical. +2. Keep functions/components focused and reusable. +3. Favor existing utility modules before adding new helpers. +4. Avoid dead code, hidden side effects, and unnecessary indirection. +5. Keep naming aligned with existing domain language (`task`, `patient`, `location`, `property`, `view`, `preset`). +6. Do not introduce comments unless asked; write self-explanatory code. + +## 11) Safe Git Practices for Agents + +1. Inspect current git status before making edits. +2. Do not revert unrelated user changes. +3. Stage only files relevant to the requested task. +4. Never force-push unless explicitly instructed. +5. Do not amend commits unless explicitly requested. +6. Do not create commits unless explicitly requested. + +## 12) Common Pitfalls + +1. Forgetting to run `npm run generate-graphql` after GraphQL document changes. +2. Forgetting to run `npm run build-intl` after locale ARB changes. +3. Assuming auth/permissions are purely frontend concerns instead of backend-enforced. +4. Breaking Redis-backed realtime paths by changing event channels without updating subscribers. +5. Mixing broad refactors with urgent bug fixes in one change. +6. Editing generated files directly. + +## 13) High-Value Files to Read First + +1. `README.md` +2. `backend/main.py` +3. `backend/config.py` +4. `backend/api/context.py` +5. `backend/api/services/authorization.py` +6. `backend/api/resolvers/__init__.py` +7. `backend/database/models/` +8. `web/package.json` +9. `web/pages/_app.tsx` +10. `web/hooks/useAuth.tsx` +11. `web/api/auth/authService.ts` +12. `web/providers/ApolloProviderWithData.tsx` +13. `web/data/client.ts` +14. `web/utils/config.ts` +15. `.github/workflows/tests.yml` + +## 14) Definition of Done for Agent Tasks + +A task is done when: +1. Requested behavior is implemented correctly. +2. Relevant generation steps are completed. +3. Relevant tests/lint/type checks pass (or limitations are clearly reported). +4. No unrelated files are modified. +5. Final handoff clearly states what changed and how it was validated. + diff --git a/backend/api/query/adapters/patient.py b/backend/api/query/adapters/patient.py index 1ea09471..d93c9b84 100644 --- a/backend/api/query/adapters/patient.py +++ b/backend/api/query/adapters/patient.py @@ -48,6 +48,15 @@ def _ensure_position_join(query: Select[Any], ctx: dict[str, Any]) -> tuple[Sele return query, ln +def _ensure_clinic_join(query: Select[Any], ctx: dict[str, Any]) -> tuple[Select[Any], Any]: + if "clinic_node" in ctx: + return query, ctx["clinic_node"] + ln = aliased(models.LocationNode) + ctx["clinic_node"] = ln + query = query.outerjoin(ln, models.Patient.clinic_id == ln.id) + return query, ln + + def _parse_property_key(field_key: str) -> str | None: if not field_key.startswith("property_"): return None @@ -55,7 +64,6 @@ def _parse_property_key(field_key: str) -> str | None: LOCATION_SORT_KEY_KINDS: dict[str, tuple[str, ...]] = { - "location-CLINIC": ("CLINIC", "PRACTICE"), "location-WARD": ("WARD",), "location-ROOM": ("ROOM",), "location-BED": ("BED",), @@ -63,7 +71,6 @@ def _parse_property_key(field_key: str) -> str | None: LOCATION_SORT_KEY_LABELS: dict[str, str] = { - "location-CLINIC": "Clinic", "location-WARD": "Ward", "location-ROOM": "Room", "location-BED": "Bed", @@ -233,6 +240,14 @@ def apply_patient_filter_clause( return query return query + if key == "clinic": + query, ln = _ensure_clinic_join(query, ctx) + expr = location_title_expr(ln) + c = apply_ops_to_column(expr, op, val) + if c is not None: + query = query.where(c) + return query + return query @@ -309,6 +324,12 @@ def apply_patient_sorts( order_parts.append( t.desc().nulls_last() if desc_order else t.asc().nulls_first() ) + elif key == "clinic": + query, ln = _ensure_clinic_join(query, ctx) + t = location_title_expr(ln) + order_parts.append( + t.desc().nulls_last() if desc_order else t.asc().nulls_first() + ) elif key in LOCATION_SORT_KEY_KINDS: query, lineage_nodes = _ensure_position_lineage_joins(query, ctx) t = _location_title_for_kind(lineage_nodes, LOCATION_SORT_KEY_KINDS[key]) @@ -473,6 +494,16 @@ def build_patient_queryable_fields_static() -> list[QueryableField]: sort_directions=sort_directions_for(True), searchable=True, ), + QueryableField( + key="clinic", + label="Clinic", + kind=QueryableFieldKind.SCALAR, + value_type=QueryableValueType.STRING, + allowed_operators=str_ops, + sortable=True, + sort_directions=sort_directions_for(True), + searchable=False, + ), QueryableField( key="position", label="Location", diff --git a/web/components/tables/PatientList.tsx b/web/components/tables/PatientList.tsx index 65bb7d32..393988a4 100644 --- a/web/components/tables/PatientList.tsx +++ b/web/components/tables/PatientList.tsx @@ -61,6 +61,7 @@ export type PatientViewModel = { name: string, firstname: string, lastname: string, + clinic: GetPatientsQuery['patients'][0]['clinic'] | null, position: GetPatientsQuery['patients'][0]['position'], openTasksCount: number, closedTasksCount: number, @@ -407,6 +408,7 @@ export const PatientList = forwardRef(({ initi name: p.name, firstname: p.firstname, lastname: p.lastname, + clinic: p.clinic ?? null, birthdate: new Date(p.birthdate), sex: p.sex, state: p.state, @@ -579,6 +581,28 @@ export const PatientList = forwardRef(({ initi size: 160, maxSize: 200, }, + { + id: 'clinic', + header: translation('clinic'), + accessorFn: ({ clinic }: PatientViewModel) => clinic?.title, + cell: ({ row }: { row: Row }) => { + if (refreshingPatientIds.has(row.original.id)) return rowLoadingCell + const clinic = row.original.clinic + return ( + <> + {clinic?.title} + + + ) + }, + minSize: 160, + size: 220, + maxSize: 300, + }, { id: 'position', header: translation('location'), @@ -596,7 +620,7 @@ export const PatientList = forwardRef(({ initi size: 260, maxSize: 320, }, - ...(['CLINIC', 'WARD', 'ROOM', 'BED'] as const).map((kind): ColumnDef => ({ + ...(['WARD', 'ROOM', 'BED'] as const).map((kind): ColumnDef => ({ id: `location-${kind}`, header: translation(LOCATION_KIND_HEADERS[kind] as 'locationClinic' | 'locationWard' | 'locationRoom' | 'locationBed'), accessorFn: (row: PatientViewModel) => { @@ -760,8 +784,8 @@ export const PatientList = forwardRef(({ initi 'birthdate': translation('birthdate'), 'sex': translation('sex'), 'state': translation('status'), + 'clinic': translation('clinic'), 'position': translation('location'), - 'location-CLINIC': translation('locationClinic'), 'location-WARD': translation('locationWard'), 'location-ROOM': translation('locationRoom'), 'location-BED': translation('locationBed'), @@ -807,6 +831,12 @@ export const PatientList = forwardRef(({ initi dataType: 'singleTag', tags: allPatientStates.map(state => ({ label: translation('patientState', { state: state as string }), tag: state })), }, + { + id: 'clinic', + label: translation('clinic'), + dataType: 'text', + tags: [], + }, { id: 'sex', label: translation('sex'), @@ -817,7 +847,7 @@ export const PatientList = forwardRef(({ initi { label: translation('diverse'), tag: Sex.Unknown }, ], }, - ...(['CLINIC', 'WARD', 'ROOM', 'BED'] as const).map((kind): FilterListItem => ({ + ...(['WARD', 'ROOM', 'BED'] as const).map((kind): FilterListItem => ({ id: `location-${kind}`, label: translation(LOCATION_KIND_HEADERS[kind] as 'locationClinic' | 'locationWard' | 'locationRoom' | 'locationBed'), dataType: 'text', diff --git a/web/components/views/TaskViewPatientsPanel.tsx b/web/components/views/TaskViewPatientsPanel.tsx index 70538501..290ec0a7 100644 --- a/web/components/views/TaskViewPatientsPanel.tsx +++ b/web/components/views/TaskViewPatientsPanel.tsx @@ -41,6 +41,7 @@ function buildEmbeddedPatientsFromTasks(tasks: GetTasksQuery['tasks']): PatientV birthdate: new Date(patient.birthdate), sex: patient.sex, state: patient.state, + clinic: patient.clinic, position: patient.position, openTasksCount: countForAggregate ? open : 0, closedTasksCount: countForAggregate ? closed : 0, diff --git a/web/data/mockPatients.ts b/web/data/mockPatients.ts index 4971daef..6867bd09 100644 --- a/web/data/mockPatients.ts +++ b/web/data/mockPatients.ts @@ -18,6 +18,7 @@ export const MOCK_PATIENT_A: PatientViewModel = { name: 'Patient A', firstname: 'Patient', lastname: 'A', + clinic: null, position: null, openTasksCount: 0, closedTasksCount: 0, @@ -33,6 +34,7 @@ export const MOCK_PATIENT_B: PatientViewModel = { name: 'Patient B', firstname: 'Patient', lastname: 'B', + clinic: null, position: null, openTasksCount: 0, closedTasksCount: 0, @@ -48,6 +50,7 @@ export const MOCK_PATIENT_C: PatientViewModel = { name: 'Patient C', firstname: 'Patient', lastname: 'C', + clinic: null, position: null, openTasksCount: 0, closedTasksCount: 0, @@ -63,6 +66,7 @@ export const MOCK_PATIENT_D: PatientViewModel = { name: 'Patient D', firstname: 'Patient', lastname: 'D', + clinic: null, position: null, openTasksCount: 0, closedTasksCount: 0, @@ -78,6 +82,7 @@ export const MOCK_PATIENT_E: PatientViewModel = { name: 'Patient E', firstname: 'Patient', lastname: 'E', + clinic: null, position: null, openTasksCount: 0, closedTasksCount: 0, diff --git a/web/utils/overviewRecentPatientToPatientViewModel.ts b/web/utils/overviewRecentPatientToPatientViewModel.ts index 374aadf1..b4e38a1b 100644 --- a/web/utils/overviewRecentPatientToPatientViewModel.ts +++ b/web/utils/overviewRecentPatientToPatientViewModel.ts @@ -17,6 +17,7 @@ export function overviewRecentPatientToPatientViewModel(p: OverviewRecentPatient birthdate: new Date(p.birthdate), sex: p.sex, state: p.state, + clinic: null, position: p.position as PatientViewModel['position'], openTasksCount: countForAggregate ? tasks.filter(t => !t.done).length : 0, closedTasksCount: countForAggregate ? tasks.filter(t => t.done).length : 0, diff --git a/web/utils/virtualDerivedTableState.ts b/web/utils/virtualDerivedTableState.ts index 7922ff44..04838370 100644 --- a/web/utils/virtualDerivedTableState.ts +++ b/web/utils/virtualDerivedTableState.ts @@ -271,6 +271,9 @@ function patientMatchesColumnFilter(patient: PatientViewModel, filter: ColumnFil } return matchesTextOperator(patient.position?.title ?? '', op, fv.parameter.stringValue ?? '') } + if (id === 'clinic') { + return matchesTextOperator(patient.clinic?.title ?? '', op, fv.parameter.stringValue ?? '') + } if (id === 'tasks') { const open = patient.openTasksCount const closed = patient.closedTasksCount @@ -411,6 +414,9 @@ function comparePatientBySortId( if (sortId === 'position') { return cmp((a.position?.title ?? '').localeCompare(b.position?.title ?? '')) } + if (sortId === 'clinic') { + return cmp((a.clinic?.title ?? '').localeCompare(b.clinic?.title ?? '')) + } if (sortId === 'tasks') { const ta = a.openTasksCount + a.closedTasksCount const tb = b.openTasksCount + b.closedTasksCount From 1fa21dc966520c361756d099450794db668ef4a5 Mon Sep 17 00:00:00 2001 From: aegis301 Date: Tue, 21 Apr 2026 13:08:43 +0200 Subject: [PATCH 5/5] restore --- web/next.config.js | 8 -------- web/package.json | 4 +++- web/postcss.config.mjs | 4 ++++ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/web/next.config.js b/web/next.config.js index 968c28fc..e9903218 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -1,16 +1,8 @@ -import path from 'path' -import { fileURLToPath } from 'url' - -const turbopackRoot = path.dirname(fileURLToPath(import.meta.url)) - /** @type {import('next').NextConfig} */ const nextConfig = { distDir: 'build', reactStrictMode: true, output: 'standalone', - turbopack: { - root: turbopackRoot, - }, images: { dangerouslyAllowSVG: true, remotePatterns: [ diff --git a/web/package.json b/web/package.json index e9ecc7cc..2dc92562 100644 --- a/web/package.json +++ b/web/package.json @@ -58,9 +58,11 @@ "@types/node": "20.17.10", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", + "autoprefixer": "10.4.23", "baseline-browser-mapping": "^2.9.19", "eslint": "9.39.1", "graphql": "16.12.0", - "graphql-request": "7.3.5" + "graphql-request": "7.3.5", + "postcss-flexbugs-fixes": "5.0.2" } } diff --git a/web/postcss.config.mjs b/web/postcss.config.mjs index ae85b2fe..47994e4a 100644 --- a/web/postcss.config.mjs +++ b/web/postcss.config.mjs @@ -1,6 +1,10 @@ const config = { plugins: { '@tailwindcss/postcss': {}, + 'postcss-flexbugs-fixes': {}, + 'autoprefixer': { + flexbox: 'no-2009', + }, }, }