2021-12-23 16:56:49 +08:00
|
|
|
<script setup lang="ts">
|
2022-12-28 05:29:14 +08:00
|
|
|
import VueMultiselect from "vue-multiselect";
|
2022-04-24 14:05:10 +08:00
|
|
|
import { reactive, computed, onMounted, PropType, toRefs } from "vue";
|
|
|
|
import { useRouter, useRoute } from "vue-router";
|
|
|
|
import { IPatient } from "../../models/patient";
|
|
|
|
import { ADD_PATIENT, UPDATE_PATIENT } from "../../graphql/patient.mutations";
|
|
|
|
|
|
|
|
import { useClientStore, useLocationStore, usePatientStore } from "../../stores";
|
|
|
|
import { useApiUtil } from "../../composables";
|
|
|
|
import { IDistrict, IProvince } from "../../models/location";
|
|
|
|
import { IClient } from "../../models/client";
|
|
|
|
import { isNullOrWs } from "../../utils";
|
|
|
|
|
|
|
|
import { useField, useForm } from "vee-validate";
|
|
|
|
import { object, string, boolean, number, date } from "yup";
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
patient: Object as PropType<IPatient>,
|
|
|
|
navigate: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const emit = defineEmits(["close"]);
|
|
|
|
|
|
|
|
let clientStore = useClientStore();
|
|
|
|
let locationsStore = useLocationStore();
|
|
|
|
let patientStore = usePatientStore();
|
|
|
|
const { withClientMutation } = useApiUtil();
|
|
|
|
|
|
|
|
let router = useRouter();
|
|
|
|
let route = useRoute();
|
|
|
|
|
|
|
|
const state = reactive({
|
|
|
|
genders: ["Male", "Female", "Missing", "Trans Gender"] as string[],
|
|
|
|
createAction: true,
|
|
|
|
countries: computed(() => locationsStore.getCountries),
|
|
|
|
provinces: [] as IProvince[],
|
|
|
|
districts: [] as IDistrict[],
|
|
|
|
clients: computed<IClient[]>(() => clientStore.getClients),
|
|
|
|
});
|
|
|
|
|
|
|
|
let clientParams = reactive({
|
|
|
|
first: undefined,
|
|
|
|
after: "",
|
|
|
|
text: "",
|
|
|
|
sortBy: ["name"],
|
|
|
|
filterAction: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
await locationsStore.fetchCountries();
|
|
|
|
await clientStore.fetchClients(clientParams);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Patient
|
|
|
|
const { patient, navigate } = toRefs(props);
|
|
|
|
|
|
|
|
const patientSchema = object({
|
|
|
|
uid: number(),
|
|
|
|
clientPatientId: string().required("Client Patient ID is Required"),
|
|
|
|
patientId: string().nullable(),
|
|
|
|
firstName: string().required("First Name is Required"),
|
|
|
|
middleName: string().nullable(),
|
|
|
|
lastName: string().required("Last Name is Required"),
|
2022-12-28 05:29:14 +08:00
|
|
|
client: object().required("Client is Required"),
|
2022-04-24 14:05:10 +08:00
|
|
|
gender: string().required("Gender is Required"),
|
|
|
|
age: number().nullable(),
|
|
|
|
dateOfBirth: date().nullable(),
|
|
|
|
ageDobEstimated: boolean().nullable(),
|
|
|
|
phoneHome: string().nullable(),
|
|
|
|
phoneMobile: string().nullable(),
|
|
|
|
consentSms: boolean().nullable(),
|
|
|
|
districtUid: number().nullable(),
|
|
|
|
provinceUid: number().nullable(),
|
|
|
|
countryUid: number().nullable(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const { handleSubmit, errors } = useForm({
|
|
|
|
validationSchema: patientSchema,
|
|
|
|
initialValues: {
|
|
|
|
uid: patient?.value?.uid,
|
|
|
|
clientPatientId: patient?.value?.clientPatientId || (route?.query?.cpid as string),
|
|
|
|
patientId: patient?.value?.patientId,
|
|
|
|
firstName: patient?.value?.firstName,
|
|
|
|
middleName: patient?.value?.middleName,
|
|
|
|
lastName: patient?.value?.lastName,
|
2022-12-28 05:29:14 +08:00
|
|
|
client: patient?.value?.client,
|
2022-04-24 14:05:10 +08:00
|
|
|
gender: patient?.value?.gender,
|
|
|
|
age: patient?.value?.age,
|
|
|
|
dateOfBirth: !isNullOrWs(patient?.value?.dateOfBirth)
|
|
|
|
? (new Date(patient?.value?.dateOfBirth!).toISOString().split("T")[0] as any)
|
|
|
|
: undefined,
|
|
|
|
ageDobEstimated: patient?.value?.ageDobEstimated,
|
|
|
|
phoneHome: patient?.value?.phoneHome,
|
|
|
|
phoneMobile: patient?.value?.phoneMobile,
|
|
|
|
consentSms: patient?.value?.consentSms,
|
2022-12-28 05:29:14 +08:00
|
|
|
districtUid: patient?.value?.districtUid,
|
|
|
|
provinceUid: patient?.value?.provinceUid,
|
|
|
|
countryUid: patient?.value?.countryUid,
|
|
|
|
} as any,
|
2022-04-24 14:05:10 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
const { value: clientPatientId } = useField("clientPatientId");
|
|
|
|
const { value: firstName } = useField("firstName");
|
|
|
|
const { value: middleName } = useField("middleName");
|
|
|
|
const { value: lastName } = useField("lastName");
|
2022-12-28 05:29:14 +08:00
|
|
|
const { value: client } = useField<IClient>("client");
|
2022-04-24 14:05:10 +08:00
|
|
|
const { value: gender } = useField("gender");
|
|
|
|
const { value: age } = useField("age");
|
|
|
|
const { value: dateOfBirth } = useField("dateOfBirth");
|
|
|
|
const { value: ageDobEstimated } = useField<boolean>("ageDobEstimated");
|
|
|
|
const { value: phoneMobile } = useField("phoneMobile");
|
|
|
|
const { value: consentSms } = useField<boolean>("consentSms");
|
|
|
|
const { value: districtUid } = useField("districtUid");
|
|
|
|
const { value: provinceUid } = useField("provinceUid");
|
|
|
|
const { value: countryUid } = useField("countryUid");
|
|
|
|
|
|
|
|
const submitPatientForm = handleSubmit((values) => {
|
2022-12-28 05:29:14 +08:00
|
|
|
console.log(values);
|
2022-04-24 14:05:10 +08:00
|
|
|
if (!values.uid) addPatient(values as IPatient);
|
|
|
|
if (values.uid) updatePatient(values as IPatient);
|
|
|
|
});
|
|
|
|
|
|
|
|
//
|
|
|
|
function addPatient(payload: IPatient) {
|
|
|
|
withClientMutation(
|
|
|
|
ADD_PATIENT,
|
|
|
|
{
|
|
|
|
payload: {
|
|
|
|
clientPatientId: payload.clientPatientId,
|
|
|
|
firstName: payload.firstName,
|
|
|
|
middleName: payload.middleName,
|
|
|
|
lastName: payload.lastName,
|
|
|
|
age: payload.age,
|
|
|
|
gender: payload.gender,
|
|
|
|
dateOfBirth: payload.dateOfBirth,
|
|
|
|
ageDobEstimated: payload.ageDobEstimated,
|
2022-12-28 05:29:14 +08:00
|
|
|
clientUid: payload.client.uid,
|
2022-04-24 14:05:10 +08:00
|
|
|
phoneMobile: payload.phoneMobile,
|
|
|
|
consentSms: payload.consentSms,
|
|
|
|
},
|
2021-12-23 16:56:49 +08:00
|
|
|
},
|
2022-04-24 14:05:10 +08:00
|
|
|
"createPatient"
|
|
|
|
).then((result) => {
|
|
|
|
patientStore.addPatient(result);
|
|
|
|
emit("close");
|
|
|
|
if (navigate.value === true)
|
|
|
|
router.push({ name: "patient-detail", params: { patientUid: result.uid } });
|
2021-12-23 16:56:49 +08:00
|
|
|
});
|
2022-04-24 14:05:10 +08:00
|
|
|
}
|
2021-12-23 16:56:49 +08:00
|
|
|
|
2022-04-24 14:05:10 +08:00
|
|
|
function updatePatient(payload: IPatient) {
|
|
|
|
withClientMutation(
|
|
|
|
UPDATE_PATIENT,
|
|
|
|
{
|
2021-12-23 16:56:49 +08:00
|
|
|
uid: payload.uid,
|
2022-01-10 00:00:14 +08:00
|
|
|
payload: {
|
2022-04-24 14:05:10 +08:00
|
|
|
clientPatientId: payload.clientPatientId,
|
2022-01-10 00:00:14 +08:00
|
|
|
firstName: payload.firstName,
|
2022-04-24 14:05:10 +08:00
|
|
|
middleName: payload.middleName,
|
|
|
|
lastName: payload.lastName,
|
2022-01-10 00:00:14 +08:00
|
|
|
age: payload.age,
|
2022-04-24 14:05:10 +08:00
|
|
|
gender: payload.gender,
|
|
|
|
dateOfBirth: payload.dateOfBirth,
|
2022-01-10 00:00:14 +08:00
|
|
|
ageDobEstimated: payload.ageDobEstimated,
|
2022-12-28 05:29:14 +08:00
|
|
|
clientUid: payload.client.uid,
|
2022-04-24 14:05:10 +08:00
|
|
|
phoneMobile: payload.phoneMobile,
|
|
|
|
consentSms: payload.consentSms,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"updatePatient"
|
|
|
|
).then((result) => {
|
|
|
|
patientStore.updatePatient(result);
|
|
|
|
emit("close", result);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provinces
|
|
|
|
function getProvinces(event: any) {
|
|
|
|
locationsStore.filterProvincesByCountry(countryUid.value as number);
|
|
|
|
}
|
2021-12-23 16:56:49 +08:00
|
|
|
|
2022-04-24 14:05:10 +08:00
|
|
|
// Districts
|
|
|
|
function getDistricts(event: any) {
|
|
|
|
locationsStore.filterDistrictsByProvince(provinceUid.value as number);
|
|
|
|
}
|
|
|
|
</script>
|
2021-11-08 01:01:12 +08:00
|
|
|
|
2022-04-04 02:54:31 +08:00
|
|
|
<template>
|
2022-04-24 14:05:10 +08:00
|
|
|
<form
|
2022-04-04 02:54:31 +08:00
|
|
|
@submit.prevent="submitPatientForm"
|
2022-04-24 14:05:10 +08:00
|
|
|
class="border-2 border-gray-900 border-dotted rounded-sm px-4 py-8"
|
|
|
|
autocomplete="off"
|
|
|
|
>
|
|
|
|
<label class="flex whitespace-nowrap w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Patient Unique Identifier</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 block w-full"
|
|
|
|
v-model="clientPatientId"
|
|
|
|
placeholder="Patient Unique Identifier"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.clientPatientId }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">First Name</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
v-model="firstName"
|
|
|
|
placeholder="First Name"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.firstName }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Middle Name</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
v-model="middleName"
|
|
|
|
placeholder="Middle Name"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.middleName }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Last Name</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
v-model="lastName"
|
|
|
|
placeholder="Last Name"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.lastName }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Age</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
type="number"
|
|
|
|
v-model="age"
|
|
|
|
placeholder="Age"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.age }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Date of Birth</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
type="date"
|
|
|
|
v-model="dateOfBirth"
|
|
|
|
placeholder="Date of Birth"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.dateOfBirth }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Age/DOB Estimated?</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
class="form-checkbox text-sky-800"
|
|
|
|
v-model="ageDobEstimated"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.ageDobEstimated }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Gender</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<select class="form-select mt-1 w-full" v-model="gender">
|
|
|
|
<option></option>
|
|
|
|
<option v-for="sex of state.genders" :key="sex" :value="sex">
|
|
|
|
{{ sex }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.gender }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Mobile Number</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input
|
|
|
|
class="form-input mt-1 w-full"
|
|
|
|
v-model="phoneMobile"
|
|
|
|
placeholder="Mobile Number"
|
|
|
|
/>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.phoneMobile }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Consent to SMS</span>
|
|
|
|
<div class="w-full">
|
|
|
|
<input type="checkbox" class="form-checkbox text-sky-800" v-model="consentSms" />
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.consentSms }}</div>
|
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<!-- other identifiers: passport, client pid, national id -->
|
2022-12-28 05:29:14 +08:00
|
|
|
<label class="flex whitespace-nowrap mb-2 w-full">
|
2022-04-24 14:05:10 +08:00
|
|
|
<span class="text-gray-700 w-4/12">Primary Referrer</span>
|
|
|
|
<div class="w-full">
|
2022-12-28 05:29:14 +08:00
|
|
|
<VueMultiselect
|
|
|
|
placeholder="Select a Primary Referrer"
|
|
|
|
v-model="client"
|
|
|
|
:options="state.clients"
|
|
|
|
:searchable="true"
|
|
|
|
label="name"
|
|
|
|
track-by="uid"
|
|
|
|
>
|
|
|
|
</VueMultiselect>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.client }}</div>
|
2022-04-24 14:05:10 +08:00
|
|
|
</div>
|
|
|
|
</label>
|
|
|
|
|
|
|
|
<hr class="my-2" />
|
|
|
|
|
|
|
|
<div class="grid grid-cols-3 gap-x-4 mb-4">
|
|
|
|
<label class="flex items-center whitespace-nowrap col-span-1 mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Country</span>
|
|
|
|
<select
|
|
|
|
class="form-select mt-1 w-full"
|
|
|
|
v-model="countryUid"
|
|
|
|
@change="getProvinces($event)"
|
|
|
|
>
|
|
|
|
<option :value="null"></option>
|
|
|
|
<option
|
|
|
|
v-for="country in state.countries"
|
|
|
|
:key="country.uid"
|
|
|
|
:value="country.uid"
|
|
|
|
>
|
|
|
|
{{ country.name }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</label>
|
|
|
|
<label class="flex items-center whitespace-nowrap col-span-1 mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">Province</span>
|
|
|
|
<select
|
|
|
|
class="form-select mt-1 w-full"
|
|
|
|
v-model="provinceUid"
|
|
|
|
@change="getDistricts($event)"
|
|
|
|
>
|
|
|
|
<option :value="null"></option>
|
|
|
|
<option
|
|
|
|
v-for="province in state.provinces"
|
|
|
|
:key="province.uid"
|
|
|
|
:value="province.uid"
|
|
|
|
>
|
|
|
|
{{ province.name }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</label>
|
|
|
|
<label class="flex items-center whitespace-nowrap col-span-1 mb-2 w-full">
|
|
|
|
<span class="text-gray-700 w-4/12">District</span>
|
|
|
|
<select class="form-select mt-1 w-full" v-model="districtUid">
|
|
|
|
<option :value="null"></option>
|
|
|
|
<option
|
|
|
|
v-for="district in state.districts"
|
|
|
|
:key="district.uid"
|
|
|
|
:value="district.uid"
|
|
|
|
>
|
|
|
|
{{ district.name }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.countryUid }}</div>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.provinceUid }}</div>
|
|
|
|
<div class="text-orange-600 w-4/12">{{ errors.districtUid }}</div>
|
|
|
|
|
|
|
|
<hr />
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
class="-mb-4 w-1/5 border border-sky-800 bg-sky-800 text-white rounded-sm px-4 py-2 m-2 transition-colors duration-500 ease select-none hover:bg-sky-800 focus:outline-none focus:shadow-outline"
|
|
|
|
>
|
|
|
|
Save Patient
|
|
|
|
</button>
|
|
|
|
</form>
|
2022-04-04 02:54:31 +08:00
|
|
|
</template>
|
2022-12-28 05:29:14 +08:00
|
|
|
|
|
|
|
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|