mirror of
https://github.com/temetro/temetro.git
synced 2026-08-24 17:46:33 +00:00
feat: patient blood type & phone + clinic location setting (v0.9.0)
Patient record: - Add `bloodType` and `phone` to the patient model (schema, canonical types on both backend + frontend, zod validation). `phone` is a demographic field (reception can read/write); `bloodType` is clinical PHI, redacted for the reception role. Surface both in the record sheet, chat summary card, and the add/edit patient form. Migration 0033. Clinic location: - New org-scoped `clinic_settings` table (address/city/country + optional lat/long), service, and routes: GET /api/clinic/settings (any clinician) and PUT /api/clinic/location (owner/admin). Edited in Settings → Signing → Clinic location. Consumed later by the wallet app. Migration 0034. i18n: - Translate all new keys into every shipped locale (en/de/fr/ar/so) and document the "translate into every locale" rule in frontend/CLAUDE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -104,12 +104,18 @@ white/6%), so layered surfaces stay close in lightness.
|
||||
|
||||
## i18n
|
||||
|
||||
`i18next` + `react-i18next` (config in `lib/i18n/config.ts`, English resources in
|
||||
`lib/i18n/locales/en/translation.json`). `components/i18n-provider.tsx` wraps the app in
|
||||
`i18next` + `react-i18next` (config in `lib/i18n/config.ts`, resources in
|
||||
`lib/i18n/locales/<lng>/translation.json`). `components/i18n-provider.tsx` wraps the app in
|
||||
`app/layout.tsx`. Use `const { t } = useTranslation()` + nested keys (e.g. `t("auth.login.title")`)
|
||||
in **client** components. To add a language, drop a `locales/<lng>/translation.json` and register it
|
||||
in `resources`/`supportedLngs` in `config.ts`.
|
||||
|
||||
> **Translate into EVERY locale, not just English.** The app ships multiple languages
|
||||
> (`lib/i18n/locales/`: currently `en`, `de`, `fr`, `ar`, `so`). Whenever you add or rename a
|
||||
> translation key, add it to **all** `locales/*/translation.json` files with a real translation for
|
||||
> each language (not the English string copied over) — leaving a key in only `en/` ships a broken UI
|
||||
> in the others. Keep the nested structure identical across every locale file.
|
||||
|
||||
**Coverage:** essentially all user-facing strings are now keyed (every app page + its dialogs/sheets,
|
||||
auth pages, settings panels, the sidebar/user menu, chat input, patient cards/detail/form, messages,
|
||||
notifications, notes). Keys are grouped by feature (`appointments.*`, `patientCard.*`, `messages.*`,
|
||||
|
||||
@@ -243,6 +243,11 @@ function SummaryCard({
|
||||
label={t("patientCard.summary.allergies")}
|
||||
value={patient.allergies.length || t("patientCard.summary.none")}
|
||||
/>
|
||||
<Stat
|
||||
label={t("patientCard.summary.bloodType")}
|
||||
value={patient.bloodType || "—"}
|
||||
/>
|
||||
<Stat label={t("patientCard.summary.phone")} value={patient.phone || "—"} />
|
||||
</div>
|
||||
<AlertBadges alerts={patient.alerts} />
|
||||
{onEdit ? (
|
||||
|
||||
@@ -243,6 +243,8 @@ export function PatientFormDialog({
|
||||
// per-doctor visibility), not free text. `providerId` is the selected user id.
|
||||
const [providers, setProviders] = useState<Provider[]>([]);
|
||||
const [providerId, setProviderId] = useState(patient?.primaryProviderId ?? "");
|
||||
const [phone, setPhone] = useState(patient?.phone ?? "");
|
||||
const [bloodType, setBloodType] = useState(patient?.bloodType ?? "");
|
||||
const [bp, setBp] = useState(patient?.vitals.bp ?? "");
|
||||
const [hr, setHr] = useState(patient?.vitals.hr ?? "");
|
||||
const [temp, setTemp] = useState(patient?.vitals.temp ?? "");
|
||||
@@ -310,6 +312,8 @@ export function PatientFormDialog({
|
||||
primaryProviderId: providerId || null,
|
||||
status,
|
||||
initials: initialsFromName(name),
|
||||
phone: phone.trim(),
|
||||
bloodType,
|
||||
allergies: allergies.filter((a) => a.substance.trim()),
|
||||
alerts: patient?.alerts ?? [],
|
||||
medications: medications.filter((m) => m.name.trim()),
|
||||
@@ -515,6 +519,31 @@ export function PatientFormDialog({
|
||||
</select>
|
||||
</Field>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Field label={t("patientForm.phone")}>
|
||||
<Input
|
||||
inputMode="tel"
|
||||
onChange={(event) => setPhone(event.target.value)}
|
||||
placeholder={t("patientForm.phonePlaceholder")}
|
||||
value={phone}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={t("patientForm.bloodType")}>
|
||||
<select
|
||||
className={controlClass}
|
||||
onChange={(event) => setBloodType(event.target.value)}
|
||||
value={bloodType}
|
||||
>
|
||||
<option value="">{t("patientForm.bloodTypeUnknown")}</option>
|
||||
{["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"].map((bt) => (
|
||||
<option key={bt} value={bt}>
|
||||
{bt}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
{showClinical && (
|
||||
<>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
|
||||
@@ -359,6 +359,14 @@ export function PatientDetail({
|
||||
label={t("patientCard.summary.openProblems")}
|
||||
value={patient.problems.length}
|
||||
/>
|
||||
<Stat
|
||||
label={t("patientCard.summary.bloodType")}
|
||||
value={patient.bloodType || "—"}
|
||||
/>
|
||||
<Stat
|
||||
label={t("patientCard.summary.phone")}
|
||||
value={patient.phone || "—"}
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
SettingsSection,
|
||||
whiteButton,
|
||||
} from "@/components/settings/settings-parts";
|
||||
import { ClinicLocationSection } from "@/components/settings/settings-location";
|
||||
import {
|
||||
getNetworkEnabled,
|
||||
getSigningKey,
|
||||
@@ -205,6 +206,8 @@ export function SigningPanel() {
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
<ClinicLocationSection />
|
||||
|
||||
<SettingsSection
|
||||
description={t("settings.signing.identityDescription")}
|
||||
title={t("settings.signing.identityTitle")}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
FieldLabel,
|
||||
SettingsCard,
|
||||
SettingsSection,
|
||||
whiteButton,
|
||||
} from "@/components/settings/settings-parts";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getClinicSettings, saveClinicLocation } from "@/lib/clinic";
|
||||
import { notify } from "@/lib/toast";
|
||||
|
||||
// Parse a coordinate input into a number or null (empty ⇒ null). Returns
|
||||
// `false` when the string is present but not a finite number, so we can flag it.
|
||||
function parseCoord(value: string): number | null | false {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
const n = Number(trimmed);
|
||||
return Number.isFinite(n) ? n : false;
|
||||
}
|
||||
|
||||
// Clinic location editor (owner/admin only — mounted inside the Signing panel).
|
||||
// Persists the clinic's address + optional map coordinates so the wallet app can
|
||||
// display the clinic location later.
|
||||
export function ClinicLocationSection() {
|
||||
const { t } = useTranslation();
|
||||
const [address, setAddress] = useState("");
|
||||
const [city, setCity] = useState("");
|
||||
const [country, setCountry] = useState("");
|
||||
const [latitude, setLatitude] = useState("");
|
||||
const [longitude, setLongitude] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
getClinicSettings()
|
||||
.then((settings) => {
|
||||
if (!active) return;
|
||||
const loc = settings.location;
|
||||
setAddress(loc.address);
|
||||
setCity(loc.city);
|
||||
setCountry(loc.country);
|
||||
setLatitude(loc.latitude === null ? "" : String(loc.latitude));
|
||||
setLongitude(loc.longitude === null ? "" : String(loc.longitude));
|
||||
})
|
||||
.catch(() => {
|
||||
/* keep empty defaults */
|
||||
})
|
||||
.finally(() => {
|
||||
if (active) setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const save = async () => {
|
||||
const lat = parseCoord(latitude);
|
||||
const lng = parseCoord(longitude);
|
||||
if (lat === false || lng === false) {
|
||||
notify.error(
|
||||
t("settings.location.errorTitle"),
|
||||
t("settings.location.invalidCoords"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
try {
|
||||
await saveClinicLocation({
|
||||
address: address.trim(),
|
||||
city: city.trim(),
|
||||
country: country.trim(),
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
});
|
||||
notify.success(
|
||||
t("settings.location.savedTitle"),
|
||||
t("settings.location.savedBody"),
|
||||
);
|
||||
} catch {
|
||||
notify.error(
|
||||
t("settings.location.errorTitle"),
|
||||
t("settings.location.error"),
|
||||
);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
description={t("settings.location.description")}
|
||||
title={t("settings.location.title")}
|
||||
>
|
||||
<SettingsCard className="flex flex-col gap-4 p-5">
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<FieldLabel>{t("settings.location.address")}</FieldLabel>
|
||||
<Input
|
||||
disabled={loading}
|
||||
onChange={(e) => setAddress(e.target.value)}
|
||||
placeholder={t("settings.location.addressPlaceholder")}
|
||||
value={address}
|
||||
/>
|
||||
</label>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<FieldLabel>{t("settings.location.city")}</FieldLabel>
|
||||
<Input
|
||||
disabled={loading}
|
||||
onChange={(e) => setCity(e.target.value)}
|
||||
value={city}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<FieldLabel>{t("settings.location.country")}</FieldLabel>
|
||||
<Input
|
||||
disabled={loading}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
value={country}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<FieldLabel>{t("settings.location.latitude")}</FieldLabel>
|
||||
<Input
|
||||
disabled={loading}
|
||||
inputMode="decimal"
|
||||
onChange={(e) => setLatitude(e.target.value)}
|
||||
placeholder="e.g. 2.0469"
|
||||
value={latitude}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1.5">
|
||||
<FieldLabel>{t("settings.location.longitude")}</FieldLabel>
|
||||
<Input
|
||||
disabled={loading}
|
||||
inputMode="decimal"
|
||||
onChange={(e) => setLongitude(e.target.value)}
|
||||
placeholder="e.g. 45.3182"
|
||||
value={longitude}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("settings.location.coordinatesHint")}
|
||||
</p>
|
||||
<div>
|
||||
<Button
|
||||
className={cn("rounded-lg", whiteButton)}
|
||||
disabled={loading || saving}
|
||||
onClick={save}
|
||||
type="button"
|
||||
>
|
||||
{saving
|
||||
? t("settings.location.saving")
|
||||
: t("settings.location.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Client for clinic-level (organization) settings — currently the clinic's
|
||||
// location (Settings → Signing → Location). Calls the backend over the shared
|
||||
// fetch wrapper (session cookie sent automatically).
|
||||
|
||||
import { apiFetch } from "@/lib/api-client";
|
||||
|
||||
export type ClinicLocation = {
|
||||
address: string;
|
||||
city: string;
|
||||
country: string;
|
||||
latitude: number | null;
|
||||
longitude: number | null;
|
||||
};
|
||||
|
||||
export type ClinicSettings = {
|
||||
location: ClinicLocation;
|
||||
};
|
||||
|
||||
// The clinic's settings. Readable by any clinician; returns empty defaults when
|
||||
// nothing has been set yet.
|
||||
export async function getClinicSettings(): Promise<ClinicSettings> {
|
||||
return apiFetch<ClinicSettings>("/api/clinic/settings");
|
||||
}
|
||||
|
||||
// Save the clinic's location (owner/admin only). Returns the updated settings.
|
||||
export async function saveClinicLocation(
|
||||
location: ClinicLocation,
|
||||
): Promise<ClinicSettings> {
|
||||
return apiFetch<ClinicSettings>("/api/clinic/location", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(location),
|
||||
});
|
||||
}
|
||||
@@ -1463,6 +1463,8 @@
|
||||
"allergies": "الحساسيات",
|
||||
"activeMeds": "الأدوية النشطة",
|
||||
"openProblems": "المشكلات المفتوحة",
|
||||
"bloodType": "فصيلة الدم",
|
||||
"phone": "الهاتف",
|
||||
"none": "لا شيء",
|
||||
"editRecord": "تعديل السجل"
|
||||
},
|
||||
@@ -1565,6 +1567,10 @@
|
||||
"primaryCare": "الرعاية الأساسية",
|
||||
"primaryCarePlaceholder": "مثال: د. لينا أورتيز",
|
||||
"primaryCareUnassigned": "غير مُعيّن",
|
||||
"phone": "الهاتف",
|
||||
"phonePlaceholder": "مثال: +1 555 010 2938",
|
||||
"bloodType": "فصيلة الدم",
|
||||
"bloodTypeUnknown": "غير معروف",
|
||||
"currentVitals": "العلامات الحيوية الحالية",
|
||||
"bp": "ضغط الدم",
|
||||
"hr": "معدل ضربات القلب",
|
||||
@@ -2011,6 +2017,24 @@
|
||||
"errorTitle": "تعذّر تحديث الوصول إلى الشبكة",
|
||||
"error": "يرجى المحاولة مرة أخرى."
|
||||
},
|
||||
"location": {
|
||||
"title": "موقع العيادة",
|
||||
"description": "عنوان عيادتك وإحداثيات الخريطة. تظهر للمرضى في تطبيق المحفظة.",
|
||||
"address": "العنوان",
|
||||
"addressPlaceholder": "عنوان الشارع",
|
||||
"city": "المدينة",
|
||||
"country": "الدولة",
|
||||
"latitude": "خط العرض",
|
||||
"longitude": "خط الطول",
|
||||
"coordinatesHint": "الإحداثيات اختيارية — تُستخدم لعرض عيادتك على الخريطة.",
|
||||
"save": "حفظ الموقع",
|
||||
"saving": "جارٍ الحفظ…",
|
||||
"savedTitle": "تم حفظ الموقع",
|
||||
"savedBody": "تم تحديث موقع عيادتك.",
|
||||
"invalidCoords": "يجب أن يكون خطا العرض والطول أرقامًا.",
|
||||
"errorTitle": "تعذّر حفظ الموقع",
|
||||
"error": "يرجى المحاولة مرة أخرى."
|
||||
},
|
||||
"signing": {
|
||||
"keyTitle": "مفتاح التوقيع",
|
||||
"active": "نشط",
|
||||
|
||||
@@ -1443,6 +1443,8 @@
|
||||
"allergies": "Allergien",
|
||||
"activeMeds": "Aktive Medikamente",
|
||||
"openProblems": "Offene Probleme",
|
||||
"bloodType": "Blutgruppe",
|
||||
"phone": "Telefon",
|
||||
"none": "Keine",
|
||||
"editRecord": "Datensatz bearbeiten"
|
||||
},
|
||||
@@ -1545,6 +1547,10 @@
|
||||
"primaryCare": "Primärversorgung",
|
||||
"primaryCarePlaceholder": "z. B. Dr. Lena Ortiz",
|
||||
"primaryCareUnassigned": "Nicht zugewiesen",
|
||||
"phone": "Telefon",
|
||||
"phonePlaceholder": "z. B. +1 555 010 2938",
|
||||
"bloodType": "Blutgruppe",
|
||||
"bloodTypeUnknown": "Unbekannt",
|
||||
"currentVitals": "Aktuelle Vitalwerte",
|
||||
"bp": "Blutdruck",
|
||||
"hr": "Herzfrequenz",
|
||||
@@ -1991,6 +1997,24 @@
|
||||
"errorTitle": "Netzwerkzugriff konnte nicht aktualisiert werden",
|
||||
"error": "Bitte versuchen Sie es erneut."
|
||||
},
|
||||
"location": {
|
||||
"title": "Standort der Klinik",
|
||||
"description": "Adresse und Kartenkoordinaten Ihrer Klinik. Wird Patienten in der Wallet-App angezeigt.",
|
||||
"address": "Adresse",
|
||||
"addressPlaceholder": "Straße und Hausnummer",
|
||||
"city": "Stadt",
|
||||
"country": "Land",
|
||||
"latitude": "Breitengrad",
|
||||
"longitude": "Längengrad",
|
||||
"coordinatesHint": "Koordinaten sind optional – um Ihre Klinik auf einer Karte anzuzeigen.",
|
||||
"save": "Standort speichern",
|
||||
"saving": "Wird gespeichert …",
|
||||
"savedTitle": "Standort gespeichert",
|
||||
"savedBody": "Der Standort Ihrer Klinik wurde aktualisiert.",
|
||||
"invalidCoords": "Breiten- und Längengrad müssen Zahlen sein.",
|
||||
"errorTitle": "Standort konnte nicht gespeichert werden",
|
||||
"error": "Bitte versuchen Sie es erneut."
|
||||
},
|
||||
"signing": {
|
||||
"keyTitle": "Signierschlüssel",
|
||||
"active": "Aktiv",
|
||||
|
||||
@@ -1443,6 +1443,8 @@
|
||||
"allergies": "Allergies",
|
||||
"activeMeds": "Active meds",
|
||||
"openProblems": "Open problems",
|
||||
"bloodType": "Blood type",
|
||||
"phone": "Phone",
|
||||
"none": "None",
|
||||
"editRecord": "Edit record"
|
||||
},
|
||||
@@ -1545,6 +1547,10 @@
|
||||
"primaryCare": "Primary care",
|
||||
"primaryCarePlaceholder": "e.g. Dr. Lena Ortiz",
|
||||
"primaryCareUnassigned": "Unassigned",
|
||||
"phone": "Phone",
|
||||
"phonePlaceholder": "e.g. +1 555 010 2938",
|
||||
"bloodType": "Blood type",
|
||||
"bloodTypeUnknown": "Unknown",
|
||||
"currentVitals": "Current vitals",
|
||||
"bp": "Blood pressure",
|
||||
"hr": "Heart rate",
|
||||
@@ -1991,6 +1997,24 @@
|
||||
"errorTitle": "Couldn't update network access",
|
||||
"error": "Please try again."
|
||||
},
|
||||
"location": {
|
||||
"title": "Clinic location",
|
||||
"description": "Your clinic's address and map coordinates. Shown to patients in the wallet app.",
|
||||
"address": "Address",
|
||||
"addressPlaceholder": "Street address",
|
||||
"city": "City",
|
||||
"country": "Country",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"coordinatesHint": "Coordinates are optional — used to show your clinic on a map.",
|
||||
"save": "Save location",
|
||||
"saving": "Saving…",
|
||||
"savedTitle": "Location saved",
|
||||
"savedBody": "Your clinic location has been updated.",
|
||||
"invalidCoords": "Latitude and longitude must be numbers.",
|
||||
"errorTitle": "Couldn't save location",
|
||||
"error": "Please try again."
|
||||
},
|
||||
"signing": {
|
||||
"keyTitle": "Signing key",
|
||||
"active": "Active",
|
||||
|
||||
@@ -1443,6 +1443,8 @@
|
||||
"allergies": "Allergies",
|
||||
"activeMeds": "Médicaments actifs",
|
||||
"openProblems": "Problèmes ouverts",
|
||||
"bloodType": "Groupe sanguin",
|
||||
"phone": "Téléphone",
|
||||
"none": "Aucun",
|
||||
"editRecord": "Modifier le dossier"
|
||||
},
|
||||
@@ -1545,6 +1547,10 @@
|
||||
"primaryCare": "Praticien référent",
|
||||
"primaryCarePlaceholder": "ex. Dr Lena Ortiz",
|
||||
"primaryCareUnassigned": "Non attribué",
|
||||
"phone": "Téléphone",
|
||||
"phonePlaceholder": "p. ex. +1 555 010 2938",
|
||||
"bloodType": "Groupe sanguin",
|
||||
"bloodTypeUnknown": "Inconnu",
|
||||
"currentVitals": "Signes vitaux actuels",
|
||||
"bp": "Tension artérielle",
|
||||
"hr": "Fréquence cardiaque",
|
||||
@@ -1991,6 +1997,24 @@
|
||||
"errorTitle": "Impossible de mettre à jour l'accès au réseau",
|
||||
"error": "Veuillez réessayer."
|
||||
},
|
||||
"location": {
|
||||
"title": "Emplacement de la clinique",
|
||||
"description": "L'adresse et les coordonnées cartographiques de votre clinique. Affichées aux patients dans l'application wallet.",
|
||||
"address": "Adresse",
|
||||
"addressPlaceholder": "Adresse postale",
|
||||
"city": "Ville",
|
||||
"country": "Pays",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"coordinatesHint": "Les coordonnées sont facultatives — utilisées pour afficher votre clinique sur une carte.",
|
||||
"save": "Enregistrer l'emplacement",
|
||||
"saving": "Enregistrement…",
|
||||
"savedTitle": "Emplacement enregistré",
|
||||
"savedBody": "L'emplacement de votre clinique a été mis à jour.",
|
||||
"invalidCoords": "La latitude et la longitude doivent être des nombres.",
|
||||
"errorTitle": "Impossible d'enregistrer l'emplacement",
|
||||
"error": "Veuillez réessayer."
|
||||
},
|
||||
"signing": {
|
||||
"keyTitle": "Clé de signature",
|
||||
"active": "Active",
|
||||
|
||||
@@ -1443,6 +1443,8 @@
|
||||
"allergies": "Xasaasiyadaha",
|
||||
"activeMeds": "Daawooyin firfircoon",
|
||||
"openProblems": "Dhibaatooyin furan",
|
||||
"bloodType": "Nooca dhiigga",
|
||||
"phone": "Taleefanka",
|
||||
"none": "Midna",
|
||||
"editRecord": "Wax ka beddel diiwaanka"
|
||||
},
|
||||
@@ -1545,6 +1547,10 @@
|
||||
"primaryCare": "Daryeelka aasaasiga ah",
|
||||
"primaryCarePlaceholder": "tusaale Dr. Lena Ortiz",
|
||||
"primaryCareUnassigned": "Aan la qoondayn",
|
||||
"phone": "Taleefanka",
|
||||
"phonePlaceholder": "tusaale: +1 555 010 2938",
|
||||
"bloodType": "Nooca dhiigga",
|
||||
"bloodTypeUnknown": "Aan la garanayn",
|
||||
"currentVitals": "Calaamadaha muhiimka ah ee hadda",
|
||||
"bp": "Cadaadiska dhiigga",
|
||||
"hr": "Garaaca wadnaha",
|
||||
@@ -1991,6 +1997,24 @@
|
||||
"errorTitle": "Lama cusboonaysiin karin gelitaanka shabakadda",
|
||||
"error": "Fadlan isku day mar kale."
|
||||
},
|
||||
"location": {
|
||||
"title": "Goobta rugta caafimaadka",
|
||||
"description": "Cinwaanka iyo isutagga khariidadda rugtaada. Waxaa loo tusayaa bukaannada app-ka wallet-ka.",
|
||||
"address": "Cinwaanka",
|
||||
"addressPlaceholder": "Cinwaanka waddada",
|
||||
"city": "Magaalada",
|
||||
"country": "Dalka",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"coordinatesHint": "Isutaggu waa ikhtiyaari — waxaa loo isticmaalaa in rugtaada lagu tuso khariidad.",
|
||||
"save": "Kaydi goobta",
|
||||
"saving": "Waa la kaydinayaa…",
|
||||
"savedTitle": "Goobta waa la kaydiyay",
|
||||
"savedBody": "Goobta rugtaada waa la cusboonaysiiyay.",
|
||||
"invalidCoords": "Latitude iyo longitude waa inay noqdaan tirooyin.",
|
||||
"errorTitle": "Lama kaydin karin goobta",
|
||||
"error": "Fadlan mar kale isku day."
|
||||
},
|
||||
"signing": {
|
||||
"keyTitle": "Furaha saxiixa",
|
||||
"active": "Firfircoon",
|
||||
|
||||
@@ -66,6 +66,8 @@ export type Patient = {
|
||||
primaryProviderId?: string | null; // user id of the responsible clinician
|
||||
status: "active" | "inpatient" | "discharged";
|
||||
initials: string; // for AvatarFallback
|
||||
phone?: string; // contact number (demographic; visible to reception)
|
||||
bloodType?: string; // e.g. "O+"; clinical — redacted for reception
|
||||
allergies: Allergy[];
|
||||
alerts: string[];
|
||||
medications: Medication[];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.8.2",
|
||||
"version": "0.9.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
Reference in New Issue
Block a user