| [700e2f9] | 1 | import {
|
|---|
| 2 | type Component,
|
|---|
| 3 | createEffect,
|
|---|
| 4 | createResource,
|
|---|
| 5 | createSignal,
|
|---|
| 6 | Show,
|
|---|
| 7 | } from "solid-js";
|
|---|
| 8 | import { useNavigate } from "@solidjs/router";
|
|---|
| 9 | import { useAuth } from "@/context/AuthContext";
|
|---|
| 10 | import {
|
|---|
| 11 | consultationApi,
|
|---|
| 12 | type Consultation,
|
|---|
| 13 | type CreateConsultationRequest,
|
|---|
| 14 | type UpdateConsultationRequest,
|
|---|
| 15 | isConsultationPaid,
|
|---|
| 16 | } from "@/api/consultation";
|
|---|
| 17 | import ConsultationTable from "../components/ConsultationTable";
|
|---|
| 18 | import ConsultationModal from "../components/ConsultationModal";
|
|---|
| 19 | import { patientApi } from "@/api/patient";
|
|---|
| 20 | import { UserType } from "@/enums/UserType";
|
|---|
| 21 | import type { Therapy } from "@/api/therapy";
|
|---|
| 22 |
|
|---|
| 23 | const Consultations: Component = () => {
|
|---|
| 24 | const { user, isAuthenticated } = useAuth();
|
|---|
| 25 | const navigate = useNavigate();
|
|---|
| 26 |
|
|---|
| 27 | const [isModalOpen, setIsModalOpen] = createSignal(false);
|
|---|
| 28 | const [editingConsultation, setEditingConsultation] =
|
|---|
| 29 | createSignal<Consultation | null>(null);
|
|---|
| 30 | const [formData, setFormData] = createSignal({
|
|---|
| 31 | patientId: 0,
|
|---|
| 32 | date: "",
|
|---|
| 33 | price: 0,
|
|---|
| 34 | advice: "",
|
|---|
| 35 | dateOfPayment: null as string | null,
|
|---|
| 36 | });
|
|---|
| 37 | const [newTherapies, setNewTherapies] = createSignal<Therapy[]>([]);
|
|---|
| 38 | const [existingTherapies, setExistingTherapies] = createSignal<Therapy[]>([]);
|
|---|
| 39 |
|
|---|
| 40 | const isTherapist = () => user()?.userType === UserType.THERAPIST;
|
|---|
| 41 | const isPatient = () => user()?.userType === UserType.PATIENT;
|
|---|
| 42 |
|
|---|
| 43 | const [patients] = createResource(
|
|---|
| 44 | () => ({
|
|---|
| 45 | authenticated: isAuthenticated(),
|
|---|
| 46 | isTherapist: isTherapist(),
|
|---|
| 47 | }),
|
|---|
| 48 | async (params) => {
|
|---|
| 49 | if (!params.authenticated || !params.isTherapist) return [];
|
|---|
| 50 | return await patientApi.getAllPatients();
|
|---|
| 51 | },
|
|---|
| 52 | );
|
|---|
| 53 |
|
|---|
| 54 | const [consultations, { refetch }] = createResource(
|
|---|
| 55 | () => ({
|
|---|
| 56 | authenticated: isAuthenticated(),
|
|---|
| 57 | userId: user()?.userId,
|
|---|
| 58 | isTherapist: isTherapist(),
|
|---|
| 59 | isPatient: isPatient(),
|
|---|
| 60 | }),
|
|---|
| 61 | async (params) => {
|
|---|
| 62 | if (!params.authenticated || !params.userId) return [];
|
|---|
| 63 |
|
|---|
| 64 | if (params.isTherapist) {
|
|---|
| 65 | return await consultationApi.getTherapistConsultations(params.userId);
|
|---|
| 66 | } else if (params.isPatient) {
|
|---|
| 67 | return await consultationApi.getPatientConsultations(params.userId);
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | return [];
|
|---|
| 71 | },
|
|---|
| 72 | );
|
|---|
| 73 |
|
|---|
| 74 | createEffect(() => {
|
|---|
| 75 | if (!isAuthenticated()) {
|
|---|
| 76 | navigate("/login", { replace: true });
|
|---|
| 77 | }
|
|---|
| 78 | });
|
|---|
| 79 |
|
|---|
| 80 | const openCreateModal = () => {
|
|---|
| 81 | setEditingConsultation(null);
|
|---|
| 82 | setNewTherapies([]);
|
|---|
| 83 | setExistingTherapies([]);
|
|---|
| 84 | const today = new Date().toISOString().split("T")[0];
|
|---|
| 85 | setFormData({
|
|---|
| 86 | patientId: patients()?.[0]?.userId || 0,
|
|---|
| 87 | date: today,
|
|---|
| 88 | price: 0,
|
|---|
| 89 | advice: "",
|
|---|
| 90 | dateOfPayment: null,
|
|---|
| 91 | });
|
|---|
| 92 | setIsModalOpen(true);
|
|---|
| 93 | };
|
|---|
| 94 |
|
|---|
| 95 | const openEditModal = (consultation: Consultation) => {
|
|---|
| 96 | setEditingConsultation(consultation);
|
|---|
| 97 | setExistingTherapies(consultation.therapies || []);
|
|---|
| 98 | setNewTherapies([]);
|
|---|
| 99 | setFormData({
|
|---|
| 100 | patientId: consultation.patientId,
|
|---|
| 101 | date: consultation.date,
|
|---|
| 102 | price: consultation.price,
|
|---|
| 103 | advice: consultation.advice,
|
|---|
| 104 | dateOfPayment: consultation.dateOfPayment,
|
|---|
| 105 | });
|
|---|
| 106 | setIsModalOpen(true);
|
|---|
| 107 | };
|
|---|
| 108 |
|
|---|
| 109 | const closeModal = () => {
|
|---|
| 110 | setIsModalOpen(false);
|
|---|
| 111 | setEditingConsultation(null);
|
|---|
| 112 | };
|
|---|
| 113 |
|
|---|
| 114 | const handleSubmit = async (e: Event) => {
|
|---|
| 115 | e.preventDefault();
|
|---|
| 116 |
|
|---|
| 117 | try {
|
|---|
| 118 | const data = formData();
|
|---|
| 119 | const therapiesToSend = newTherapies().map((t) => ({
|
|---|
| 120 | name: t.name,
|
|---|
| 121 | dose: t.dose,
|
|---|
| 122 | expDate: t.expDate,
|
|---|
| 123 | }));
|
|---|
| 124 |
|
|---|
| 125 | if (editingConsultation()) {
|
|---|
| 126 | const updateData: UpdateConsultationRequest = {
|
|---|
| 127 | date: data.date,
|
|---|
| 128 | price: data.price,
|
|---|
| 129 | advice: data.advice,
|
|---|
| 130 | dateOfPayment: data.dateOfPayment,
|
|---|
| 131 | therapies: therapiesToSend,
|
|---|
| 132 | };
|
|---|
| 133 | await consultationApi.updateConsultation(
|
|---|
| 134 | editingConsultation()!.idConsultation,
|
|---|
| 135 | updateData,
|
|---|
| 136 | );
|
|---|
| 137 | } else {
|
|---|
| 138 | const createData: CreateConsultationRequest = {
|
|---|
| 139 | patientId: data.patientId,
|
|---|
| 140 | date: data.date,
|
|---|
| 141 | price: data.price,
|
|---|
| 142 | advice: data.advice,
|
|---|
| 143 | dateOfPayment: data.dateOfPayment,
|
|---|
| 144 | therapies: therapiesToSend,
|
|---|
| 145 | };
|
|---|
| 146 | await consultationApi.createConsultation(createData);
|
|---|
| 147 | }
|
|---|
| 148 |
|
|---|
| 149 | refetch();
|
|---|
| 150 | closeModal();
|
|---|
| 151 | } catch (error: any) {
|
|---|
| 152 | alert(error.message || "Failed to save consultation");
|
|---|
| 153 | }
|
|---|
| 154 | };
|
|---|
| 155 |
|
|---|
| 156 | const handleDelete = async (consultationId: number) => {
|
|---|
| 157 | if (!confirm("Are you sure you want to delete this consultation?")) return;
|
|---|
| 158 |
|
|---|
| 159 | try {
|
|---|
| 160 | await consultationApi.deleteConsultation(consultationId);
|
|---|
| 161 | refetch();
|
|---|
| 162 | } catch (error: any) {
|
|---|
| 163 | alert(error.message || "Failed to delete consultation");
|
|---|
| 164 | }
|
|---|
| 165 | };
|
|---|
| 166 |
|
|---|
| 167 | const togglePaymentStatus = async (consultation: Consultation) => {
|
|---|
| 168 | try {
|
|---|
| 169 | const updateData: UpdateConsultationRequest = {
|
|---|
| 170 | dateOfPayment: isConsultationPaid(consultation)
|
|---|
| 171 | ? null
|
|---|
| 172 | : new Date().toISOString().split("T")[0],
|
|---|
| 173 | };
|
|---|
| 174 | await consultationApi.updateConsultation(
|
|---|
| 175 | consultation.idConsultation,
|
|---|
| 176 | updateData,
|
|---|
| 177 | );
|
|---|
| 178 | refetch();
|
|---|
| 179 | } catch (error: any) {
|
|---|
| 180 | alert(error.message || "Failed to update payment status");
|
|---|
| 181 | }
|
|---|
| 182 | };
|
|---|
| 183 |
|
|---|
| 184 | return (
|
|---|
| 185 | <div class="container mx-auto px-4 py-8 max-w-7xl">
|
|---|
| 186 | <div class="mb-6 flex justify-between items-center">
|
|---|
| 187 | <h1 class="text-3xl font-bold text-gray-900">
|
|---|
| 188 | {isTherapist() ? "Consultation Records" : "My Consultations"}
|
|---|
| 189 | </h1>
|
|---|
| 190 | <Show when={isTherapist()}>
|
|---|
| 191 | <button
|
|---|
| 192 | onClick={openCreateModal}
|
|---|
| 193 | class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition cursor-pointer"
|
|---|
| 194 | >
|
|---|
| 195 | Log New Consultation
|
|---|
| 196 | </button>
|
|---|
| 197 | </Show>
|
|---|
| 198 | </div>
|
|---|
| 199 |
|
|---|
| 200 | <Show
|
|---|
| 201 | when={!consultations.loading}
|
|---|
| 202 | fallback={<div class="text-center py-8">Loading consultations...</div>}
|
|---|
| 203 | >
|
|---|
| 204 | <Show
|
|---|
| 205 | when={(consultations()?.length ?? 0) > 0}
|
|---|
| 206 | fallback={
|
|---|
| 207 | <div class="text-center py-8 text-gray-500">
|
|---|
| 208 | No consultations logged yet. Click "Log New Consultation" to add
|
|---|
| 209 | one.
|
|---|
| 210 | </div>
|
|---|
| 211 | }
|
|---|
| 212 | >
|
|---|
| 213 | <ConsultationTable
|
|---|
| 214 | consultations={consultations()!}
|
|---|
| 215 | onEdit={openEditModal}
|
|---|
| 216 | onDelete={handleDelete}
|
|---|
| 217 | onTogglePayment={togglePaymentStatus}
|
|---|
| 218 | readOnly={isPatient()}
|
|---|
| 219 | />
|
|---|
| 220 | </Show>
|
|---|
| 221 | </Show>
|
|---|
| 222 |
|
|---|
| 223 | <Show when={isModalOpen()}>
|
|---|
| 224 | <ConsultationModal
|
|---|
| 225 | editingConsultation={editingConsultation()}
|
|---|
| 226 | formData={formData()}
|
|---|
| 227 | patients={patients() || []}
|
|---|
| 228 | newTherapies={newTherapies()}
|
|---|
| 229 | existingTherapies={existingTherapies()}
|
|---|
| 230 | onClose={closeModal}
|
|---|
| 231 | onSubmit={handleSubmit}
|
|---|
| 232 | onFormChange={setFormData}
|
|---|
| 233 | onNewTherapiesChange={setNewTherapies}
|
|---|
| 234 | onExistingTherapiesChange={(therapies) => {
|
|---|
| 235 | setExistingTherapies(therapies);
|
|---|
| 236 | refetch();
|
|---|
| 237 | }}
|
|---|
| 238 | readOnly={isPatient()}
|
|---|
| 239 | />
|
|---|
| 240 | </Show>
|
|---|
| 241 | </div>
|
|---|
| 242 | );
|
|---|
| 243 | };
|
|---|
| 244 |
|
|---|
| 245 | export default Consultations;
|
|---|