Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions components/schedule/SessionDetailModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,78 @@ function handleAddNote(patientId: string) {
noteModals.progressReport = true;
}

// ---------------------------------------------------------------
// Create patient report (tests used + diagnosis) for session
// ---------------------------------------------------------------
interface TherapyReportRow {
id: string;
patientId: string;
deliveredAt: string | null;
}

const reportsUrl = computed(() =>
props.session ? `/api/session/${props.session.id}/reports` : ""
);

const { data: sessionReports, refresh: refreshSessionReports } = await useFetch<
TherapyReportRow[]
>(reportsUrl, {
immediate: !!props.session,
default: () => [],
});

const reportsByPatientId = computed(() => {
const map = new Map<string, TherapyReportRow>();
for (const report of sessionReports.value ?? []) {
if (report?.patientId) map.set(report.patientId, report);
}
return map;
});

function getReportForPatient(patientId: string): TherapyReportRow | undefined {
return reportsByPatientId.value.get(patientId);
}

const createReportModalOpen = ref(false);
const reportPatientId = ref("");

function handleCreateReport(patientId: string) {
if (!props.session) return;
reportPatientId.value = patientId;
createReportModalOpen.value = true;
}

async function handleReportSave(data: {
testsUsed: string;
diagnosis: string;
}) {
if (!props.session) return;
try {
await $fetch("/api/session/reports", {
method: "POST",
body: {
patientId: reportPatientId.value,
sessionId: props.session.id,
testsUsed: data.testsUsed,
diagnosis: data.diagnosis,
},
});
await refreshSessionReports();
createReportModalOpen.value = false;
toast.add({
title: t("report.submitSuccess"),
color: "success",
icon: "i-lucide-circle-check",
});
} catch {
toast.add({
title: t("report.submitError"),
color: "error",
icon: "i-lucide-triangle-alert",
});
}
}

async function handleNoteSave(formData: Record<string, unknown>) {
const result = await saveTherapyNote(
formData,
Expand Down Expand Up @@ -691,6 +763,27 @@ const modalDescription = computed(() =>
>
{{ t("profile.columns.addNote") }}
</UButton>

<UButton
v-if="
!getReportForPatient(sp.patientId)
"
size="xs"
color="primary"
variant="soft"
icon="i-lucide-clipboard-plus"
@click="
handleCreateReport(sp.patientId)
"
>
{{ t("report.createButton") }}
</UButton>
<UBadge
v-else
color="neutral"
variant="subtle"
:label="t('report.reportSubmitted')"
/>
</template>

<UButton
Expand Down Expand Up @@ -854,4 +947,11 @@ const modalDescription = computed(() =>
v-model="noteModals.viewNote"
:note="activeNote"
/>

<TherapyCreateReportModal
v-if="canWriteNotes"
v-model="createReportModalOpen"
:patient-id="reportPatientId"
@save="handleReportSave"
/>
</template>
96 changes: 96 additions & 0 deletions components/therapy/CreateReportModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!-- Therapist: create a post-appointment report for a patient — which tests/
assessments were used and the resulting diagnosis/recommendation.
Coordinator (USER_SERVICE) staff later deliver it via
pages/report/reportsView.vue. -->
<script setup lang="ts">
const props = defineProps<{
modelValue: boolean;
patientId: string;
}>();

const emit = defineEmits<{
"update:modelValue": [value: boolean];
save: [data: { testsUsed: string; diagnosis: string }];
}>();

const { t } = useI18n();
const toast = useToast();

const testsUsed = ref("");
const diagnosis = ref("");

watch(
() => props.modelValue,
(open) => {
if (open) {
testsUsed.value = "";
diagnosis.value = "";
}
}
);

function handleSubmit() {
if (!testsUsed.value.trim() || !diagnosis.value.trim()) {
toast.add({
title: t("report.validationError"),
color: "error",
icon: "i-lucide-triangle-alert",
});
return;
}
emit("save", {
testsUsed: testsUsed.value.trim(),
diagnosis: diagnosis.value.trim(),
});
}
</script>

<template>
<UModal
:open="modelValue"
:title="t('report.createTitle')"
:ui="{ content: 'max-w-2xl' }"
@update:open="(v: boolean) => emit('update:modelValue', v)"
>
<template #body>
<form
id="create-report-form"
class="space-y-4"
@submit.prevent="handleSubmit"
>
<UFormField :label="t('report.testsUsedLabel')" required>
<UTextarea
v-model="testsUsed"
:rows="2"
:placeholder="t('report.testsUsedPlaceholder')"
class="w-full"
/>
</UFormField>
<UFormField :label="t('report.diagnosisLabel')" required>
<UTextarea
v-model="diagnosis"
:rows="5"
:placeholder="t('report.diagnosisPlaceholder')"
class="w-full"
/>
</UFormField>
</form>
</template>

<template #footer>
<div class="flex w-full justify-end gap-3">
<UButton
color="neutral"
variant="outline"
:label="t('profile.cancel')"
@click="emit('update:modelValue', false)"
/>
<UButton
type="submit"
form="create-report-form"
:label="t('report.submit')"
/>
</div>
</template>
</UModal>
</template>
4 changes: 4 additions & 0 deletions composables/auth/useUserLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export function useUserLinks() {
to: "session-sessionsView",
label: "Sessions",
});
legalRoutes.push({
to: "report-reportsView",
label: "Reports",
});
}

if (val[AP.ADMIN]) {
Expand Down
25 changes: 25 additions & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,31 @@
"submitSuccess": "Referral submitted for {name}.",
"submitError": "Failed to submit referral."
},
"report": {
"createTitle": "Create Report",
"createButton": "Create Report",
"reportSubmitted": "Report submitted",
"testsUsedLabel": "Tests / assessments used",
"testsUsedPlaceholder": "e.g. ADOS-2, Vineland-3, WISC-V",
"diagnosisLabel": "Diagnosis & recommendation",
"diagnosisPlaceholder": "Describe the diagnosis and what the patient needs going forward.",
"validationError": "Please fill in both fields before submitting.",
"submit": "Submit report",
"submitSuccess": "Report created.",
"submitError": "Failed to create report.",
"myTitle": "Reports",
"patient": "Patient",
"therapist": "Therapist",
"submitted": "Submitted",
"status": "Status",
"delivered": "Delivered",
"pendingDelivery": "Pending delivery",
"deliver": "Deliver",
"deliverSuccess": "Report delivered.",
"deliverError": "Failed to deliver report.",
"empty": "No reports yet.",
"loadError": "Failed to load reports."
},
"sessionModal": {
"session": "Session",
"therapist": "Therapist",
Expand Down
25 changes: 25 additions & 0 deletions i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,31 @@
"submitSuccess": "Informe enviado para {name}.",
"submitError": "No se pudo enviar el informe."
},
"report": {
"createTitle": "Crear informe",
"createButton": "Crear informe",
"reportSubmitted": "Informe enviado",
"testsUsedLabel": "Pruebas / evaluaciones utilizadas",
"testsUsedPlaceholder": "p. ej. ADOS-2, Vineland-3, WISC-V",
"diagnosisLabel": "Diagnóstico y recomendación",
"diagnosisPlaceholder": "Describa el diagnóstico y lo que el paciente necesita a continuación.",
"validationError": "Complete ambos campos antes de enviar.",
"submit": "Enviar informe",
"submitSuccess": "Informe creado.",
"submitError": "No se pudo crear el informe.",
"myTitle": "Informes",
"patient": "Paciente",
"therapist": "Terapeuta",
"submitted": "Enviado",
"status": "Estado",
"delivered": "Entregado",
"pendingDelivery": "Pendiente de entrega",
"deliver": "Entregar",
"deliverSuccess": "Informe entregado.",
"deliverError": "No se pudo entregar el informe.",
"empty": "Aún no hay informes.",
"loadError": "No se pudieron cargar los informes."
},
"sessionModal": {
"session": "Sesión",
"therapist": "Terapeuta",
Expand Down
Loading