Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd48cc5907 | ||
|
|
49ffad1dde | ||
|
|
893a4a9b96 | ||
|
|
60f60b4187 | ||
|
|
51725d7f73 | ||
|
|
608c791cfc | ||
|
|
b5f1ab7f88 | ||
|
|
d78ad28607 | ||
|
|
8c51cc2719 | ||
|
|
80c558d284 | ||
|
|
82f4759571 | ||
|
|
9bf7dacf55 | ||
|
|
ee06f42c06 | ||
|
|
ed3c83bbe4 | ||
|
|
56e71ea693 | ||
|
|
ff19c022bb | ||
|
|
bcfe566373 | ||
|
|
9a7a951379 | ||
|
|
816fe7cbf5 | ||
|
|
616c15c87c | ||
|
|
6ee33d2525 | ||
|
|
5d54157391 | ||
|
|
87121d00fd | ||
|
|
fd8385650c | ||
|
|
674a4be4ce | ||
|
|
f40d25042b | ||
|
|
9ce103f38e | ||
|
|
bbdaeee304 | ||
|
|
5166229d06 | ||
|
|
98910563b8 | ||
|
|
d1fd8bb194 | ||
|
|
806cfad6a8 |
No files matched your search
@@ -3,3 +3,14 @@ NUXT_BPJS_API_ORIGIN=
|
|||||||
NUXT_API_VCLAIM_SWAGGER= # https://vclaim-api.multy.chat
|
NUXT_API_VCLAIM_SWAGGER= # https://vclaim-api.multy.chat
|
||||||
NUXT_SYNC_API_ORIGIN=
|
NUXT_SYNC_API_ORIGIN=
|
||||||
NUXT_API_ORIGIN=
|
NUXT_API_ORIGIN=
|
||||||
|
|
||||||
|
SSO_CONFIRM_URL =
|
||||||
|
|
||||||
|
X_AP_CODE=rssa-sso
|
||||||
|
X_AP_SECRET_KEY=sapiperah
|
||||||
|
KEYCLOAK_LOGOUT_REDIRECT=http://localhost:3000/
|
||||||
|
|
||||||
|
# test local
|
||||||
|
KEYCLOAK_REALM=rssa_testing
|
||||||
|
KEYCLOAK_URL=http://127.0.0.1:8080/
|
||||||
|
CLIENT_ID=portal-simrs-new
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Build Stage
|
||||||
|
FROM node:24-alpine AS build-stage
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Enable pnpm using corepack
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
|
# Copy pnpm related files and package.json to leverage Docker layer caching
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install dependencies using pnpm
|
||||||
|
# Using --frozen-lockfile ensures consistent installations based on pnpm-lock.yaml
|
||||||
|
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Copy the rest of the application files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the Vue.js application for production
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
# Production Stage
|
||||||
|
FROM nginx:stable-alpine AS production-stage
|
||||||
|
|
||||||
|
# Copy the built Vue.js application from the build stage to Nginx's web root
|
||||||
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Expose port 80 for Nginx
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Command to run Nginx in the foreground
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -3,6 +3,7 @@ import type { z } from 'zod'
|
|||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import { Loader2 } from 'lucide-vue-next'
|
import { Loader2 } from 'lucide-vue-next'
|
||||||
import { useForm } from 'vee-validate'
|
import { useForm } from 'vee-validate'
|
||||||
|
import { useKeycloak } from "~/composables/useKeycloack"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
schema: z.ZodSchema<any>
|
schema: z.ZodSchema<any>
|
||||||
@@ -13,6 +14,7 @@ const props = defineProps<Props>()
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
submit: [data: any]
|
submit: [data: any]
|
||||||
|
sso: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { handleSubmit, defineField, errors, meta } = useForm({
|
const { handleSubmit, defineField, errors, meta } = useForm({
|
||||||
@@ -33,6 +35,25 @@ const onSubmit = handleSubmit(async (values) => {
|
|||||||
console.error('Submission failed:', error)
|
console.error('Submission failed:', error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { initKeycloak, getProfile, loginSSO } = useKeycloak()
|
||||||
|
const profile = ref<any>(null)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await initKeycloak('check-sso')
|
||||||
|
profile.value = getProfile()
|
||||||
|
console.log(profile)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSSO = (async () => {
|
||||||
|
try {
|
||||||
|
const redirect = window.location.origin + '/auth/sso'
|
||||||
|
await loginSSO({ redirectUri: redirect })
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Call SSO failed:', error)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -71,4 +92,8 @@ const onSubmit = handleSubmit(async (values) => {
|
|||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<Button @click="onSSO" target="_blank">
|
||||||
|
Login SSO
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
@@ -89,10 +89,10 @@ function proceedItem(action: string) {
|
|||||||
|
|
||||||
function getLinks() {
|
function getLinks() {
|
||||||
switch (activeServicePosition.value) {
|
switch (activeServicePosition.value) {
|
||||||
case 'medical':
|
case 'med':
|
||||||
linkItemsFiltered.value = baseLinkItems.filter((item) => item.groups?.includes('medical'))
|
linkItemsFiltered.value = baseLinkItems.filter((item) => item.groups?.includes('medical'))
|
||||||
break
|
break
|
||||||
case 'registration':
|
case 'reg':
|
||||||
linkItemsFiltered.value = baseLinkItems.filter((item) => item.groups?.includes('registration'))
|
linkItemsFiltered.value = baseLinkItems.filter((item) => item.groups?.includes('registration'))
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load diff
@@ -11,6 +11,7 @@ import { calculateAge } from '~/models/person'
|
|||||||
// components
|
// components
|
||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
import { InputBase, FileField as FileUpload } from '~/components/pub/my-ui/form'
|
import { InputBase, FileField as FileUpload } from '~/components/pub/my-ui/form'
|
||||||
|
import { Skeleton } from '~/components/pub/ui/skeleton'
|
||||||
import { SelectBirthPlace } from '~/components/app/person/fields'
|
import { SelectBirthPlace } from '~/components/app/person/fields'
|
||||||
import {
|
import {
|
||||||
InputName,
|
InputName,
|
||||||
@@ -60,10 +61,20 @@ interface PatientFormInput {
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isReadonly: boolean
|
isReadonly: boolean
|
||||||
|
languageOptions?: { value: string; label: string }[]
|
||||||
|
ethnicOptions?: { value: string; label: string }[]
|
||||||
initialValues?: PatientFormInput
|
initialValues?: PatientFormInput
|
||||||
|
mode?: 'add' | 'edit'
|
||||||
|
identityFileUrl?: string
|
||||||
|
familyCardFileUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'preview', url: string): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const formSchema = toTypedSchema(PatientSchema)
|
const formSchema = toTypedSchema(PatientSchema)
|
||||||
|
|
||||||
const { values, resetForm, setValues, setFieldValue, validate, setFieldError } = useForm<FormData>({
|
const { values, resetForm, setValues, setFieldValue, validate, setFieldError } = useForm<FormData>({
|
||||||
@@ -208,6 +219,7 @@ watch(
|
|||||||
label="Suku"
|
label="Suku"
|
||||||
placeholder="Pilih suku bangsa"
|
placeholder="Pilih suku bangsa"
|
||||||
:is-disabled="isReadonly || values.nationality !== 'WNI'"
|
:is-disabled="isReadonly || values.nationality !== 'WNI'"
|
||||||
|
:items="ethnicOptions || []"
|
||||||
/>
|
/>
|
||||||
<SelectLanguage
|
<SelectLanguage
|
||||||
field-name="language"
|
field-name="language"
|
||||||
@@ -215,6 +227,7 @@ watch(
|
|||||||
placeholder="Pilih preferensi bahasa"
|
placeholder="Pilih preferensi bahasa"
|
||||||
is-required
|
is-required
|
||||||
:is-disabled="isReadonly"
|
:is-disabled="isReadonly"
|
||||||
|
:items="languageOptions || []"
|
||||||
/>
|
/>
|
||||||
<SelectReligion
|
<SelectReligion
|
||||||
field-name="religion"
|
field-name="religion"
|
||||||
@@ -256,22 +269,112 @@ watch(
|
|||||||
:col-count="2"
|
:col-count="2"
|
||||||
:cell-flex="false"
|
:cell-flex="false"
|
||||||
>
|
>
|
||||||
<FileUpload
|
<div class="flex flex-col gap-3">
|
||||||
field-name="identityCardFile"
|
<FileUpload
|
||||||
label="Dokumen KTP"
|
field-name="residentIdentityFile"
|
||||||
placeholder="Unggah scan dokumen KTP"
|
label="Dokumen KTP"
|
||||||
:accept="['pdf', 'jpg', 'png']"
|
placeholder="Unggah scan dokumen KTP"
|
||||||
:max-size-mb="1"
|
:accept="['pdf', 'jpg', 'png']"
|
||||||
:is-disabled="isReadonly"
|
:max-size-mb="1"
|
||||||
/>
|
:is-disabled="isReadonly"
|
||||||
<FileUpload
|
/>
|
||||||
field-name="familyCardFile"
|
<!-- Embed Preview Dokumen KTP (mode edit) -->
|
||||||
label="Dokumen KK"
|
<div
|
||||||
placeholder="Unggah scan dokumen KK"
|
v-if="mode === 'edit'"
|
||||||
:accept="['pdf', 'jpg', 'png']"
|
class="flex flex-col gap-1"
|
||||||
:max-size-mb="1"
|
>
|
||||||
:is-disabled="isReadonly"
|
<span class="text-xs text-muted-foreground">Dokumen Tersimpan</span>
|
||||||
/>
|
<div
|
||||||
|
class="relative h-28 w-48 cursor-pointer overflow-hidden rounded-md border bg-muted"
|
||||||
|
:class="{ 'cursor-not-allowed opacity-60': !identityFileUrl }"
|
||||||
|
@click="identityFileUrl && emit('preview', identityFileUrl)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="identityFileUrl"
|
||||||
|
:src="identityFileUrl"
|
||||||
|
alt="Dokumen KTP"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
v-else
|
||||||
|
class="h-full w-full"
|
||||||
|
/>
|
||||||
|
<!-- Overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity"
|
||||||
|
:class="identityFileUrl ? 'opacity-0 hover:opacity-100' : 'opacity-100'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="identityFileUrl"
|
||||||
|
class="flex flex-col items-center gap-1 text-white"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-eye h-5 w-5" />
|
||||||
|
<span class="text-xs font-medium">Lihat Dokumen</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex flex-col items-center gap-1 text-white/70"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-info h-5 w-5" />
|
||||||
|
<span class="text-xs font-medium">No Data</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<FileUpload
|
||||||
|
field-name="familyIdentityFile"
|
||||||
|
label="Dokumen KK"
|
||||||
|
placeholder="Unggah scan dokumen KK"
|
||||||
|
:accept="['pdf', 'jpg', 'png']"
|
||||||
|
:max-size-mb="1"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
|
/>
|
||||||
|
<!-- Embed Preview Dokumen KK (mode edit) -->
|
||||||
|
<div
|
||||||
|
v-if="mode === 'edit'"
|
||||||
|
class="flex flex-col gap-1"
|
||||||
|
>
|
||||||
|
<span class="text-xs text-muted-foreground">Dokumen Tersimpan</span>
|
||||||
|
<div
|
||||||
|
class="relative h-28 w-48 cursor-pointer overflow-hidden rounded-md border bg-muted"
|
||||||
|
:class="{ 'cursor-not-allowed opacity-60': !familyCardFileUrl }"
|
||||||
|
@click="familyCardFileUrl && emit('preview', familyCardFileUrl)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="familyCardFileUrl"
|
||||||
|
:src="familyCardFileUrl"
|
||||||
|
alt="Dokumen KK"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
v-else
|
||||||
|
class="h-full w-full"
|
||||||
|
/>
|
||||||
|
<!-- Overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity"
|
||||||
|
:class="familyCardFileUrl ? 'opacity-0 hover:opacity-100' : 'opacity-100'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="familyCardFileUrl"
|
||||||
|
class="flex flex-col items-center gap-1 text-white"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-eye h-5 w-5" />
|
||||||
|
<span class="text-xs font-medium">Lihat Dokumen</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex flex-col items-center gap-1 text-white/70"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-info h-5 w-5" />
|
||||||
|
<span class="text-xs font-medium">No Data</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@@ -5,6 +5,9 @@ import { cn } from '~/lib/utils'
|
|||||||
|
|
||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
import { disabilityCodes } from '~/lib/constants'
|
||||||
|
import { mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fieldName?: string
|
fieldName?: string
|
||||||
label?: string
|
label?: string
|
||||||
@@ -26,16 +29,10 @@ const {
|
|||||||
fieldGroupClass,
|
fieldGroupClass,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const disabilityOptions = [
|
const disabilityOptions = mapToComboboxOptList(disabilityCodes).map(({ label, value }) => ({
|
||||||
{ label: 'Tuna Daksa', value: 'daksa' },
|
label,
|
||||||
{ label: 'Tuna Netra', value: 'netra' },
|
value,
|
||||||
{ label: 'Tuna Rungu', value: 'rungu' },
|
}))
|
||||||
{ label: 'Tuna Wicara', value: 'wicara' },
|
|
||||||
{ label: 'Tuna Rungu-Wicara', value: 'rungu_wicara' },
|
|
||||||
{ label: 'Tuna Grahita', value: 'grahita' },
|
|
||||||
{ label: 'Tuna Laras', value: 'laras' },
|
|
||||||
{ label: 'Lainnya', value: 'other', priority: -100 },
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { cn } from '~/lib/utils'
|
|||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
items: { value: string; label: string }[]
|
||||||
fieldName?: string
|
fieldName?: string
|
||||||
label?: string
|
label?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
@@ -28,23 +29,23 @@ const {
|
|||||||
labelClass,
|
labelClass,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const ethnicOptions = [
|
// const ethnicOptions = [
|
||||||
{ label: 'Tidak diketahui', value: 'unknown', priority: 1 },
|
// { label: 'Tidak diketahui', value: 'unknown', priority: 1 },
|
||||||
{ label: 'Jawa', value: 'jawa' },
|
// { label: 'Jawa', value: 'jawa' },
|
||||||
{ label: 'Sunda', value: 'sunda' },
|
// { label: 'Sunda', value: 'sunda' },
|
||||||
{ label: 'Batak', value: 'batak' },
|
// { label: 'Batak', value: 'batak' },
|
||||||
{ label: 'Betawi', value: 'betawi' },
|
// { label: 'Betawi', value: 'betawi' },
|
||||||
{ label: 'Minangkabau', value: 'minangkabau' },
|
// { label: 'Minangkabau', value: 'minangkabau' },
|
||||||
{ label: 'Bugis', value: 'bugis' },
|
// { label: 'Bugis', value: 'bugis' },
|
||||||
{ label: 'Madura', value: 'madura' },
|
// { label: 'Madura', value: 'madura' },
|
||||||
{ label: 'Banjar', value: 'banjar' },
|
// { label: 'Banjar', value: 'banjar' },
|
||||||
{ label: 'Bali', value: 'bali' },
|
// { label: 'Bali', value: 'bali' },
|
||||||
{ label: 'Dayak', value: 'dayak' },
|
// { label: 'Dayak', value: 'dayak' },
|
||||||
{ label: 'Aceh', value: 'aceh' },
|
// { label: 'Aceh', value: 'aceh' },
|
||||||
{ label: 'Sasak', value: 'sasak' },
|
// { label: 'Sasak', value: 'sasak' },
|
||||||
{ label: 'Papua', value: 'papua' },
|
// { label: 'Papua', value: 'papua' },
|
||||||
{ label: 'Lainnya', value: 'lainnya', priority: -100 },
|
// { label: 'Lainnya', value: 'lainnya', priority: -100 },
|
||||||
]
|
// ]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -70,7 +71,7 @@ const ethnicOptions = [
|
|||||||
<Combobox
|
<Combobox
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:items="ethnicOptions"
|
:items="items"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:is-disabled="isDisabled"
|
:is-disabled="isDisabled"
|
||||||
search-placeholder="Cari..."
|
search-placeholder="Cari..."
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { cn } from '~/lib/utils'
|
|||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
items: { value: string; label: string }[]
|
||||||
fieldName?: string
|
fieldName?: string
|
||||||
label?: string
|
label?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
@@ -29,15 +30,15 @@ const {
|
|||||||
labelClass,
|
labelClass,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const langOptions = [
|
// const langOptions = [
|
||||||
{ label: 'Bahasa Indonesia', value: 'id', priority: 1 },
|
// { label: 'Bahasa Indonesia', value: 'id', priority: 1 },
|
||||||
{ label: 'Bahasa Jawa', value: 'jawa' },
|
// { label: 'Bahasa Jawa', value: 'jawa' },
|
||||||
{ label: 'Bahasa Sunda', value: 'sunda' },
|
// { label: 'Bahasa Sunda', value: 'sunda' },
|
||||||
{ label: 'Bahasa Bali', value: 'bali' },
|
// { label: 'Bahasa Bali', value: 'bali' },
|
||||||
{ label: 'Bahasa Jaksel', value: 'jaksel' },
|
// { label: 'Bahasa Jaksel', value: 'jaksel' },
|
||||||
{ label: 'Bahasa Inggris', value: 'en' },
|
// { label: 'Bahasa Inggris', value: 'en' },
|
||||||
{ label: 'Tidak Diketahui', value: 'unknown', priority: 100 },
|
// { label: 'Tidak Diketahui', value: 'unknown', priority: 100 },
|
||||||
]
|
// ]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -64,7 +65,7 @@ const langOptions = [
|
|||||||
:is-disabled="isDisabled"
|
:is-disabled="isDisabled"
|
||||||
:id="fieldName"
|
:id="fieldName"
|
||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:items="langOptions"
|
:items="items"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:preserve-order="false"
|
:preserve-order="false"
|
||||||
:class="
|
:class="
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { ClickType } from '~/components/pub/my-ui/nav-footer'
|
|||||||
import { formatAddress } from '~/models/person-address'
|
import { formatAddress } from '~/models/person-address'
|
||||||
import {
|
import {
|
||||||
addressLocationTypeCode,
|
addressLocationTypeCode,
|
||||||
|
disabilityCodes,
|
||||||
educationCodes,
|
educationCodes,
|
||||||
genderCodes,
|
genderCodes,
|
||||||
occupationCodes,
|
occupationCodes,
|
||||||
@@ -26,6 +27,7 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '~/
|
|||||||
import { Fragment } from '~/components/pub/my-ui/form/'
|
import { Fragment } from '~/components/pub/my-ui/form/'
|
||||||
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
||||||
import DetailSection from '~/components/pub/my-ui/form/view/detail-section.vue'
|
import DetailSection from '~/components/pub/my-ui/form/view/detail-section.vue'
|
||||||
|
import { Skeleton } from '~/components/pub/ui/skeleton'
|
||||||
import { toZoned } from '@internationalized/date'
|
import { toZoned } from '@internationalized/date'
|
||||||
|
|
||||||
// #region Props & Emits
|
// #region Props & Emits
|
||||||
@@ -36,6 +38,7 @@ const props = defineProps<{
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'back'): void
|
(e: 'back'): void
|
||||||
(e: 'edit'): void
|
(e: 'edit'): void
|
||||||
|
(e: 'preview', url: string): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
@@ -47,6 +50,7 @@ const educationOptions = mapToComboboxOptList(educationCodes)
|
|||||||
const occupationOptions = mapToComboboxOptList(occupationCodes)
|
const occupationOptions = mapToComboboxOptList(occupationCodes)
|
||||||
const relationshipOptions = mapToComboboxOptList(relationshipCodes)
|
const relationshipOptions = mapToComboboxOptList(relationshipCodes)
|
||||||
const personContactTypeOptions = mapToComboboxOptList(personContactTypes)
|
const personContactTypeOptions = mapToComboboxOptList(personContactTypes)
|
||||||
|
const disabilityOptions = mapToComboboxOptList(disabilityCodes)
|
||||||
|
|
||||||
// Computed addresses from nested data
|
// Computed addresses from nested data
|
||||||
const domicileAddress = computed(() => {
|
const domicileAddress = computed(() => {
|
||||||
@@ -67,6 +71,14 @@ const patientAge = computed(() => {
|
|||||||
}
|
}
|
||||||
return calculateAge(props.patient.person.birthDate)
|
return calculateAge(props.patient.person.birthDate)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const familiyCardFile = computed(() => {
|
||||||
|
return props.patient.person.familyIdentityFileUrl || ''
|
||||||
|
})
|
||||||
|
const identityFile = computed(() => {
|
||||||
|
return props.patient.person.residentIdentityFileUrl || ''
|
||||||
|
})
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region Lifecycle Hooks
|
// #region Lifecycle Hooks
|
||||||
@@ -92,7 +104,7 @@ function onNavigate(type: ClickType) {
|
|||||||
type="multiple"
|
type="multiple"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
collapsible
|
collapsible
|
||||||
:defaultValue="['item-patient', 'item-address', 'item-contact', 'item-parents', 'item-relative']"
|
:defaultValue="['item-patient', 'item-document', 'item-address', 'item-contact', 'item-parents', 'item-relative']"
|
||||||
>
|
>
|
||||||
<Fragment
|
<Fragment
|
||||||
v-slot="{ section }"
|
v-slot="{ section }"
|
||||||
@@ -144,6 +156,106 @@ function onNavigate(type: ClickType) {
|
|||||||
'-'
|
'-'
|
||||||
}}
|
}}
|
||||||
</DetailRow>
|
</DetailRow>
|
||||||
|
<DetailRow label="Pasien Bayi">{{ patient.newBornStatus ? 'Ya' : 'Tidak' }}</DetailRow>
|
||||||
|
<DetailRow label="Hambatan Komunikasi">
|
||||||
|
{{ patient.person.communicationIssueStatus ? 'Ya' : 'Tidak' }}
|
||||||
|
</DetailRow>
|
||||||
|
<DetailRow label="Disabilitas">
|
||||||
|
{{ disabilityOptions.find((item) => item.code === patient.person.disability)?.label || 'Tidak' }}
|
||||||
|
</DetailRow>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
|
<Fragment
|
||||||
|
v-slot="{ section }"
|
||||||
|
title="Dokumen"
|
||||||
|
>
|
||||||
|
<AccordionItem value="item-document">
|
||||||
|
<AccordionTrigger>{{ section }}</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<!-- Dokumen KTP -->
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<span class="text-sm text-muted-foreground">Dokumen KTP</span>
|
||||||
|
<div
|
||||||
|
class="relative h-32 w-64 cursor-pointer overflow-hidden rounded-md border bg-muted"
|
||||||
|
:class="{ 'cursor-not-allowed opacity-60': !identityFile }"
|
||||||
|
@click="identityFile && emit('preview', identityFile)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="identityFile"
|
||||||
|
:src="identityFile"
|
||||||
|
alt="Dokumen KTP"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
v-else
|
||||||
|
class="h-full w-full"
|
||||||
|
/>
|
||||||
|
<!-- Overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity"
|
||||||
|
:class="identityFile ? 'opacity-0 hover:opacity-100' : 'opacity-100'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="identityFile"
|
||||||
|
class="flex flex-col items-center gap-1 text-white"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-eye h-6 w-6" />
|
||||||
|
<span class="text-sm font-medium">Lihat Dokumen</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex flex-col items-center gap-1 text-white/70"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-info h-6 w-6" />
|
||||||
|
<span class="text-sm font-medium">No Data</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Dokumen KK -->
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<span class="text-sm text-muted-foreground">Dokumen KK</span>
|
||||||
|
<div
|
||||||
|
class="relative h-32 w-64 cursor-pointer overflow-hidden rounded-md border bg-muted"
|
||||||
|
:class="{ 'cursor-not-allowed opacity-60': !familiyCardFile }"
|
||||||
|
@click="familiyCardFile && emit('preview', familiyCardFile)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="familiyCardFile"
|
||||||
|
:src="familiyCardFile"
|
||||||
|
alt="Dokumen KK"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
v-else
|
||||||
|
class="h-full w-full"
|
||||||
|
/>
|
||||||
|
<!-- Overlay -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity"
|
||||||
|
:class="familiyCardFile ? 'opacity-0 hover:opacity-100' : 'opacity-100'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="familiyCardFile"
|
||||||
|
class="flex flex-col items-center gap-1 text-white"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-eye h-6 w-6" />
|
||||||
|
<span class="text-sm font-medium">Lihat Dokumen</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex flex-col items-center gap-1 text-white/70"
|
||||||
|
>
|
||||||
|
<span class="i-lucide-info h-6 w-6" />
|
||||||
|
<span class="text-sm font-medium">No Data</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -0,0 +1,476 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import Block from '~/components/pub/my-ui/form/block.vue'
|
||||||
|
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
import { educationCodes, genderCodes, occupationCodes, religionCodes } from '~/lib/constants'
|
||||||
|
import { mapToComboboxOptList } from '~/lib/utils'
|
||||||
|
|
||||||
|
interface DivisionFormData {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
parentId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
division: {
|
||||||
|
msg: {
|
||||||
|
placeholder: string
|
||||||
|
search: string
|
||||||
|
empty: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items: {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
code: string
|
||||||
|
}[]
|
||||||
|
schema: any
|
||||||
|
initialValues?: Partial<DivisionFormData>
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: DivisionFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const educationOpts = mapToComboboxOptList(educationCodes)
|
||||||
|
const occupationOpts = mapToComboboxOptList(occupationCodes)
|
||||||
|
const religionOpts = mapToComboboxOptList(religionCodes)
|
||||||
|
const genderOpts = mapToComboboxOptList(genderCodes)
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||||
|
const formData: DivisionFormData = {
|
||||||
|
name: values.name || '',
|
||||||
|
code: values.code || '',
|
||||||
|
parentId: values.parentId || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
v-slot="{ handleSubmit, resetForm }"
|
||||||
|
as=""
|
||||||
|
keep-values
|
||||||
|
:validation-schema="formSchema"
|
||||||
|
:initial-values="initialValues"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
id="entry-form"
|
||||||
|
@submit="handleSubmit($event, (values) => onSubmitForm(values, { resetForm }))"
|
||||||
|
>
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup>
|
||||||
|
<Label label-for="residentIdentityNumber">KTP</Label>
|
||||||
|
<Field
|
||||||
|
id="residentIdentityNumber"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="residentIdentityNumber"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="residentIdentityNumber"
|
||||||
|
type="text"
|
||||||
|
maxlength="16"
|
||||||
|
placeholder="Nomor KTP"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- FrontTitle -->
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label label-for="frontTitle">Gelar Depan</Label>
|
||||||
|
<Field
|
||||||
|
id="frontTitle"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="frontTitle"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="frontTitle"
|
||||||
|
type="text"
|
||||||
|
placeholder="Dr., Ir., dll"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label
|
||||||
|
label-for="name"
|
||||||
|
position="dynamic"
|
||||||
|
>
|
||||||
|
Nama
|
||||||
|
</Label>
|
||||||
|
<Field
|
||||||
|
id="name"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="name"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
placeholder="Nama lengkap"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- EndTitle -->
|
||||||
|
<FieldGroup :column="3">
|
||||||
|
<Label
|
||||||
|
label-for="endTitle"
|
||||||
|
position="dynamic"
|
||||||
|
>
|
||||||
|
Gelar Belakang
|
||||||
|
</Label>
|
||||||
|
<Field
|
||||||
|
id="endTitle"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="endTitle"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="endTitle"
|
||||||
|
type="text"
|
||||||
|
placeholder="S.Kom, M.Kes, dll"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- BirthDate -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="birthDate">Tanggal Lahir</Label>
|
||||||
|
<Field
|
||||||
|
id="birthDate"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="birthDate"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="birthDate"
|
||||||
|
type="date"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- BirthRegency_Code -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="birthRegencyCode">Tempat Lahir</Label>
|
||||||
|
<Field
|
||||||
|
id="birthRegencyCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="birthRegencyCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="parentId"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="educationOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Gender_Code -->
|
||||||
|
<FieldGroup>
|
||||||
|
<Label label-for="genderCode">Jenis Kelamin</Label>
|
||||||
|
<Field
|
||||||
|
id="genderCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="genderCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="genderCode"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="genderOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- PassportNumber -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="passportNumber">Paspor</Label>
|
||||||
|
<Field
|
||||||
|
id="passportNumber"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="passportNumber"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="passportNumber"
|
||||||
|
type="text"
|
||||||
|
placeholder="Nomor Paspor"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- DrivingLicenseNumber -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="drivingLicenseNumber">SIM</Label>
|
||||||
|
<Field
|
||||||
|
id="drivingLicenseNumber"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="drivingLicenseNumber"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="drivingLicenseNumber"
|
||||||
|
type="text"
|
||||||
|
placeholder="Nomor SIM"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Religion_Code -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="religionCode">Agama</Label>
|
||||||
|
<Field
|
||||||
|
id="religionCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="religionCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="religionCode"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="religionOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="ethnicCode">Suku</Label>
|
||||||
|
<Field
|
||||||
|
id="ethnicCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="ethnicCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="ethnicCode"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="occupationOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Language_Code -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="languageCode">Bahasa</Label>
|
||||||
|
<Field
|
||||||
|
id="languageCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="languageCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="parentId"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="educationOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Education_Code -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="educationCode">Pendidikan</Label>
|
||||||
|
<Field
|
||||||
|
id="educationCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="educationCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="educationCode"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="educationOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Occupation_Code -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="occupationCode">Pekerjaan</Label>
|
||||||
|
<Field
|
||||||
|
id="occupationCode"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="occupationCode"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Combobox
|
||||||
|
id="occupationCode"
|
||||||
|
v-bind="componentField"
|
||||||
|
:items="occupationOpts"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
|
||||||
|
<!-- Occupation_Name -->
|
||||||
|
<FieldGroup :column="2">
|
||||||
|
<Label label-for="occupationName">Detail Pekerjaan</Label>
|
||||||
|
<Field
|
||||||
|
id="occupationName"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="occupationName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="occupationName"
|
||||||
|
type="text"
|
||||||
|
placeholder="Contoh: Guru SMP, Petani"
|
||||||
|
autocomplete="off"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -28,6 +28,7 @@ import ConfirmationInfo from '~/components/app/device-order/confirmation-info.vu
|
|||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
encounter_id: number
|
encounter_id: number
|
||||||
|
canUpdate?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const encounter_id = props.encounter_id
|
const encounter_id = props.encounter_id
|
||||||
@@ -153,7 +154,7 @@ function pickItem(): DeviceOrder | null {
|
|||||||
:pagination-meta="paginationMeta"
|
:pagination-meta="paginationMeta"
|
||||||
@page-change="handlePageChange"
|
@page-change="handlePageChange"
|
||||||
/>
|
/>
|
||||||
<!--
|
<!--
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@edit="edit"
|
@edit="edit"
|
||||||
@submit="submit"
|
@submit="submit"
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ import { useIntegrationSepEntry } from '~/handlers/integration-sep-entry.handler
|
|||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id: number
|
id: number
|
||||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
classCode?: 'ambulatory' | 'emergency' | 'inpatient'
|
||||||
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
subclassCode?: 'regular' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||||
formType: string
|
formType: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const formRef = ref<InstanceType<typeof AppEncounterEntryForm> | null>(null)
|
const formRef = ref<InstanceType<typeof AppEncounterEntryForm> | null>(null)
|
||||||
|
// const patientMode = ref<'new' | 'exists'>('exists')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
paymentsList,
|
paymentsList,
|
||||||
@@ -56,7 +57,6 @@ const {
|
|||||||
getDoctorInfo,
|
getDoctorInfo,
|
||||||
getValidateMember,
|
getValidateMember,
|
||||||
getValidateSepNumber,
|
getValidateSepNumber,
|
||||||
handleFetchDoctors,
|
|
||||||
} = useEncounterEntry(props)
|
} = useEncounterEntry(props)
|
||||||
const { recSepId, openHistory, histories, getMonitoringHistoryMappers } = useIntegrationSepEntry()
|
const { recSepId, openHistory, histories, getMonitoringHistoryMappers } = useIntegrationSepEntry()
|
||||||
|
|
||||||
@@ -77,18 +77,18 @@ function handleSaveClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleFetch(value?: any) {
|
function handleFetch(value?: any) {
|
||||||
if (value?.subSpecialistId) {
|
// handleFetchDoctors(props.subclassCode)
|
||||||
handleFetchDoctors(value.subSpecialistId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleEvent(menu: string, value?: any) {
|
async function handleEvent(menu: string, value?: any) {
|
||||||
if (menu === 'search') {
|
if (menu === 'search') {
|
||||||
|
// patientMode.value = 'exists'
|
||||||
getPatientsList({ 'page-size': 10, includes: 'person' }).then(() => {
|
getPatientsList({ 'page-size': 10, includes: 'person' }).then(() => {
|
||||||
openPatient.value = true
|
openPatient.value = true
|
||||||
})
|
})
|
||||||
} else if (menu === 'add') {
|
} else if (menu === 'add') {
|
||||||
navigateTo('/client/patient/add')
|
// navigateTo('/client/patient/add')
|
||||||
|
// patientMode.value = 'new'
|
||||||
} else if (menu === 'add-sep') {
|
} else if (menu === 'add-sep') {
|
||||||
if (isSepValid.value) {
|
if (isSepValid.value) {
|
||||||
return
|
return
|
||||||
@@ -97,7 +97,7 @@ async function handleEvent(menu: string, value?: any) {
|
|||||||
isService: 'false',
|
isService: 'false',
|
||||||
encounterId: props.id || null,
|
encounterId: props.id || null,
|
||||||
sourcePath: route.path,
|
sourcePath: route.path,
|
||||||
resource: `${props.classCode}-${props.subClassCode}`,
|
resource: `${props.classCode}-${props.subclassCode}`,
|
||||||
...value,
|
...value,
|
||||||
})
|
})
|
||||||
} else if (menu === 'sep-number-changed') {
|
} else if (menu === 'sep-number-changed') {
|
||||||
@@ -169,6 +169,7 @@ onMounted(async () => {
|
|||||||
Kunjungan
|
Kunjungan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- :patientMode="patientMode" -->
|
||||||
<AppEncounterEntryForm
|
<AppEncounterEntryForm
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:mode="props.formType"
|
:mode="props.formType"
|
||||||
@@ -213,7 +214,7 @@ onMounted(async () => {
|
|||||||
:is-action="true"
|
:is-action="true"
|
||||||
:histories="histories"
|
:histories="histories"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Footer Actions -->
|
<!-- Footer Actions -->
|
||||||
<div class="mt-6 flex justify-end gap-2 border-t border-t-slate-300 pt-4">
|
<div class="mt-6 flex justify-end gap-2 border-t border-t-slate-300 pt-4">
|
||||||
<Button
|
<Button
|
||||||
@@ -228,10 +229,10 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
Batal
|
Batal
|
||||||
</Button>
|
</Button>
|
||||||
|
<!-- :disabled="isSaveDisabled" -->
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
class="h-[40px] min-w-[120px] text-white"
|
class="h-[40px] min-w-[120px] text-white"
|
||||||
:disabled="isSaveDisabled"
|
|
||||||
@click="handleSaveClick"
|
@click="handleSaveClick"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import FilterForm from '~/components/app/encounter/filter-form.vue'
|
|||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient'
|
classCode?: 'ambulatory' | 'emergency' | 'inpatient'
|
||||||
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
subclassCode?: 'regular' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk' | undefined
|
||||||
canCreate?: boolean
|
canCreate?: boolean
|
||||||
canUpdate?: boolean
|
canUpdate?: boolean
|
||||||
canDelete?: boolean
|
canDelete?: boolean
|
||||||
@@ -162,7 +162,6 @@ async function getPatientList() {
|
|||||||
'Responsible_Doctor-employee',
|
'Responsible_Doctor-employee',
|
||||||
'Responsible_Doctor-employee-person',
|
'Responsible_Doctor-employee-person',
|
||||||
'EncounterDocuments',
|
'EncounterDocuments',
|
||||||
'unit',
|
|
||||||
'vclaimReference', // vclaimReference | vclaimSep
|
'vclaimReference', // vclaimReference | vclaimSep
|
||||||
]
|
]
|
||||||
const includesParams = includesParamsArrays.join(',')
|
const includesParams = includesParamsArrays.join(',')
|
||||||
@@ -172,8 +171,11 @@ async function getPatientList() {
|
|||||||
if (props.classCode) {
|
if (props.classCode) {
|
||||||
params['class-code'] = props.classCode
|
params['class-code'] = props.classCode
|
||||||
}
|
}
|
||||||
if (props.subClassCode) {
|
if (props.subclassCode == 'regular') {
|
||||||
params['sub-class-code'] = props.subClassCode
|
params['specialist-code'] = 'rehab'
|
||||||
|
params['specialist-code-opt'] = 'ne'
|
||||||
|
} else {
|
||||||
|
params['specialist-code'] = 'rehab'
|
||||||
}
|
}
|
||||||
const result = await getEncounterList(params)
|
const result = await getEncounterList(params)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -365,6 +367,7 @@ function handleRemoveConfirmation() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
{{ subclassCode }}
|
||||||
<CH.ContentHeader v-bind="hreaderPrep">
|
<CH.ContentHeader v-bind="hreaderPrep">
|
||||||
<FilterNav
|
<FilterNav
|
||||||
:active-positon="activeServicePosition"
|
:active-positon="activeServicePosition"
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import CheckOutView from '~/components/app/encounter/check-out-view.vue'
|
|||||||
import CheckOutEntry from '~/components/app/encounter/check-out-entry.vue'
|
import CheckOutEntry from '~/components/app/encounter/check-out-entry.vue'
|
||||||
import type { Encounter } from '~/models/encounter'
|
import type { Encounter } from '~/models/encounter'
|
||||||
import { checkIn } from '~/services/encounter.service'
|
import { checkIn } from '~/services/encounter.service'
|
||||||
|
import { getServicePosition } from '~/lib/roles'
|
||||||
|
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||||
|
|
||||||
//
|
//
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -22,11 +24,24 @@ const props = defineProps<{
|
|||||||
canUpdate?: boolean
|
canUpdate?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
//
|
||||||
|
const { user } = useUserStore()
|
||||||
|
const servicePosition = user.user_contractPosition_code == 'emp' ? getServicePosition('emp|'+user.employee_position_code) : null
|
||||||
|
// const subClassCode = servicePosition == 'chemo' ? 'chemo' : null
|
||||||
|
|
||||||
// doctors
|
// doctors
|
||||||
const localEncounter = ref<Encounter>(props.encounter)
|
const localEncounter = ref<Encounter>(props.encounter)
|
||||||
const doctors = await getDoctorValueLabelList({'includes': 'employee,employee-person'}, true)
|
const doctors = ref<Item[]>([])
|
||||||
const employees = await getEmployeeValueLabelList({'includes': 'person', 'position-code': 'reg'})
|
const employees = await getEmployeeValueLabelList({'includes': 'person', 'position-code': 'reg'})
|
||||||
const units = await getUnitValueLabelList()
|
const units = await getUnitValueLabelList()
|
||||||
|
const canUpdate = ref(true)
|
||||||
|
|
||||||
|
if (props.encounter.status_code == 'done') {
|
||||||
|
canUpdate.value = false
|
||||||
|
}
|
||||||
|
if (canUpdate.value) {
|
||||||
|
doctors.value = await getDoctorValueLabelList({'includes': 'employee,employee-person', 'unit-code': user.unit_code}, true)
|
||||||
|
}
|
||||||
|
|
||||||
// check in
|
// check in
|
||||||
const checkInValues = ref<any>({
|
const checkInValues = ref<any>({
|
||||||
@@ -94,6 +109,7 @@ function submitCheckOut(values: CheckOutFormData) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
v-if="canUpdate"
|
||||||
v-model:open="checkInDialogOpen"
|
v-model:open="checkInDialogOpen"
|
||||||
title="Ubah Informasi Masuk"
|
title="Ubah Informasi Masuk"
|
||||||
size="md"
|
size="md"
|
||||||
@@ -103,7 +119,7 @@ function submitCheckOut(values: CheckOutFormData) {
|
|||||||
:schema="CheckInSchema"
|
:schema="CheckInSchema"
|
||||||
:values="checkInValues"
|
:values="checkInValues"
|
||||||
:encounter="encounter"
|
:encounter="encounter"
|
||||||
:doctors="doctors"
|
:doctors="doctors || []"
|
||||||
:employees="employees"
|
:employees="employees"
|
||||||
:is-loading="checkInIsLoading"
|
:is-loading="checkInIsLoading"
|
||||||
:is-readonly="checkInIsReadonly"
|
:is-readonly="checkInIsReadonly"
|
||||||
@@ -113,6 +129,7 @@ function submitCheckOut(values: CheckOutFormData) {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
v-if="canUpdate"
|
||||||
v-model:open="checkOutDialogOpen"
|
v-model:open="checkOutDialogOpen"
|
||||||
title="Ubah Informasi Keluar"
|
title="Ubah Informasi Keluar"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|||||||
@@ -1,344 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
// type
|
|
||||||
import type { Patient, genPatientProps } from '~/models/patient'
|
|
||||||
import type { ExposedForm } from '~/types/form'
|
|
||||||
import type { PatientBase } from '~/models/patient'
|
|
||||||
|
|
||||||
// schema and models
|
|
||||||
import { genPatient } from '~/models/patient'
|
|
||||||
import { PatientSchema } from '~/schemas/patient.schema'
|
|
||||||
import { PersonAddressRelativeSchema } from '~/schemas/person-address-relative.schema'
|
|
||||||
import { PersonAddressSchema } from '~/schemas/person-address.schema'
|
|
||||||
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
|
|
||||||
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
|
|
||||||
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
|
|
||||||
|
|
||||||
// components
|
|
||||||
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
|
||||||
import AppPatientEntryForm from '~/components/app/patient/entry-form.vue'
|
|
||||||
import AppPersonAddressEntryForm from '~/components/app/person-address/entry-form.vue'
|
|
||||||
import AppPersonAddressEntryFormRelative from '~/components/app/person-address/entry-form-relative.vue'
|
|
||||||
import AppPersonFamilyParentsForm from '~/components/app/person/family-parents-form-bak.vue'
|
|
||||||
import AppPersonContactEntryForm from '~/components/app/person-contact/entry-form.vue'
|
|
||||||
import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-form.vue'
|
|
||||||
|
|
||||||
// services
|
|
||||||
import { uploadAttachment } from '~/services/patient.service'
|
|
||||||
|
|
||||||
import {
|
|
||||||
// for form entry
|
|
||||||
isReadonly,
|
|
||||||
isProcessing,
|
|
||||||
isFormEntryDialogOpen,
|
|
||||||
isRecordConfirmationOpen,
|
|
||||||
onResetState,
|
|
||||||
handleActionSave,
|
|
||||||
handleCancelForm,
|
|
||||||
} from '~/handlers/patient.handler'
|
|
||||||
|
|
||||||
import { toast } from '~/components/pub/ui/toast'
|
|
||||||
|
|
||||||
// #region Props & Emits
|
|
||||||
const props = defineProps<{
|
|
||||||
callbackUrl?: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const residentIdentityFile = ref<File>()
|
|
||||||
const familyCardFile = ref<File>()
|
|
||||||
|
|
||||||
// form related state
|
|
||||||
const personAddressForm = ref<ExposedForm<any> | null>(null)
|
|
||||||
const personAddressRelativeForm = ref<ExposedForm<any> | null>(null)
|
|
||||||
const personContactForm = ref<ExposedForm<any> | null>(null)
|
|
||||||
const personEmergencyContactRelative = ref<ExposedForm<any> | null>(null)
|
|
||||||
const personFamilyForm = ref<ExposedForm<any> | null>(null)
|
|
||||||
const personPatientForm = ref<ExposedForm<any> | null>(null)
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// #region State & Computed
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// #region Lifecycle Hooks
|
|
||||||
onMounted(() => {
|
|
||||||
// Initial synchronization when forms are mounted and isSameAddress is true by default
|
|
||||||
nextTick(() => {
|
|
||||||
const isSameAddress = personAddressRelativeForm.value?.values?.isSameAddress
|
|
||||||
if (
|
|
||||||
(isSameAddress === true || isSameAddress === '1') &&
|
|
||||||
personAddressForm.value?.values &&
|
|
||||||
personAddressRelativeForm.value
|
|
||||||
) {
|
|
||||||
const currentAddressValues = personAddressForm.value.values
|
|
||||||
if (Object.keys(currentAddressValues).length > 0) {
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// #region Functions
|
|
||||||
async function composeFormData(): Promise<Patient> {
|
|
||||||
const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
|
|
||||||
personPatientForm.value?.validate(),
|
|
||||||
personAddressForm.value?.validate(),
|
|
||||||
personAddressRelativeForm.value?.validate(),
|
|
||||||
personFamilyForm.value?.validate(),
|
|
||||||
personContactForm.value?.validate(),
|
|
||||||
personEmergencyContactRelative.value?.validate(),
|
|
||||||
])
|
|
||||||
|
|
||||||
const results = [patient, address, addressRelative, families, contacts, emergencyContact]
|
|
||||||
console.log(results)
|
|
||||||
const allValid = results.every((r) => r?.valid)
|
|
||||||
|
|
||||||
// exit, if form errors happend during validation
|
|
||||||
// for example: dropdown not selected
|
|
||||||
if (!allValid) return Promise.reject('Form validation failed')
|
|
||||||
|
|
||||||
const formDataRequest: genPatientProps = {
|
|
||||||
patient: patient?.values,
|
|
||||||
residentAddress: address?.values,
|
|
||||||
cardAddress: addressRelative?.values,
|
|
||||||
familyData: families?.values,
|
|
||||||
contacts: contacts?.values,
|
|
||||||
responsible: emergencyContact?.values,
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = genPatient()
|
|
||||||
|
|
||||||
if (patient?.values.residentIdentityFile) {
|
|
||||||
residentIdentityFile.value = patient?.values.residentIdentityFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if (patient?.values.familyIdentityFile) {
|
|
||||||
familyCardFile.value = patient?.values.familyIdentityFile
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => resolve(formData))
|
|
||||||
}
|
|
||||||
// #endregion region
|
|
||||||
|
|
||||||
// #region Utilities & event handlers
|
|
||||||
async function handleActionClick(eventType: string) {
|
|
||||||
try {
|
|
||||||
if (eventType === 'submit') {
|
|
||||||
const patient: Patient = await composeFormData()
|
|
||||||
let createdPatientId = 0
|
|
||||||
|
|
||||||
const response = await handleActionSave(
|
|
||||||
patient,
|
|
||||||
() => {},
|
|
||||||
() => {},
|
|
||||||
toast,
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = (response?.body?.data ?? null) as PatientBase | null
|
|
||||||
if (!data) return
|
|
||||||
createdPatientId = data.id
|
|
||||||
|
|
||||||
if (residentIdentityFile.value) {
|
|
||||||
void uploadAttachment(residentIdentityFile.value, createdPatientId, 'ktp')
|
|
||||||
}
|
|
||||||
if (familyCardFile.value) {
|
|
||||||
void uploadAttachment(familyCardFile.value, createdPatientId, 'kk')
|
|
||||||
}
|
|
||||||
|
|
||||||
// If has callback provided redirect to callback with patientData
|
|
||||||
if (props.callbackUrl && props.callbackUrl.length > 0) {
|
|
||||||
await navigateTo(props.callbackUrl + '?patient-id=' + createdPatientId)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate to patient list or show success message
|
|
||||||
await navigateTo('/client/patient')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'cancel') {
|
|
||||||
if (props.callbackUrl) {
|
|
||||||
await navigateTo(props.callbackUrl)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await navigateTo({
|
|
||||||
name: 'client-patient',
|
|
||||||
})
|
|
||||||
// handleCancelForm()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Show error toast to user
|
|
||||||
if (typeof error === 'string') {
|
|
||||||
toast({
|
|
||||||
title: 'Error',
|
|
||||||
description: error,
|
|
||||||
variant: 'destructive',
|
|
||||||
})
|
|
||||||
} else if (error instanceof Error) {
|
|
||||||
toast({
|
|
||||||
title: 'Error',
|
|
||||||
description: error.message || 'Terjadi kesalahan saat menyimpan data',
|
|
||||||
variant: 'destructive',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: 'Error',
|
|
||||||
description: 'Terjadi kesalahan saat menyimpan data',
|
|
||||||
variant: 'destructive',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
// #region Watchers
|
|
||||||
// Watcher untuk sinkronisasi initial ketika kedua form sudah ready
|
|
||||||
watch(
|
|
||||||
[() => personAddressForm.value, () => personAddressRelativeForm.value],
|
|
||||||
([addressForm, relativeForm]) => {
|
|
||||||
if (addressForm && relativeForm) {
|
|
||||||
// Trigger initial sync jika isSameAddress adalah true
|
|
||||||
nextTick(() => {
|
|
||||||
const isSameAddress = relativeForm.values?.isSameAddress
|
|
||||||
if ((isSameAddress === true || isSameAddress === '1') && addressForm.values) {
|
|
||||||
const currentAddressValues = addressForm.values
|
|
||||||
if (Object.keys(currentAddressValues).length > 0) {
|
|
||||||
relativeForm.setValues(
|
|
||||||
{
|
|
||||||
...relativeForm.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher untuk sinkronisasi alamat ketika isSameAddress = true
|
|
||||||
watch(
|
|
||||||
() => personAddressForm.value?.values,
|
|
||||||
(newAddressValues) => {
|
|
||||||
// Cek apakah alamat KTP harus sama dengan alamat sekarang
|
|
||||||
const isSameAddress = personAddressRelativeForm.value?.values?.isSameAddress
|
|
||||||
|
|
||||||
if ((isSameAddress === true || isSameAddress === '1') && newAddressValues && personAddressRelativeForm.value) {
|
|
||||||
// Sinkronkan semua field alamat dari alamat sekarang ke alamat KTP
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: newAddressValues.province_code || undefined,
|
|
||||||
regency_code: newAddressValues.regency_code || undefined,
|
|
||||||
district_code: newAddressValues.district_code || undefined,
|
|
||||||
village_code: newAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: newAddressValues.postalRegion_code || undefined,
|
|
||||||
address: newAddressValues.address || undefined,
|
|
||||||
rt: newAddressValues.rt || undefined,
|
|
||||||
rw: newAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher untuk memantau perubahan isSameAddress
|
|
||||||
watch(
|
|
||||||
() => personAddressRelativeForm.value?.values?.isSameAddress,
|
|
||||||
(isSameAddress) => {
|
|
||||||
if (
|
|
||||||
(isSameAddress === true || isSameAddress === '1') &&
|
|
||||||
personAddressForm.value?.values &&
|
|
||||||
personAddressRelativeForm.value?.values
|
|
||||||
) {
|
|
||||||
// Ketika isSameAddress diubah menjadi true, copy alamat sekarang ke alamat KTP
|
|
||||||
const currentAddressValues = personAddressForm.value.values
|
|
||||||
personAddressRelativeForm.value.setValues(
|
|
||||||
{
|
|
||||||
...personAddressRelativeForm.value.values,
|
|
||||||
province_code: currentAddressValues.province_code || undefined,
|
|
||||||
regency_code: currentAddressValues.regency_code || undefined,
|
|
||||||
district_code: currentAddressValues.district_code || undefined,
|
|
||||||
village_code: currentAddressValues.village_code || undefined,
|
|
||||||
postalRegion_code: currentAddressValues.postalRegion_code || undefined,
|
|
||||||
address: currentAddressValues.address || undefined,
|
|
||||||
rt: currentAddressValues.rt || undefined,
|
|
||||||
rw: currentAddressValues.rw || undefined,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
// #endregion
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Tambah Pasien</div>
|
|
||||||
<AppPatientEntryForm
|
|
||||||
ref="personPatientForm"
|
|
||||||
:schema="PatientSchema"
|
|
||||||
/>
|
|
||||||
<div class="h-6"></div>
|
|
||||||
<AppPersonAddressEntryForm
|
|
||||||
ref="personAddressForm"
|
|
||||||
title="Alamat Sekarang"
|
|
||||||
:schema="PersonAddressSchema"
|
|
||||||
/>
|
|
||||||
<div class="h-6"></div>
|
|
||||||
<AppPersonAddressEntryFormRelative
|
|
||||||
ref="personAddressRelativeForm"
|
|
||||||
title="Alamat KTP"
|
|
||||||
:schema="PersonAddressRelativeSchema"
|
|
||||||
/>
|
|
||||||
<div class="h-6"></div>
|
|
||||||
<AppPersonFamilyParentsForm
|
|
||||||
ref="personFamilyForm"
|
|
||||||
title="Identitas Orang Tua"
|
|
||||||
:schema="PersonFamiliesSchema"
|
|
||||||
/>
|
|
||||||
<div class="h-6"></div>
|
|
||||||
<AppPersonContactEntryForm
|
|
||||||
ref="personContactForm"
|
|
||||||
title="Kontak Pasien"
|
|
||||||
:schema="PersonContactListSchema"
|
|
||||||
/>
|
|
||||||
<AppPersonRelativeEntryForm
|
|
||||||
ref="personEmergencyContactRelative"
|
|
||||||
title="Penanggung Jawab"
|
|
||||||
:schema="ResponsiblePersonSchema"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="my-2 flex justify-end py-2">
|
|
||||||
<Action @click="handleActionClick" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* component style */
|
|
||||||
</style>
|
|
||||||
@@ -6,6 +6,8 @@ import type { Person } from '~/models/person'
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||||
|
|
||||||
import { getPatientDetail } from '~/services/patient.service'
|
import { getPatientDetail } from '~/services/patient.service'
|
||||||
|
|
||||||
@@ -17,6 +19,9 @@ const props = defineProps<{
|
|||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region State & Computed
|
// #region State & Computed
|
||||||
|
const isDocPreviewDialogOpen = ref<boolean>(false)
|
||||||
|
const docPreviewUrl = ref<string>('')
|
||||||
|
|
||||||
const patient = ref(
|
const patient = ref(
|
||||||
withBase<PatientEntity>({
|
withBase<PatientEntity>({
|
||||||
person: {} as Person,
|
person: {} as Person,
|
||||||
@@ -80,6 +85,19 @@ async function onEdit() {
|
|||||||
:patient="patient"
|
:patient="patient"
|
||||||
@back="onBack"
|
@back="onBack"
|
||||||
@edit="onEdit"
|
@edit="onEdit"
|
||||||
|
@preview="
|
||||||
|
(url: string) => {
|
||||||
|
docPreviewUrl = url
|
||||||
|
isDocPreviewDialogOpen = true
|
||||||
|
}
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
|
<Dialog
|
||||||
|
v-model:open="isDocPreviewDialogOpen"
|
||||||
|
title="Preview Dokumen"
|
||||||
|
size="2xl"
|
||||||
|
>
|
||||||
|
<DocPreviewDialog :link="docPreviewUrl" />
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -17,6 +17,8 @@ import AppPersonAddressEntryFormRelative from '~/components/app/person-address/e
|
|||||||
import AppPersonFamilyParentsForm from '~/components/app/person/family-parents-form.vue'
|
import AppPersonFamilyParentsForm from '~/components/app/person/family-parents-form.vue'
|
||||||
import AppPersonContactEntryForm from '~/components/app/person-contact/entry-form.vue'
|
import AppPersonContactEntryForm from '~/components/app/person-contact/entry-form.vue'
|
||||||
import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-form.vue'
|
import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-form.vue'
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||||
|
|
||||||
// helper
|
// helper
|
||||||
import { format, parseISO } from 'date-fns'
|
import { format, parseISO } from 'date-fns'
|
||||||
@@ -24,6 +26,8 @@ import { id as localeID } from 'date-fns/locale'
|
|||||||
|
|
||||||
// services
|
// services
|
||||||
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'
|
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'
|
||||||
|
import { getValueLabelList as getEthnicOpts } from '~/services/ethnic.service'
|
||||||
|
import { getValueLabelList as getLanguageOpts } from '~/services/language.service'
|
||||||
|
|
||||||
import { isReadonly, isProcessing, handleActionSave, handleActionEdit } from '~/handlers/patient.handler'
|
import { isReadonly, isProcessing, handleActionSave, handleActionEdit } from '~/handlers/patient.handler'
|
||||||
|
|
||||||
@@ -47,6 +51,23 @@ const props = defineProps<{
|
|||||||
const residentIdentityFile = ref<File>()
|
const residentIdentityFile = ref<File>()
|
||||||
const familyCardFile = ref<File>()
|
const familyCardFile = ref<File>()
|
||||||
|
|
||||||
|
// Dialog preview dokumen
|
||||||
|
const isDocPreviewDialogOpen = ref<boolean>(false)
|
||||||
|
const docPreviewUrl = ref<string>('')
|
||||||
|
|
||||||
|
// Computed untuk URL dokumen tersimpan
|
||||||
|
const identityFileUrl = computed(() => {
|
||||||
|
return patientDetail.value.person?.residentIdentityFileUrl || ''
|
||||||
|
})
|
||||||
|
const familyCardFileUrl = computed(() => {
|
||||||
|
return patientDetail.value.person?.familyIdentityFileUrl || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
function handlePreviewDoc(url: string) {
|
||||||
|
docPreviewUrl.value = url
|
||||||
|
isDocPreviewDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
// form related state
|
// form related state
|
||||||
const personAddressForm = ref<ExposedForm<any> | null>(null)
|
const personAddressForm = ref<ExposedForm<any> | null>(null)
|
||||||
const personAddressRelativeForm = ref<ExposedForm<any> | null>(null)
|
const personAddressRelativeForm = ref<ExposedForm<any> | null>(null)
|
||||||
@@ -58,6 +79,9 @@ const personPatientForm = ref<ExposedForm<any> | null>(null)
|
|||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #region State & Computed
|
// #region State & Computed
|
||||||
|
const ethnicOptions = ref<{ value: string; label: string }[]>([])
|
||||||
|
const languageOptions = ref<{ value: string; label: string }[]>([])
|
||||||
|
|
||||||
const patientDetail = ref(
|
const patientDetail = ref(
|
||||||
withBase<PatientEntity>({
|
withBase<PatientEntity>({
|
||||||
person: {} as Person,
|
person: {} as Person,
|
||||||
@@ -223,6 +247,13 @@ const responsibleFormInitialValues = computed(() => {
|
|||||||
|
|
||||||
// #region Lifecycle Hooks
|
// #region Lifecycle Hooks
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
const optsReq = {
|
||||||
|
'page-no-limit': true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ethnicOptions.value = await getEthnicOpts(optsReq, true)
|
||||||
|
languageOptions.value = await getLanguageOpts(optsReq, true)
|
||||||
|
|
||||||
// if edit mode, fetch patient detail
|
// if edit mode, fetch patient detail
|
||||||
if (props.mode === 'edit' && props.patientId) {
|
if (props.mode === 'edit' && props.patientId) {
|
||||||
await loadInitData(props.patientId)
|
await loadInitData(props.patientId)
|
||||||
@@ -309,7 +340,9 @@ async function handleActionClick(eventType: string) {
|
|||||||
const patient: Patient = await composeFormData()
|
const patient: Patient = await composeFormData()
|
||||||
|
|
||||||
let createdPatientId = 0
|
let createdPatientId = 0
|
||||||
|
let createdPersonId = 0
|
||||||
let response: any
|
let response: any
|
||||||
|
|
||||||
// return
|
// return
|
||||||
if (props.mode === 'edit' && props.patientId) {
|
if (props.mode === 'edit' && props.patientId) {
|
||||||
response = await handleActionEdit(
|
response = await handleActionEdit(
|
||||||
@@ -330,13 +363,15 @@ async function handleActionClick(eventType: string) {
|
|||||||
|
|
||||||
const data = (response?.body?.data ?? null) as PatientBase | null
|
const data = (response?.body?.data ?? null) as PatientBase | null
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
|
||||||
createdPatientId = data.id
|
createdPatientId = data.id
|
||||||
|
createdPersonId = data.person_id!
|
||||||
|
|
||||||
if (residentIdentityFile.value) {
|
if (residentIdentityFile.value) {
|
||||||
void uploadAttachment(residentIdentityFile.value, createdPatientId, 'ktp')
|
void uploadAttachment(residentIdentityFile.value, createdPersonId, 'ktp')
|
||||||
}
|
}
|
||||||
if (familyCardFile.value) {
|
if (familyCardFile.value) {
|
||||||
void uploadAttachment(familyCardFile.value, createdPatientId, 'kk')
|
void uploadAttachment(familyCardFile.value, createdPersonId, 'kk')
|
||||||
}
|
}
|
||||||
|
|
||||||
// If has callback provided redirect to callback with patientData
|
// If has callback provided redirect to callback with patientData
|
||||||
@@ -435,10 +470,16 @@ watch(
|
|||||||
{{ mode === 'edit' ? 'Edit Pasien' : 'Tambah Pasien' }}
|
{{ mode === 'edit' ? 'Edit Pasien' : 'Tambah Pasien' }}
|
||||||
</div>
|
</div>
|
||||||
<AppPatientEntryForm
|
<AppPatientEntryForm
|
||||||
:key="`patient-${formKey}`"
|
|
||||||
ref="personPatientForm"
|
ref="personPatientForm"
|
||||||
|
:key="`patient-${formKey}`"
|
||||||
:is-readonly="isProcessing || isReadonly"
|
:is-readonly="isProcessing || isReadonly"
|
||||||
:initial-values="patientFormInitialValues"
|
:initial-values="patientFormInitialValues"
|
||||||
|
:language-options="languageOptions"
|
||||||
|
:ethnic-options="ethnicOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:identity-file-url="identityFileUrl"
|
||||||
|
:family-card-file-url="familyCardFileUrl"
|
||||||
|
@preview="handlePreviewDoc"
|
||||||
/>
|
/>
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
<AppPersonAddressEntryForm
|
<AppPersonAddressEntryForm
|
||||||
@@ -483,6 +524,15 @@ watch(
|
|||||||
<div class="my-2 flex justify-end py-2">
|
<div class="my-2 flex justify-end py-2">
|
||||||
<Action @click="handleActionClick" />
|
<Action @click="handleActionClick" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog Preview Dokumen -->
|
||||||
|
<Dialog
|
||||||
|
v-model:open="isDocPreviewDialogOpen"
|
||||||
|
title="Preview Dokumen"
|
||||||
|
size="2xl"
|
||||||
|
>
|
||||||
|
<DocPreviewDialog :link="docPreviewUrl" />
|
||||||
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ import SoapiList from './list.vue'
|
|||||||
import EarlyForm from './form.vue'
|
import EarlyForm from './form.vue'
|
||||||
import RehabForm from './form-rehab.vue'
|
import RehabForm from './form-rehab.vue'
|
||||||
import FunctionForm from './form-function.vue'
|
import FunctionForm from './form-function.vue'
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
encounter: Encounter
|
||||||
|
label: string
|
||||||
|
canUpdate?: boolean
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const type = computed(() => (route.query.menu as string) || 'early-medical-assessment')
|
const type = computed(() => (route.query.menu as string) || 'early-medical-assessment')
|
||||||
@@ -23,8 +31,9 @@ const ActiveForm = computed(() => formMap[type.value] || EarlyForm)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- {{ type }} -->
|
||||||
<div>
|
<div>
|
||||||
<SoapiList v-if="mode === 'list'" />
|
<SoapiList v-if="mode === 'list'" :label="label" :canUpdate="canUpdate" />
|
||||||
<component
|
<component
|
||||||
v-else
|
v-else
|
||||||
:is="ActiveForm"
|
:is="ActiveForm"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import type { Encounter } from '~/models/encounter'
|
|||||||
interface Props {
|
interface Props {
|
||||||
encounter: Encounter
|
encounter: Encounter
|
||||||
label: string
|
label: string
|
||||||
|
canUpdate?: boolean
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const emits = defineEmits(['add', 'edit'])
|
const emits = defineEmits(['add', 'edit'])
|
||||||
@@ -58,10 +59,12 @@ const typeCode = ref('')
|
|||||||
const hreaderPrep: HeaderPrep = {
|
const hreaderPrep: HeaderPrep = {
|
||||||
title: props.label,
|
title: props.label,
|
||||||
icon: 'i-lucide-users',
|
icon: 'i-lucide-users',
|
||||||
addNav: {
|
}
|
||||||
|
if(props.canUpdate) {
|
||||||
|
hreaderPrep['addNav'] = {
|
||||||
label: 'Tambah',
|
label: 'Tambah',
|
||||||
onClick: () => goToEntry(),
|
onClick: () => goToEntry(),
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = computed(() => (route.query.menu as string) || 'early-medical-assessment')
|
const type = computed(() => (route.query.menu as string) || 'early-medical-assessment')
|
||||||
@@ -169,6 +172,8 @@ provide('table_data_loader', isLoading)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- {{ canUpdate }}
|
||||||
|
{{ hreaderPrep }} -->
|
||||||
<Header
|
<Header
|
||||||
:prep="{ ...hreaderPrep }"
|
:prep="{ ...hreaderPrep }"
|
||||||
:ref-search-nav="refSearchNav"
|
:ref-search-nav="refSearchNav"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSidebar } from '~/components/pub/ui/sidebar'
|
import { useSidebar } from '~/components/pub/ui/sidebar'
|
||||||
|
import { useKeycloak } from "~/composables/useKeycloack"
|
||||||
|
|
||||||
// defineProps<{
|
// defineProps<{
|
||||||
// user: {
|
// user: {
|
||||||
@@ -11,11 +12,15 @@ import { useSidebar } from '~/components/pub/ui/sidebar'
|
|||||||
|
|
||||||
const { isMobile } = useSidebar()
|
const { isMobile } = useSidebar()
|
||||||
const { user, logout, setActiveRole, getActiveRole } = useUserStore()
|
const { user, logout, setActiveRole, getActiveRole } = useUserStore()
|
||||||
|
const { initKeycloak, logoutSSO } = useKeycloak()
|
||||||
// const userStore = useUserStore().user
|
// const userStore = useUserStore().user
|
||||||
|
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
|
initKeycloak('check-sso')
|
||||||
|
|
||||||
navigateTo('/auth/login')
|
navigateTo('/auth/login')
|
||||||
logout()
|
logout()
|
||||||
|
logoutSSO(window.location.origin + '/auth/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
const showModalTheme = ref(false)
|
const showModalTheme = ref(false)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface Item {
|
export interface Item {
|
||||||
value: string | number
|
value: string
|
||||||
label: string
|
label: string
|
||||||
code?: string
|
code?: string
|
||||||
priority?: number
|
priority?: number
|
||||||
|
|||||||
@@ -1,40 +1,33 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
height?: number
|
height?: number
|
||||||
|
class?: string
|
||||||
activeTab?: 1 | 2
|
activeTab?: 1 | 2
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const classVal = computed(() => {
|
||||||
|
return props.class ? props.class : ''
|
||||||
|
})
|
||||||
const activeTab = ref(props.activeTab || 1)
|
const activeTab = ref(props.activeTab || 1)
|
||||||
|
|
||||||
function handleClick(value: 1 | 2) {
|
function switchActiveTab() {
|
||||||
activeTab.value = value
|
activeTab.value = activeTab.value === 1 ? 2 : 1
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="content-switcher" :style="`height: ${height || 200}px`">
|
<div :class="`content-switcher ${classVal}`" :style="height ? `height:${200}px` : ''">
|
||||||
<div :class="`${activeTab === 1 ? 'active' : 'inactive'}`">
|
<div class="wrapper">
|
||||||
<div class="content-wrapper">
|
<div :class="`item item-1 ${activeTab === 1 ? 'active' : 'inactive'}`">
|
||||||
<div>
|
<slot name="content1" />
|
||||||
<slot name="content1" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content-nav">
|
<div :class="`nav border-slate-300 ${ activeTab == 1 ? 'border-l' : 'border-r'}`">
|
||||||
<button @click="handleClick(1)">
|
<button @click="switchActiveTab()" class="!p-0 w-full h-full">
|
||||||
<Icon name="i-lucide-chevron-right" />
|
<Icon :name="activeTab == 1 ? 'i-lucide-chevron-left' : 'i-lucide-chevron-right'" class="text-3xl" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div :class="`item item-2 ${activeTab === 2 ? 'active' : 'inactive'}`">
|
||||||
<div :class="`${activeTab === 2 ? 'active' : 'inactive'}`">
|
<slot name="content2" />
|
||||||
<div class="content-nav">
|
|
||||||
<button @click="handleClick(2)">
|
|
||||||
<Icon name="i-lucide-chevron-left" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="content-wrapper">
|
|
||||||
<div>
|
|
||||||
<slot name="content2" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,45 +35,24 @@ function handleClick(value: 1 | 2) {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.content-switcher {
|
.content-switcher {
|
||||||
@apply flex overflow-hidden gap-3
|
@apply overflow-hidden
|
||||||
}
|
}
|
||||||
.content-switcher > * {
|
.wrapper {
|
||||||
@apply border border-slate-300 rounded-md flex overflow-hidden
|
@apply flex w-[200%] h-full
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
@apply w-[calc(50%-60px)]
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.item-1.active {
|
||||||
@apply p-4 2xl:p-5 overflow-hidden grow
|
@apply ms-0 transition-all duration-500 ease-in-out
|
||||||
}
|
}
|
||||||
.inactive .content-wrapper {
|
.item-1.inactive {
|
||||||
@apply p-0 w-0
|
@apply -ms-[calc(50%-60px)] transition-all duration-500 ease-in-out
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-nav {
|
.nav {
|
||||||
@apply h-full flex flex-row items-center justify-center content-center !text-2xl overflow-hidden
|
@apply h-full w-[60px] flex flex-row items-center justify-center content-center !text-2xl overflow-hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-nav button {
|
|
||||||
@apply pt-2 px-2 h-full w-full
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .content-switcher .inactive > .content-wrapper {
|
|
||||||
@apply w-0 p-0 opacity-0 transition-all duration-500 ease-in-out
|
|
||||||
} */
|
|
||||||
.content-switcher .inactive {
|
|
||||||
@apply w-16 transition-all duration-500 ease-in-out
|
|
||||||
}
|
|
||||||
.content-switcher .inactive > .content-nav {
|
|
||||||
@apply w-full transition-all duration-100 ease-in-out
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-switcher .active {
|
|
||||||
@apply grow transition-all duration-500 ease-in-out
|
|
||||||
}
|
|
||||||
.content-switcher .active > .content-nav {
|
|
||||||
@apply w-0 transition-all duration-100 ease-in-out
|
|
||||||
}
|
|
||||||
/* .content-switcher .active > .content-wrapper {
|
|
||||||
@apply w-full delay-1000 transition-all duration-1000 ease-in-out
|
|
||||||
} */
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -4,6 +4,9 @@ import { type EncounterItem } from "~/handlers/encounter-init.handler";
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialActiveMenu: string
|
initialActiveMenu: string
|
||||||
data: EncounterItem[]
|
data: EncounterItem[]
|
||||||
|
canCreate?: boolean
|
||||||
|
canUpdate?: boolean
|
||||||
|
canDelete?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const activeMenu = ref(props.initialActiveMenu)
|
const activeMenu = ref(props.initialActiveMenu)
|
||||||
@@ -38,7 +41,11 @@ function changeMenu(value: string) {
|
|||||||
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
<component
|
<component
|
||||||
:is="data.find((m) => m.id === activeMenu)?.component"
|
:is="data.find((m) => m.id === activeMenu)?.component"
|
||||||
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
v-bind="data.find((m) => m.id === activeMenu)?.props"
|
||||||
|
:can-create="canCreate"
|
||||||
|
:can-update="canUpdate"
|
||||||
|
:can-delete="canDelete"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import Keycloak from "keycloak-js";
|
||||||
|
import { ref, computed, onBeforeMount } from "vue";
|
||||||
|
|
||||||
|
let kc: any | null = null;
|
||||||
|
|
||||||
|
const initialized = ref(false);
|
||||||
|
const authenticated = ref(false);
|
||||||
|
const token = ref<string | null>(null);
|
||||||
|
const profile = ref<any>(null);
|
||||||
|
|
||||||
|
export function useKeycloak() {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const initKeycloak = async (onLoad: "login-required" | "check-sso" = "check-sso") => {
|
||||||
|
if (kc) return kc;
|
||||||
|
kc = new Keycloak({
|
||||||
|
url: config.public.KEYCLOAK_URL,
|
||||||
|
realm: config.public.KEYCLOAK_REALM,
|
||||||
|
clientId: config.public.CLIENT_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const initOptions = {
|
||||||
|
onLoad,
|
||||||
|
promiseType: "native" as const,
|
||||||
|
pkceMethod: "S256" as const,
|
||||||
|
};
|
||||||
|
console.log(kc.url)
|
||||||
|
authenticated.value = await kc.init(initOptions);
|
||||||
|
initialized.value = true;
|
||||||
|
token.value = kc.token ?? null;
|
||||||
|
if (authenticated.value) {
|
||||||
|
try {
|
||||||
|
profile.value = await kc.loadUserProfile();
|
||||||
|
} catch (e) {
|
||||||
|
profile.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// automatically update token in background
|
||||||
|
kc.onTokenExpired = async () => {
|
||||||
|
try {
|
||||||
|
const refreshed = await kc.updateToken(30);
|
||||||
|
token.value = kc?.token ?? null;
|
||||||
|
if (!refreshed) {
|
||||||
|
// token not refreshed but still valid
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to refresh token", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return kc;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(authenticated)
|
||||||
|
console.error("Keycloak init xyz failed", err);
|
||||||
|
initialized.value = true;
|
||||||
|
authenticated.value = false;
|
||||||
|
return kc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginSSO = (options?: Keycloak.KeycloakLoginOptions) => {
|
||||||
|
if (!kc) throw new Error("Keycloak not initialized");
|
||||||
|
return kc.login(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutSSO = (redirectUri?: string) => {
|
||||||
|
if (!kc) throw new Error("Keycloak not initialized");
|
||||||
|
return kc.logout({ redirectUri });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getToken = () => token.value;
|
||||||
|
const isAuthenticated = computed(() => authenticated.value);
|
||||||
|
const getProfile = () => profile.value;
|
||||||
|
|
||||||
|
// init on client automatically
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// try check-sso silently
|
||||||
|
if (!initialized.value) initKeycloak("check-sso");
|
||||||
|
});
|
||||||
|
|
||||||
|
const apiErrors = ref<Record<string, string>>({})
|
||||||
|
const { login } = useUserStore()
|
||||||
|
|
||||||
|
const getResponse = async () => {
|
||||||
|
console.log("=================== onto login fes!!! ===================")
|
||||||
|
const params = {
|
||||||
|
token: token.value,
|
||||||
|
user: profile.value
|
||||||
|
}
|
||||||
|
const result = await xfetch('/api/v1/authentication/login-fes', 'POST', {
|
||||||
|
data: params,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
const { data: rawdata, meta } = result.body
|
||||||
|
if (meta.status === 'verified') {
|
||||||
|
login(rawdata)
|
||||||
|
navigateTo('/')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result.errors) {
|
||||||
|
Object.entries(result.errors).forEach(
|
||||||
|
([field, errorInfo]: [string, any]) => (apiErrors.value[field] = errorInfo.message),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
apiErrors.value.general = result.error?.message || result.message || 'Login failed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
initKeycloak,
|
||||||
|
loginSSO,
|
||||||
|
logoutSSO,
|
||||||
|
getToken,
|
||||||
|
isAuthenticated,
|
||||||
|
getProfile,
|
||||||
|
getResponse,
|
||||||
|
};
|
||||||
|
}
|
||||||
+36
-24
@@ -2,35 +2,47 @@ export const systemCode = 'system'
|
|||||||
|
|
||||||
export const rehabInstCode = 'rehab'
|
export const rehabInstCode = 'rehab'
|
||||||
export const rehabUnitCode = 'rehab'
|
export const rehabUnitCode = 'rehab'
|
||||||
|
export const chemoUnitCode = 'chemo'
|
||||||
|
|
||||||
export const headPosCode = 'head' // head position
|
// Object keys are faster than array
|
||||||
export const respPosCode = 'resp' // responsible position, verificator
|
export const infraPositions: Record<string, boolean> = {
|
||||||
|
'head': true,
|
||||||
|
'resp': true,
|
||||||
|
'doc': true,
|
||||||
|
'nur':true
|
||||||
|
}
|
||||||
|
export type InfraPositionCode = keyof typeof infraPositions
|
||||||
|
|
||||||
export type UnitLevel =
|
export const servicePositioons: Record<string, boolean> = {
|
||||||
'inst' | // installation
|
'none': true,
|
||||||
'unit' | // unit / poly
|
'reg': true,
|
||||||
'spec' | // specialist
|
'scr': true,
|
||||||
'subspec' // subspecialist
|
'med': true,
|
||||||
|
}
|
||||||
|
export type ServicePositionCode = keyof typeof servicePositioons
|
||||||
|
|
||||||
export const medicalRoles = [
|
export const medicalRoles: Record<string, boolean> = {
|
||||||
'emp|doc', // doctor
|
'emp|doc': true, // doctor
|
||||||
'emp|nur', // nurse
|
'emp|nur': true, // nurse
|
||||||
'emp|miw', // midwife
|
'emp|miw': true, // midwife
|
||||||
'emp|thr', // therapist
|
'emp|thr': true, // therapist
|
||||||
'emp|nut', // nutritionist
|
'emp|nut': true, // nutritionist
|
||||||
'emp|pha', // pharmacy
|
'emp|pha': true, // pharmacy
|
||||||
'emp|lab' // laborant
|
'emp|lab': true // laborant
|
||||||
]
|
}
|
||||||
|
export type MedicalRoleCode = keyof typeof medicalRoles
|
||||||
|
|
||||||
export const serviceRoles = [
|
export const serviceRoles: Record<string, boolean> = {
|
||||||
'emp|reg',
|
'emp|reg': true,
|
||||||
|
'emp|scr': true,
|
||||||
...medicalRoles,
|
...medicalRoles,
|
||||||
]
|
|
||||||
|
|
||||||
export function genSpecHeadCode(unit_level: UnitLevel, unit_code: string): string {
|
|
||||||
return `${unit_level}|${unit_code}|${headPosCode}`
|
|
||||||
}
|
}
|
||||||
|
export type ServiceRoleCode = keyof typeof serviceRoles
|
||||||
|
|
||||||
export function genUnitRespCode(unit_level: UnitLevel, unit_code: string): string {
|
export const unitLevels: Record<string, boolean> = {
|
||||||
return `${unit_level}|${unit_code}|${respPosCode}`
|
'inst': true, // installation
|
||||||
|
'unit': true, // unit / poly
|
||||||
|
'spec': true, // specialist
|
||||||
|
'subspec': true, // subspecialist
|
||||||
}
|
}
|
||||||
|
export type UnitLevel = keyof typeof unitLevels
|
||||||
@@ -20,6 +20,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/add': {
|
'/ambulatory/encounter/add': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
@@ -34,6 +35,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/edit': {
|
'/ambulatory/encounter/[id]/edit': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
@@ -47,6 +49,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=status': {
|
'/ambulatory/encounter/[id]/process?menu=status': {
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
@@ -57,6 +60,18 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=rehab-medical-assessment': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=early-medical-assessment': {
|
'/ambulatory/encounter/[id]/process?menu=early-medical-assessment': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -67,8 +82,20 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=fkr': {
|
'/ambulatory/encounter/[id]/process?menu=early-nurse-assessment': {
|
||||||
|
'emp|doc': ['R'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=kfr': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
'emp|nur': ['R'],
|
'emp|nur': ['R'],
|
||||||
'emp|thr': ['R'],
|
'emp|thr': ['R'],
|
||||||
@@ -77,6 +104,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=education-assessment': {
|
'/ambulatory/encounter/[id]/process?menu=education-assessment': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -87,6 +115,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=general-consent': {
|
'/ambulatory/encounter/[id]/process?menu=general-consent': {
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
@@ -97,6 +126,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=patient-amb-note': {
|
'/ambulatory/encounter/[id]/process?menu=patient-amb-note': {
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
@@ -107,6 +137,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=prescription': {
|
'/ambulatory/encounter/[id]/process?menu=prescription': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -117,6 +148,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=device-order': {
|
'/ambulatory/encounter/[id]/process?menu=device-order': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -127,6 +159,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=radiology-order': {
|
'/ambulatory/encounter/[id]/process?menu=radiology-order': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -137,6 +170,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=cp-lab-order': {
|
'/ambulatory/encounter/[id]/process?menu=cp-lab-order': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -147,6 +181,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=micro-lab-order': {
|
'/ambulatory/encounter/[id]/process?menu=micro-lab-order': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -157,6 +192,7 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/encounter/[id]/process?menu=ap-lab-order': {
|
'/ambulatory/encounter/[id]/process?menu=ap-lab-order': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
@@ -167,6 +203,161 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|pha': ['R'],
|
'emp|pha': ['R'],
|
||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=inpatient-letter': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=reference-back': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=procedure-room-order': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=mcu-result': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=action-report': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=surgery-report': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=vaccine-data': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=consultation': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=control-letter': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R', 'U'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=screening': {
|
||||||
|
'emp|doc': ['R'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R', 'U'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=supporting-document': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=resume': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=amb-resume': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
|
},
|
||||||
|
'/ambulatory/encounter/[id]/process?menu=price-list': {
|
||||||
|
'emp|doc': ['R', 'U'],
|
||||||
|
'emp|nur': ['R'],
|
||||||
|
'emp|thr': ['R'],
|
||||||
|
'emp|miw': ['R'],
|
||||||
|
'emp|nut': ['R'],
|
||||||
|
'emp|pha': ['R'],
|
||||||
|
'emp|lab': ['R'],
|
||||||
|
'emp|rad': ['R'],
|
||||||
|
'emp|scr': ['R'],
|
||||||
},
|
},
|
||||||
'/ambulatory/consulation': {
|
'/ambulatory/consulation': {
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Permission } from "~/models/role";
|
||||||
|
|
||||||
|
// Should we define the keys first?
|
||||||
|
// export type Keys = 'key1' | 'key2' | 'key3' | etc
|
||||||
|
|
||||||
|
export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||||
|
'/client/patient': {
|
||||||
|
'emp|reg': ['C','R','U','D'],
|
||||||
|
},
|
||||||
|
'/client/patient/add': {
|
||||||
|
'emp|reg': ['C','R','U','D'],
|
||||||
|
},
|
||||||
|
'/client/patient/{id}': {
|
||||||
|
'emp|reg': ['C','R','U','D'],
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import { getDetail as getDoctorDetail, getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
|
import { getDetail as getDoctorDetail, getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
|
||||||
import {
|
import {
|
||||||
create as createEncounter,
|
create as createEncounter,
|
||||||
|
createWithPatient,
|
||||||
getDetail as getEncounterDetail,
|
getDetail as getEncounterDetail,
|
||||||
update as updateEncounter,
|
update as updateEncounter,
|
||||||
} from '~/services/encounter.service'
|
} from '~/services/encounter.service'
|
||||||
@@ -44,7 +45,8 @@ import {
|
|||||||
export function useEncounterEntry(props: {
|
export function useEncounterEntry(props: {
|
||||||
id: number
|
id: number
|
||||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
||||||
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
subclassCode?: 'regular' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||||
|
specialist_Code?: 'regular' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||||
}) {
|
}) {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@@ -207,7 +209,7 @@ export function useEncounterEntry(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getDoctorInfo(value: string) {
|
async function getDoctorInfo(value: string) {
|
||||||
const resp = await getDoctorDetail(value, { includes: 'unit,specialist,subspecialist' })
|
const resp = await getDoctorDetail(value, { includes: 'specialist,subspecialist' })
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
selectedDoctor.value = resp.body.data
|
selectedDoctor.value = resp.body.data
|
||||||
}
|
}
|
||||||
@@ -294,24 +296,44 @@ export function useEncounterEntry(props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFetchDoctors(subSpecialistId: string | null = null) {
|
async function handleFetchDoctors(specialist_code?: string, subSpecialist_code?: string) {
|
||||||
try {
|
try {
|
||||||
const filterParams: any = { 'page-size': 100, includes: 'employee-Person,unit,specialist,subspecialist' }
|
const filterParams: Record<string, any> = {
|
||||||
|
'page-size': 100,
|
||||||
if (!subSpecialistId) {
|
includes: 'employee-Person,specialist,subspecialist'
|
||||||
doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSub = getIsSubspecialist(subSpecialistId, specialistsTree.value)
|
// rehab is special
|
||||||
|
// if (unit) {
|
||||||
if (isSub) {
|
// if (unit == 'rehab') {
|
||||||
filterParams['subspecialist-id'] = subSpecialistId
|
// filterParams['unit-code'] = 'rehab'
|
||||||
} else {
|
// } else {
|
||||||
filterParams['specialist-id'] = subSpecialistId
|
// filterParams['unit-code'] = 'rehab'
|
||||||
|
// filterParams['unit-code-opt'] = 'ne'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (specialist_code) {
|
||||||
|
if (specialist_code == 'rehab') {
|
||||||
|
filterParams['specialist-code'] = 'rehab'
|
||||||
|
} else {
|
||||||
|
filterParams['specialist-code'] = 'rehab'
|
||||||
|
filterParams['specialist-code-opt'] = 'ne'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subSpecialist_code) {
|
||||||
|
filterParams['subspecialist-code'] = subSpecialist_code
|
||||||
}
|
}
|
||||||
|
|
||||||
doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
||||||
|
|
||||||
|
// const isSub = getIsSubspecialist(subSpecialistId, specialistsTree.value)
|
||||||
|
|
||||||
|
// if (isSub) {
|
||||||
|
// filterParams['subspecialist-id'] = subSpecialistId
|
||||||
|
// } else {
|
||||||
|
// filterParams['specialist-id'] = subSpecialistId
|
||||||
|
// }
|
||||||
|
|
||||||
|
// doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching doctors:', error)
|
console.error('Error fetching doctors:', error)
|
||||||
doctorsList.value = []
|
doctorsList.value = []
|
||||||
@@ -332,7 +354,7 @@ export function useEncounterEntry(props: {
|
|||||||
value: item.toString(),
|
value: item.toString(),
|
||||||
label: participantGroups[item],
|
label: participantGroups[item],
|
||||||
})) as any
|
})) as any
|
||||||
await handleFetchDoctors()
|
await handleFetchDoctors(props.subclassCode)
|
||||||
await handleFetchSpecialists()
|
await handleFetchSpecialists()
|
||||||
if (route.query) {
|
if (route.query) {
|
||||||
formObjects.value = { ...formObjects.value }
|
formObjects.value = { ...formObjects.value }
|
||||||
@@ -489,13 +511,18 @@ export function useEncounterEntry(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveEncounter(formValues: any) {
|
async function handleSaveEncounter(formValues: any) {
|
||||||
if (!selectedPatient.value || !selectedPatientObject.value) {
|
let enccounterRef = formValues
|
||||||
toast({
|
if (!formValues.encounter) {
|
||||||
title: 'Gagal',
|
if (!selectedPatient.value || !selectedPatientObject.value) {
|
||||||
description: 'Pasien harus dipilih terlebih dahulu',
|
toast({
|
||||||
variant: 'destructive',
|
title: 'Gagal',
|
||||||
})
|
description: 'Pasien harus dipilih terlebih dahulu',
|
||||||
return
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
enccounterRef = {...formValues.encounter}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -509,24 +536,24 @@ export function useEncounterEntry(props: {
|
|||||||
return date.toISOString()
|
return date.toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { specialist_id, subspecialist_id } = getSpecialistIdsFromCode(formValues.subSpecialistId || '')
|
const { specialist_id, subspecialist_id } = getSpecialistIdsFromCode(enccounterRef.subSpecialistId || '')
|
||||||
|
|
||||||
const patientId = formValues.patient_id || selectedPatientObject.value?.id || Number(selectedPatient.value)
|
const patientId = enccounterRef.patient_id || selectedPatientObject.value?.id || Number(selectedPatient.value)
|
||||||
|
|
||||||
const registeredAtValue = formValues.registeredAt || formValues.registerDate || ''
|
const registeredAtValue = enccounterRef.registeredAt || enccounterRef.registerDate || ''
|
||||||
const visitDateValue = formValues.visitDate || formValues.registeredAt || formValues.registerDate || ''
|
const visitDateValue = enccounterRef.visitDate || enccounterRef.registeredAt || enccounterRef.registerDate || ''
|
||||||
const memberNumber = formValues.member_number ?? formValues.cardNumber ?? formValues.memberNumber ?? null
|
const memberNumber = enccounterRef.member_number ?? enccounterRef.cardNumber ?? enccounterRef.memberNumber ?? null
|
||||||
const refNumber = formValues.ref_number ?? formValues.sepNumber ?? formValues.refNumber ?? null
|
const refNumber = enccounterRef.ref_number ?? enccounterRef.sepNumber ?? enccounterRef.refNumber ?? null
|
||||||
sepFile.value = formValues.sepFile || null
|
sepFile.value = enccounterRef.sepFile || null
|
||||||
sippFile.value = formValues.sippFile || null
|
sippFile.value = enccounterRef.sippFile || null
|
||||||
|
|
||||||
let paymentMethodCode = formValues.paymentMethod_code ?? null
|
let paymentMethodCode = enccounterRef.paymentMethod_code ?? null
|
||||||
if (!isUsePaymentNew && !paymentMethodCode) {
|
if (!isUsePaymentNew && !paymentMethodCode) {
|
||||||
if (formValues.paymentType === 'jkn' || formValues.paymentType === 'jkmm') {
|
if (enccounterRef.paymentType === 'jkn' || enccounterRef.paymentType === 'jkmm') {
|
||||||
paymentMethodCode = 'insurance'
|
paymentMethodCode = 'insurance'
|
||||||
} else if (formValues.paymentType === 'spm') {
|
} else if (enccounterRef.paymentType === 'spm') {
|
||||||
paymentMethodCode = 'cash'
|
paymentMethodCode = 'cash'
|
||||||
} else if (formValues.paymentType === 'pks') {
|
} else if (enccounterRef.paymentType === 'pks') {
|
||||||
paymentMethodCode = 'membership'
|
paymentMethodCode = 'membership'
|
||||||
} else {
|
} else {
|
||||||
paymentMethodCode = 'cash'
|
paymentMethodCode = 'cash'
|
||||||
@@ -535,19 +562,20 @@ export function useEncounterEntry(props: {
|
|||||||
|
|
||||||
const payload: any = {
|
const payload: any = {
|
||||||
patient_id: patientId,
|
patient_id: patientId,
|
||||||
appointment_doctor_code: formValues.doctor_code || null,
|
appointment_doctor_code: enccounterRef.doctor_code || null,
|
||||||
class_code: props.classCode || '',
|
class_code: props.classCode || '',
|
||||||
subClass_code: props.subClassCode || '',
|
subClass_code: props.subclassCode || '',
|
||||||
infra_id: formValues.infra_id ?? null,
|
specialist_code: selectedDoctor.value?.specialist_code || '',
|
||||||
unit_code: formValues.unitCode ?? userStore?.user?.unit_code ?? null,
|
infra_id: enccounterRef.infra_id ?? null,
|
||||||
refSource_name: formValues.refSource_name ?? 'RSSA',
|
unit_code: enccounterRef.unitCode ?? userStore?.user?.unit_code ?? null,
|
||||||
refTypeCode: formValues.paymentType === 'jkn' ? 'bpjs' : '',
|
refSource_name: enccounterRef.refSource_name ?? 'RSSA',
|
||||||
|
refTypeCode: enccounterRef.paymentType === 'jkn' ? 'bpjs' : '',
|
||||||
vclaimReference: vclaimReference.value ?? null,
|
vclaimReference: vclaimReference.value ?? null,
|
||||||
paymentType: formValues.paymentType,
|
paymentType: enccounterRef.paymentType,
|
||||||
registeredAt: formatDate(registeredAtValue),
|
registeredAt: formatDate(registeredAtValue),
|
||||||
visitDate: formatDate(visitDateValue),
|
visitDate: formatDate(visitDateValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.classCode !== 'inpatient') {
|
if (props.classCode !== 'inpatient') {
|
||||||
delete payload.infra_id
|
delete payload.infra_id
|
||||||
}
|
}
|
||||||
@@ -570,10 +598,10 @@ export function useEncounterEntry(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (paymentMethodCode === 'insurance') {
|
if (paymentMethodCode === 'insurance') {
|
||||||
payload.insuranceCompany_id = formValues.insuranceCompany_id ?? null
|
payload.insuranceCompany_id = enccounterRef.insuranceCompany_id ?? null
|
||||||
if (memberNumber) payload.member_number = memberNumber
|
if (memberNumber) payload.member_number = memberNumber
|
||||||
if (formValues.refTypeCode) payload.refTypeCode = formValues.refTypeCode
|
if (enccounterRef.refTypeCode) payload.refTypeCode = enccounterRef.refTypeCode
|
||||||
if (formValues.vclaimReference) payload.vclaimReference = formValues.vclaimReference
|
if (enccounterRef.vclaimReference) payload.vclaimReference = enccounterRef.vclaimReference
|
||||||
} else {
|
} else {
|
||||||
if (paymentMethodCode === 'membership' && memberNumber) {
|
if (paymentMethodCode === 'membership' && memberNumber) {
|
||||||
payload.member_number = memberNumber
|
payload.member_number = memberNumber
|
||||||
@@ -589,8 +617,18 @@ export function useEncounterEntry(props: {
|
|||||||
if (isEditMode.value) {
|
if (isEditMode.value) {
|
||||||
result = await updateEncounter(props.id, payload)
|
result = await updateEncounter(props.id, payload)
|
||||||
} else {
|
} else {
|
||||||
console.log('💾 [ADD MODE] Sending POST request:', { payload })
|
// console.log('💾 [ADD MODE] Sending POST request:', { payload })
|
||||||
result = await createEncounter(payload)
|
if (!formValues.encounter) {
|
||||||
|
result = await createEncounter(payload)
|
||||||
|
} else {
|
||||||
|
if (formValues.patient?.person?.birthDate) {
|
||||||
|
formValues.patient.person.birthDate += 'T00:00:00.000Z'
|
||||||
|
}
|
||||||
|
result = await createWithPatient({
|
||||||
|
encounter: payload,
|
||||||
|
patient: formValues.patient,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|||||||
@@ -30,13 +30,12 @@ export interface EncounterListData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const StatusAsync = defineAsyncComponent(() => import('~/components/content/encounter/status.vue'))
|
const StatusAsync = defineAsyncComponent(() => import('~/components/content/encounter/status.vue'))
|
||||||
const AssesmentFunctionListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
const EarlyMedicalRehabAssessmentListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||||
const EarlyMedicalAssesmentListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
const EarlyMedicalAssesmentListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||||
const EarlyMedicalRehabListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
const initialNursesAssessmentAsync = defineAsyncComponent(() => import('~/components/content/initial-nursing/entry.vue'))
|
||||||
|
const AssesmentFunctionListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||||
const ChemoProtocolListAsync = defineAsyncComponent(() => import('~/components/app/chemotherapy/list.protocol.vue'))
|
const ChemoProtocolListAsync = defineAsyncComponent(() => import('~/components/app/chemotherapy/list.protocol.vue'))
|
||||||
const ChemoMedicineProtocolListAsync = defineAsyncComponent(
|
const ChemoMedicineProtocolListAsync = defineAsyncComponent(() => import('~/components/app/chemotherapy/list.medicine.vue'))
|
||||||
() => import('~/components/app/chemotherapy/list.medicine.vue'),
|
|
||||||
)
|
|
||||||
const DeviceOrderAsync = defineAsyncComponent(() => import('~/components/content/device-order/main.vue'))
|
const DeviceOrderAsync = defineAsyncComponent(() => import('~/components/content/device-order/main.vue'))
|
||||||
const PrescriptionAsync = defineAsyncComponent(() => import('~/components/content/prescription/main.vue'))
|
const PrescriptionAsync = defineAsyncComponent(() => import('~/components/content/prescription/main.vue'))
|
||||||
const CpLabOrderAsync = defineAsyncComponent(() => import('~/components/content/cp-lab-order/main.vue'))
|
const CpLabOrderAsync = defineAsyncComponent(() => import('~/components/content/cp-lab-order/main.vue'))
|
||||||
@@ -54,7 +53,6 @@ const KfrListAsync = defineAsyncComponent(() => import('~/components/content/kfr
|
|||||||
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/list.vue'))
|
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/list.vue'))
|
||||||
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/list.vue'))
|
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/list.vue'))
|
||||||
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/list.vue'))
|
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/list.vue'))
|
||||||
const InitialNursingStudyAsync = defineAsyncComponent(() => import('~/components/content/initial-nursing/entry.vue'))
|
|
||||||
const AssessmentEducationEntryAsync = defineAsyncComponent(
|
const AssessmentEducationEntryAsync = defineAsyncComponent(
|
||||||
() => import('~/components/content/assessment-education/entry.vue'),
|
() => import('~/components/content/assessment-education/entry.vue'),
|
||||||
)
|
)
|
||||||
@@ -68,12 +66,6 @@ const defaultKeys: Record<string, any> = {
|
|||||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||||
unit: 'all',
|
unit: 'all',
|
||||||
},
|
},
|
||||||
// earlyNurseryAssessment: {
|
|
||||||
// id: 'early-nursery-assessment',
|
|
||||||
// title: 'Pengkajian Awal Keperawatan',
|
|
||||||
// classCode: ['ambulatory', 'emergency', 'inpatient'],
|
|
||||||
// unit: 'all',
|
|
||||||
// },
|
|
||||||
earlyMedicalAssessment: {
|
earlyMedicalAssessment: {
|
||||||
id: 'early-medical-assessment',
|
id: 'early-medical-assessment',
|
||||||
title: 'Pengkajian Awal Medis',
|
title: 'Pengkajian Awal Medis',
|
||||||
@@ -87,28 +79,15 @@ const defaultKeys: Record<string, any> = {
|
|||||||
unit: 'rehab',
|
unit: 'rehab',
|
||||||
afterId: 'early-medical-assessment',
|
afterId: 'early-medical-assessment',
|
||||||
},
|
},
|
||||||
functionAssessment: {
|
initialNursesAssessment: {
|
||||||
id: 'function-assessment',
|
id: 'early-nurse-assessment',
|
||||||
title: 'Asesmen Fungsi',
|
|
||||||
classCode: ['ambulatory'],
|
|
||||||
unit: 'rehab',
|
|
||||||
afterId: 'rehab-medical-assessment',
|
|
||||||
},
|
|
||||||
// therapyProtocol: {
|
|
||||||
// id: 'therapy-protocol',
|
|
||||||
// classCode: ['ambulatory'],
|
|
||||||
// title: 'Protokol Terapi',
|
|
||||||
// unit: 'rehab',
|
|
||||||
// afterId: 'function-assessment',
|
|
||||||
initialNursingStudy: {
|
|
||||||
id: 'initial-nursing-study',
|
|
||||||
title: 'Kajian Awal Keperawatan',
|
title: 'Kajian Awal Keperawatan',
|
||||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||||
unit: 'all',
|
unit: 'all',
|
||||||
},
|
},
|
||||||
fkr: {
|
kfr: {
|
||||||
id: 'fkr',
|
id: 'kfr',
|
||||||
title: 'FKR',
|
title: 'KFR',
|
||||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||||
unit: 'all',
|
unit: 'all',
|
||||||
},
|
},
|
||||||
@@ -234,12 +213,6 @@ const defaultKeys: Record<string, any> = {
|
|||||||
classCode: ['ambulatory', 'emergency'],
|
classCode: ['ambulatory', 'emergency'],
|
||||||
unit: 'all',
|
unit: 'all',
|
||||||
},
|
},
|
||||||
kfr: {
|
|
||||||
id: 'kfr',
|
|
||||||
title: 'KFR',
|
|
||||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
|
||||||
unit: 'all',
|
|
||||||
},
|
|
||||||
refBack: {
|
refBack: {
|
||||||
id: 'reference-back',
|
id: 'reference-back',
|
||||||
title: 'PRB',
|
title: 'PRB',
|
||||||
@@ -276,7 +249,7 @@ const defaultKeys: Record<string, any> = {
|
|||||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||||
unit: 'all',
|
unit: 'all',
|
||||||
},
|
},
|
||||||
// initialNursingStudy: {
|
// initialNursesAssessment: {
|
||||||
// id: 'initial-nursing-study',
|
// id: 'initial-nursing-study',
|
||||||
// title: 'Kajian Awal Keperawatan',
|
// title: 'Kajian Awal Keperawatan',
|
||||||
// classCode: ['ambulatory', 'emergency', 'inpatient'],
|
// classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||||
@@ -335,7 +308,7 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentKeys?.earlyMedicalRehabAssessment) {
|
if (currentKeys?.earlyMedicalRehabAssessment) {
|
||||||
currentKeys.earlyMedicalRehabAssessment['component'] = EarlyMedicalRehabListAsync
|
currentKeys.earlyMedicalRehabAssessment['component'] = EarlyMedicalRehabAssessmentListAsync
|
||||||
currentKeys.earlyMedicalRehabAssessment['props'] = {
|
currentKeys.earlyMedicalRehabAssessment['props'] = {
|
||||||
encounter: data?.encounter,
|
encounter: data?.encounter,
|
||||||
type: 'early-rehab',
|
type: 'early-rehab',
|
||||||
@@ -464,12 +437,10 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
|||||||
currentKeys.priceList['component'] = null
|
currentKeys.priceList['component'] = null
|
||||||
currentKeys.priceList['props'] = { encounter_id: id }
|
currentKeys.priceList['props'] = { encounter_id: id }
|
||||||
}
|
}
|
||||||
|
if (currentKeys?.initialNursesAssessment) {
|
||||||
if (currentKeys?.initialNursingStudy) {
|
currentKeys.initialNursesAssessment['component'] = initialNursesAssessmentAsync
|
||||||
currentKeys.initialNursingStudy['component'] = InitialNursingStudyAsync
|
currentKeys.initialNursesAssessment['props'] = { encounter: data?.encounter }
|
||||||
currentKeys.initialNursingStudy['props'] = { encounter: data?.encounter }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentKeys?.actionReport) {
|
if (currentKeys?.actionReport) {
|
||||||
currentKeys.actionReport['component'] = ActionReportEntryAsync
|
currentKeys.actionReport['component'] = ActionReportEntryAsync
|
||||||
currentKeys.actionReport['props'] = {
|
currentKeys.actionReport['props'] = {
|
||||||
@@ -533,6 +504,9 @@ export function mapResponseToEncounter(result: any): any {
|
|||||||
? result.visitDate
|
? result.visitDate
|
||||||
: result.registeredAt || result.patient?.registeredAt || null,
|
: result.registeredAt || result.patient?.registeredAt || null,
|
||||||
adm_employee_id: result.adm_employee_id || 0,
|
adm_employee_id: result.adm_employee_id || 0,
|
||||||
|
adm_employee: result.adm_employee || null,
|
||||||
|
responsible_nurse_id: result.responsible_nurse_id || null,
|
||||||
|
responsible_nurse: result.responsible_nurse || null,
|
||||||
appointment_doctor_id: result.appointment_doctor_id || null,
|
appointment_doctor_id: result.appointment_doctor_id || null,
|
||||||
responsible_doctor_id: result.responsible_doctor_id || null,
|
responsible_doctor_id: result.responsible_doctor_id || null,
|
||||||
appointment_doctor: result.appointment_doctor || null,
|
appointment_doctor: result.appointment_doctor || null,
|
||||||
@@ -595,7 +569,7 @@ export function getMenuItems(id: string | number, props: any, user: any, data: E
|
|||||||
const currentUnitItems: any = currentListItems[`${unitCode}`]
|
const currentUnitItems: any = currentListItems[`${unitCode}`]
|
||||||
if (!currentUnitItems) return []
|
if (!currentUnitItems) return []
|
||||||
let menus = []
|
let menus = []
|
||||||
if (currentUnitItems.roles && currentUnitItems.roles?.includes(user.activeRole)) {
|
if (currentUnitItems.roles && currentUnitItems.roles && user.activeRole in currentUnitItems.roles) {
|
||||||
menus = [...currentUnitItems.items]
|
menus = [...currentUnitItems.items]
|
||||||
} else {
|
} else {
|
||||||
menus = unitCode !== 'all' && currentUnitItems?.items ? [...currentUnitItems.items] : [...currentUnitItems]
|
menus = unitCode !== 'all' && currentUnitItems?.items ? [...currentUnitItems.items] : [...currentUnitItems]
|
||||||
|
|||||||
+63
-41
@@ -74,18 +74,18 @@ export const bigTimeUnitCodes: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const dischargeMethodCodes: Record<string, string> = {
|
export const dischargeMethodCodes: Record<string, string> = {
|
||||||
home: "Pulang",
|
home: 'Pulang',
|
||||||
"home-request": "Pulang Atas Permintaan Sendiri",
|
'home-request': 'Pulang Atas Permintaan Sendiri',
|
||||||
"consul-back": "Konsultasi Balik / Lanjutan",
|
'consul-back': 'Konsultasi Balik / Lanjutan',
|
||||||
"consul-poly": "Konsultasi Poliklinik Lain",
|
'consul-poly': 'Konsultasi Poliklinik Lain',
|
||||||
"consul-executive": "Konsultasi Antar Dokter Eksekutif",
|
'consul-executive': 'Konsultasi Antar Dokter Eksekutif',
|
||||||
"consul-ch-day": "Konsultasi Hari Lain",
|
'consul-ch-day': 'Konsultasi Hari Lain',
|
||||||
emergency: "Rujuk IGD",
|
emergency: 'Rujuk IGD',
|
||||||
"emergency-covid": "Rujuk IGD Covid",
|
'emergency-covid': 'Rujuk IGD Covid',
|
||||||
inpatient: "Rujuk Rawat Inap",
|
inpatient: 'Rujuk Rawat Inap',
|
||||||
external: "Rujuk Faskes Lain",
|
external: 'Rujuk Faskes Lain',
|
||||||
death: "Meninggal",
|
death: 'Meninggal',
|
||||||
"death-on-arrival": "Meninggal Saat Tiba"
|
'death-on-arrival': 'Meninggal Saat Tiba',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const genderCodes: Record<string, string> = {
|
export const genderCodes: Record<string, string> = {
|
||||||
@@ -387,13 +387,13 @@ export const medicalActionTypeCode: Record<string, string> = {
|
|||||||
export type medicalActionTypeCodeKey = keyof typeof medicalActionTypeCode
|
export type medicalActionTypeCodeKey = keyof typeof medicalActionTypeCode
|
||||||
|
|
||||||
export const encounterDocTypeCode: Record<string, string> = {
|
export const encounterDocTypeCode: Record<string, string> = {
|
||||||
"person-resident-number": 'person-resident-number',
|
'person-resident-number': 'person-resident-number',
|
||||||
"person-driving-license": 'person-driving-license',
|
'person-driving-license': 'person-driving-license',
|
||||||
"person-passport": 'person-passport',
|
'person-passport': 'person-passport',
|
||||||
"person-family-card": 'person-family-card',
|
'person-family-card': 'person-family-card',
|
||||||
"mcu-item-result": 'mcu-item-result',
|
'mcu-item-result': 'mcu-item-result',
|
||||||
"vclaim-sep": 'vclaim-sep',
|
'vclaim-sep': 'vclaim-sep',
|
||||||
"vclaim-sipp": 'vclaim-sipp',
|
'vclaim-sipp': 'vclaim-sipp',
|
||||||
} as const
|
} as const
|
||||||
export type encounterDocTypeCodeKey = keyof typeof encounterDocTypeCode
|
export type encounterDocTypeCodeKey = keyof typeof encounterDocTypeCode
|
||||||
export const encounterDocOpt: { label: string; value: encounterDocTypeCodeKey }[] = [
|
export const encounterDocOpt: { label: string; value: encounterDocTypeCodeKey }[] = [
|
||||||
@@ -406,20 +406,19 @@ export const encounterDocOpt: { label: string; value: encounterDocTypeCodeKey }[
|
|||||||
{ label: 'Klaim SIPP', value: 'vclaim-sipp' },
|
{ label: 'Klaim SIPP', value: 'vclaim-sipp' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
export const docTypeCode = {
|
export const docTypeCode = {
|
||||||
"encounter-patient": 'encounter-patient',
|
'encounter-patient': 'encounter-patient',
|
||||||
"encounter-support": 'encounter-support',
|
'encounter-support': 'encounter-support',
|
||||||
"encounter-other": 'encounter-other',
|
'encounter-other': 'encounter-other',
|
||||||
"vclaim-sep": 'vclaim-sep',
|
'vclaim-sep': 'vclaim-sep',
|
||||||
"vclaim-sipp": 'vclaim-sipp',
|
'vclaim-sipp': 'vclaim-sipp',
|
||||||
} as const
|
} as const
|
||||||
export const docTypeLabel = {
|
export const docTypeLabel = {
|
||||||
"encounter-patient": 'Data Pasien',
|
'encounter-patient': 'Data Pasien',
|
||||||
"encounter-support": 'Data Penunjang',
|
'encounter-support': 'Data Penunjang',
|
||||||
"encounter-other": 'Lain - Lain',
|
'encounter-other': 'Lain - Lain',
|
||||||
"vclaim-sep": 'SEP',
|
'vclaim-sep': 'SEP',
|
||||||
"vclaim-sipp": 'SIPP',
|
'vclaim-sipp': 'SIPP',
|
||||||
} as const
|
} as const
|
||||||
export type docTypeCodeKey = keyof typeof docTypeCode
|
export type docTypeCodeKey = keyof typeof docTypeCode
|
||||||
export const supportingDocOpt = [
|
export const supportingDocOpt = [
|
||||||
@@ -428,8 +427,7 @@ export const supportingDocOpt = [
|
|||||||
{ label: 'Lain - Lain', value: 'encounter-other' },
|
{ label: 'Lain - Lain', value: 'encounter-other' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export type SurgeryType = 'kecil' | 'sedang' | 'besar' | 'khusus'
|
||||||
export type SurgeryType = "kecil" | "sedang" | "besar" | "khusus"
|
|
||||||
export const SurgeryTypeOptList: { label: string; value: SurgeryType }[] = [
|
export const SurgeryTypeOptList: { label: string; value: SurgeryType }[] = [
|
||||||
{ label: 'Kecil', value: 'kecil' },
|
{ label: 'Kecil', value: 'kecil' },
|
||||||
{ label: 'Sedang', value: 'sedang' },
|
{ label: 'Sedang', value: 'sedang' },
|
||||||
@@ -437,14 +435,14 @@ export const SurgeryTypeOptList: { label: string; value: SurgeryType }[] = [
|
|||||||
{ label: 'Khusus', value: 'khusus' },
|
{ label: 'Khusus', value: 'khusus' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type BillingCodeType = "general" | "regional" | "local"
|
export type BillingCodeType = 'general' | 'regional' | 'local'
|
||||||
export const BillingCodeTypeOptList: { label: string; value: BillingCodeType }[] = [
|
export const BillingCodeTypeOptList: { label: string; value: BillingCodeType }[] = [
|
||||||
{ label: 'General', value: 'general' },
|
{ label: 'General', value: 'general' },
|
||||||
{ label: 'Regional', value: 'regional' },
|
{ label: 'Regional', value: 'regional' },
|
||||||
{ label: 'Local', value: 'local' },
|
{ label: 'Local', value: 'local' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type SurgerySystemType = "cito" | "urgent" | "efektif" | "khusus"
|
export type SurgerySystemType = 'cito' | 'urgent' | 'efektif' | 'khusus'
|
||||||
export const SurgerySystemTypeOptList: { label: string; value: SurgerySystemType }[] = [
|
export const SurgerySystemTypeOptList: { label: string; value: SurgerySystemType }[] = [
|
||||||
{ label: 'Cito', value: 'cito' },
|
{ label: 'Cito', value: 'cito' },
|
||||||
{ label: 'Urgent', value: 'urgent' },
|
{ label: 'Urgent', value: 'urgent' },
|
||||||
@@ -452,7 +450,7 @@ export const SurgerySystemTypeOptList: { label: string; value: SurgerySystemType
|
|||||||
{ label: 'Khusus', value: 'khusus' },
|
{ label: 'Khusus', value: 'khusus' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type DissectionType = "bersih" | "bersih terkontaminasi" | "terkontaminasi kotor" | "kotor"
|
export type DissectionType = 'bersih' | 'bersih terkontaminasi' | 'terkontaminasi kotor' | 'kotor'
|
||||||
export const DissectionTypeOptList: { label: string; value: DissectionType }[] = [
|
export const DissectionTypeOptList: { label: string; value: DissectionType }[] = [
|
||||||
{ label: 'Bersih', value: 'bersih' },
|
{ label: 'Bersih', value: 'bersih' },
|
||||||
{ label: 'Bersih terkontaminasi', value: 'bersih terkontaminasi' },
|
{ label: 'Bersih terkontaminasi', value: 'bersih terkontaminasi' },
|
||||||
@@ -460,19 +458,25 @@ export const DissectionTypeOptList: { label: string; value: DissectionType }[] =
|
|||||||
{ label: 'Kotor', value: 'kotor' },
|
{ label: 'Kotor', value: 'kotor' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type SurgeryOrderType = "satu" | "ulangan"
|
export type SurgeryOrderType = 'satu' | 'ulangan'
|
||||||
export const SurgeryOrderTypeOptList: { label: string; value: SurgeryOrderType }[] = [
|
export const SurgeryOrderTypeOptList: { label: string; value: SurgeryOrderType }[] = [
|
||||||
{ label: 'Satu', value: 'satu' },
|
{ label: 'Satu', value: 'satu' },
|
||||||
{ label: 'Ulangan', value: 'ulangan' },
|
{ label: 'Ulangan', value: 'ulangan' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type BirthDescriptionType = "lahir hidup" | "lahir mati"
|
export type BirthDescriptionType = 'lahir hidup' | 'lahir mati'
|
||||||
export const BirthDescriptionTypeOptList: { label: string; value: BirthDescriptionType }[] = [
|
export const BirthDescriptionTypeOptList: { label: string; value: BirthDescriptionType }[] = [
|
||||||
{ label: 'Lahir Hidup', value: 'lahir hidup' },
|
{ label: 'Lahir Hidup', value: 'lahir hidup' },
|
||||||
{ label: 'Lahir Mati', value: 'lahir mati' },
|
{ label: 'Lahir Mati', value: 'lahir mati' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type BirthPlaceDescriptionType = "rssa" | "bidan luar" | "dokter luar" | "dukun bayi" | "puskesmas" | "paramedis luar"
|
export type BirthPlaceDescriptionType =
|
||||||
|
| 'rssa'
|
||||||
|
| 'bidan luar'
|
||||||
|
| 'dokter luar'
|
||||||
|
| 'dukun bayi'
|
||||||
|
| 'puskesmas'
|
||||||
|
| 'paramedis luar'
|
||||||
export const BirthPlaceDescriptionTypeOptList: { label: string; value: BirthPlaceDescriptionType }[] = [
|
export const BirthPlaceDescriptionTypeOptList: { label: string; value: BirthPlaceDescriptionType }[] = [
|
||||||
{ label: 'RSSA', value: 'rssa' },
|
{ label: 'RSSA', value: 'rssa' },
|
||||||
{ label: 'Bidan luar', value: 'bidan luar' },
|
{ label: 'Bidan luar', value: 'bidan luar' },
|
||||||
@@ -482,7 +486,7 @@ export const BirthPlaceDescriptionTypeOptList: { label: string; value: BirthPlac
|
|||||||
{ label: 'Paramedis luar', value: 'paramedis luar' },
|
{ label: 'Paramedis luar', value: 'paramedis luar' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type SpecimenType = "pa" | "mikrobiologi" | "laborat" | "tidak perlu"
|
export type SpecimenType = 'pa' | 'mikrobiologi' | 'laborat' | 'tidak perlu'
|
||||||
export const SpecimenTypeOptList: { label: string; value: SpecimenType }[] = [
|
export const SpecimenTypeOptList: { label: string; value: SpecimenType }[] = [
|
||||||
{ label: 'PA', value: 'pa' },
|
{ label: 'PA', value: 'pa' },
|
||||||
{ label: 'Mikrobiologi', value: 'mikrobiologi' },
|
{ label: 'Mikrobiologi', value: 'mikrobiologi' },
|
||||||
@@ -490,7 +494,15 @@ export const SpecimenTypeOptList: { label: string; value: SpecimenType }[] = [
|
|||||||
{ label: 'Tidak perlu', value: 'tidak perlu' },
|
{ label: 'Tidak perlu', value: 'tidak perlu' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export type PrbProgramType = "ashma" | "diabetes mellitus" | "hipertensi" | "penyakit jantung" | "ppok" | "schizopherenia" | "stroke" | "systemic lupus erythematosus"
|
export type PrbProgramType =
|
||||||
|
| 'ashma'
|
||||||
|
| 'diabetes mellitus'
|
||||||
|
| 'hipertensi'
|
||||||
|
| 'penyakit jantung'
|
||||||
|
| 'ppok'
|
||||||
|
| 'schizopherenia'
|
||||||
|
| 'stroke'
|
||||||
|
| 'systemic lupus erythematosus'
|
||||||
export const PrbProgramTypeOptList: { label: string; value: PrbProgramType }[] = [
|
export const PrbProgramTypeOptList: { label: string; value: PrbProgramType }[] = [
|
||||||
{ label: 'ASHMA', value: 'ashma' },
|
{ label: 'ASHMA', value: 'ashma' },
|
||||||
{ label: 'Diabetes Mellitus', value: 'diabetes mellitus' },
|
{ label: 'Diabetes Mellitus', value: 'diabetes mellitus' },
|
||||||
@@ -500,4 +512,14 @@ export const PrbProgramTypeOptList: { label: string; value: PrbProgramType }[] =
|
|||||||
{ label: 'Schizopherenia', value: 'schizopherenia' },
|
{ label: 'Schizopherenia', value: 'schizopherenia' },
|
||||||
{ label: 'Stroke', value: 'stroke' },
|
{ label: 'Stroke', value: 'stroke' },
|
||||||
{ label: 'Systemic Lupus Erythematosus', value: 'systemic lupus erythematosus' },
|
{ label: 'Systemic Lupus Erythematosus', value: 'systemic lupus erythematosus' },
|
||||||
]
|
]
|
||||||
|
export const disabilityCodes: Record<string, string> = {
|
||||||
|
daksa: 'Tuna Daksa',
|
||||||
|
netra: 'Tuna Netra',
|
||||||
|
rungu: 'Tuna Rungu',
|
||||||
|
wicara: 'Tuna Wicara',
|
||||||
|
rungu_wicara: 'Tuna Rungu-Wicara',
|
||||||
|
grahita: 'Tuna Grahita',
|
||||||
|
laras: 'Tuna Laras',
|
||||||
|
other: 'Lainnya',
|
||||||
|
}
|
||||||
+15
-5
@@ -1,16 +1,26 @@
|
|||||||
import { medicalRoles, respPosCode } from '~/const/common/role'
|
import type { UnitLevel, ServicePositionCode } from '~/const/common/role'
|
||||||
|
import { medicalRoles, infraPositions } from '~/const/common/role'
|
||||||
|
|
||||||
export function getServicePosition(role?: string): string {
|
export function getServicePosition(role?: string): ServicePositionCode {
|
||||||
if(!role) {
|
if(!role) {
|
||||||
return 'none'
|
return 'none'
|
||||||
}
|
}
|
||||||
if (medicalRoles.includes(role)) {
|
if (role in medicalRoles) {
|
||||||
return 'medical'
|
return 'med'
|
||||||
} else if (role === 'emp|reg') {
|
} else if (role === 'emp|reg') {
|
||||||
return 'registration'
|
return 'reg'
|
||||||
} else if (role.includes('|resp')) {
|
} else if (role.includes('|resp')) {
|
||||||
return 'verificator'
|
return 'verificator'
|
||||||
} else {
|
} else {
|
||||||
return 'none'
|
return 'none'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function genSpecHeadCode(unit_level: UnitLevel, unit_code: string): string {
|
||||||
|
return `${unit_level}|${unit_code}|${infraPositions.head}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genUnitRespCode(unit_level: UnitLevel, unit_code: string): string {
|
||||||
|
return `${unit_level}|${unit_code}|${infraPositions.resp}`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
export default defineNuxtRouteMiddleware((to) => {
|
import { useKeycloak } from "~/composables/useKeycloack"
|
||||||
|
|
||||||
|
export default defineNuxtRouteMiddleware(async (to) => {
|
||||||
if (to.meta.public) return
|
if (to.meta.public) return
|
||||||
|
// if (!to.meta?.requiresAuth) return;
|
||||||
|
|
||||||
const { $pinia } = useNuxtApp()
|
const { $pinia } = useNuxtApp()
|
||||||
|
|
||||||
|
const { initKeycloak, isAuthenticated, getResponse} = useKeycloak(); // global composable
|
||||||
|
await initKeycloak("check-sso");
|
||||||
|
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
const userStore = useUserStore($pinia)
|
const userStore = useUserStore($pinia)
|
||||||
if (!userStore.isAuthenticated) {
|
if (!userStore.isAuthenticated && !isAuthenticated.value) {
|
||||||
return navigateTo('/auth/login')
|
return navigateTo('/auth/login')
|
||||||
|
} else {
|
||||||
|
if (isAuthenticated.value) {
|
||||||
|
await getResponse()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
+16
-1
@@ -5,6 +5,10 @@ import type { EncounterDocument } from "./encounter-document"
|
|||||||
import type { InternalReference } from "./internal-reference"
|
import type { InternalReference } from "./internal-reference"
|
||||||
import type { Nurse } from "./nurse"
|
import type { Nurse } from "./nurse"
|
||||||
import { type Patient, genPatient } from "./patient"
|
import { type Patient, genPatient } from "./patient"
|
||||||
|
import type { Person } from "./person"
|
||||||
|
import type { PersonAddress } from "./person-address"
|
||||||
|
import type { PersonContact } from "./person-contact"
|
||||||
|
import type { PersonRelative } from "./person-relative"
|
||||||
import type { Specialist } from "./specialist"
|
import type { Specialist } from "./specialist"
|
||||||
import type { Subspecialist } from "./subspecialist"
|
import type { Subspecialist } from "./subspecialist"
|
||||||
import { genUnit, type Unit } from "./unit"
|
import { genUnit, type Unit } from "./unit"
|
||||||
@@ -45,6 +49,16 @@ export interface Encounter {
|
|||||||
encounterDocuments: EncounterDocument[]
|
encounterDocuments: EncounterDocument[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateDtoWithPatient {
|
||||||
|
encounter: Encounter
|
||||||
|
patient: {
|
||||||
|
person: Person
|
||||||
|
personAddresses: PersonAddress[]
|
||||||
|
personContacts: PersonContact[]
|
||||||
|
personRelatives: PersonRelative[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function genEncounter(): Encounter {
|
export function genEncounter(): Encounter {
|
||||||
return {
|
return {
|
||||||
id: 0,
|
id: 0,
|
||||||
@@ -52,7 +66,7 @@ export function genEncounter(): Encounter {
|
|||||||
patient: genPatient(),
|
patient: genPatient(),
|
||||||
registeredAt: '',
|
registeredAt: '',
|
||||||
class_code: '',
|
class_code: '',
|
||||||
unit_code: 0,
|
unit_code: '',
|
||||||
unit: genUnit(),
|
unit: genUnit(),
|
||||||
visitDate: '',
|
visitDate: '',
|
||||||
adm_employee_id: 0,
|
adm_employee_id: 0,
|
||||||
@@ -62,6 +76,7 @@ export function genEncounter(): Encounter {
|
|||||||
medicalDischargeEducation: '',
|
medicalDischargeEducation: '',
|
||||||
status_code: '',
|
status_code: '',
|
||||||
encounterDocuments: [],
|
encounterDocuments: [],
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Base, genBase } from "./_base"
|
import { type Base, genBase } from './_base'
|
||||||
|
|
||||||
export interface Ethnic extends Base {
|
export interface Ethnic extends Base {
|
||||||
code: string
|
code: string
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { type Base, genBase } from "./_base"
|
import { type Base, genBase } from './_base'
|
||||||
|
|
||||||
export interface Language extends Base {
|
export interface Language extends Base {
|
||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function genMcuSrc(): Language {
|
export function genLanguage(): Language {
|
||||||
return {
|
return {
|
||||||
...genBase(),
|
...genBase(),
|
||||||
code: '',
|
code: '',
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { type Base, genBase } from "./_base"
|
import { type Base, genBase } from "./_base"
|
||||||
|
import type { Employee } from "./employee"
|
||||||
|
|
||||||
export interface Nurse extends Base {
|
export interface Nurse extends Base {
|
||||||
employee_id: number
|
employee_id: number
|
||||||
|
employee?: Employee
|
||||||
ihs_number?: string
|
ihs_number?: string
|
||||||
unit_id: number
|
unit_id: number
|
||||||
infra_id: number
|
infra_id: number
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ useHead({
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const menu = computed(() => route.query.menu as string | undefined)
|
const menu = computed(() => route.query.menu as string | undefined)
|
||||||
const accessKey = computed(() => `/ambulatory/encounter/[id]/process` + (menu.value ? `?menu=${menu.value}` : ''))
|
const accessKey = computed(() => `/ambulatory/encounter/[id]/process` + (menu.value ? `?menu=${menu.value}` : ''))
|
||||||
const roleAccess: Record<string, Permission[]> = permissions[accessKey.value] || {}
|
const roleAccess = computed(() => permissions[accessKey.value] || {})
|
||||||
const hasAccess = getPageAccess(roleAccess, 'read') || true
|
const hasAccess = computed(() => getPageAccess(roleAccess.value, 'read'))
|
||||||
const canCreate = hasCreateAccess(roleAccess)
|
const canCreate = computed(() => getPageAccess(roleAccess.value, 'create'))
|
||||||
const canRead = hasReadAccess(roleAccess)
|
const canRead = computed(() => getPageAccess(roleAccess.value, 'read'))
|
||||||
const canUpdate = hasUpdateAccess(roleAccess)
|
const canUpdate = computed(() => getPageAccess(roleAccess.value, 'update'))
|
||||||
const canDelete = hasDeleteAccess(roleAccess)
|
const canDelete = computed(() => getPageAccess(roleAccess.value, 'delete'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { permissions } from '~/const/page-permission/ambulatory'
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import { usePageChecker } from "~/lib/page-checker"
|
import { usePageChecker } from "~/lib/page-checker"
|
||||||
|
import { getServicePosition } from '~/lib/roles'
|
||||||
|
|
||||||
// Pubs
|
// Pubs
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
@@ -28,19 +29,29 @@ useHead({
|
|||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/add'] || {}
|
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/add'] || {}
|
||||||
const hasAccess = getPageAccess(roleAccess, 'create')
|
const hasAccess = getPageAccess(roleAccess, 'create')
|
||||||
|
|
||||||
|
// TODO: Make a function for this
|
||||||
|
const { user } = useUserStore()
|
||||||
|
const servicePosition = user.user_contractPosition_code == 'emp' ? getServicePosition(user.activeRole) : null
|
||||||
|
const subClassCode = servicePosition == 'med' ?
|
||||||
|
// medic
|
||||||
|
(user.specialist_code == 'rehab' ? 'rehab' : 'regular') :
|
||||||
|
// non medic
|
||||||
|
(
|
||||||
|
servicePosition == 'reg' ?
|
||||||
|
(user.installation_code == 'rehab' ? 'rehab' : 'regular') :
|
||||||
|
undefined
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="hasAccess">
|
<Content v-if="hasAccess"
|
||||||
<Content
|
:id="0"
|
||||||
:id="0"
|
class-code="ambulatory"
|
||||||
class-code="ambulatory"
|
:subclass-code="subClassCode"
|
||||||
sub-class-code="reg"
|
form-type="add"
|
||||||
form-type="add"
|
/>
|
||||||
/>
|
<Error v-else
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
:status-code="403"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@@ -11,6 +11,7 @@ import Error from '~/components/pub/my-ui/error/error.vue'
|
|||||||
|
|
||||||
// Apps
|
// Apps
|
||||||
import Content from '~/components/content/encounter/list.vue'
|
import Content from '~/components/content/encounter/list.vue'
|
||||||
|
import { getServicePosition } from '~/lib/roles'
|
||||||
|
|
||||||
const { getRouteTitle, getPageAccess } = usePageChecker()
|
const { getRouteTitle, getPageAccess } = usePageChecker()
|
||||||
|
|
||||||
@@ -37,15 +38,27 @@ const canRemove = getPageAccess(roleAccess, 'delete')
|
|||||||
|
|
||||||
// User info
|
// User info
|
||||||
const { user } = useUserStore()
|
const { user } = useUserStore()
|
||||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
// TODO: Make a function for this
|
||||||
|
const servicePosition = user.user_contractPosition_code == 'emp' ? getServicePosition(user.activeRole) : null
|
||||||
|
const subClassCode = servicePosition == 'med' ?
|
||||||
|
// medic
|
||||||
|
(user.specialist_code == 'rehab' ? 'rehab' : 'regular') :
|
||||||
|
// non medic
|
||||||
|
(
|
||||||
|
servicePosition == 'reg' ?
|
||||||
|
(user.installation_code == 'rehab' ? 'rehab' : 'regular') :
|
||||||
|
undefined
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="hasAccess">
|
<div v-if="hasAccess">
|
||||||
|
{{ servicePosition }}--
|
||||||
|
{{ subClassCode }}--
|
||||||
<Content
|
<Content
|
||||||
class-code="ambulatory"
|
class-code="ambulatory"
|
||||||
:sub-class-code="subClassCode"
|
:subclass-code="subClassCode"
|
||||||
:can-create="canCreate"
|
:can-create="canCreate"
|
||||||
:can-delete="canRemove"
|
:can-delete="canRemove"
|
||||||
/>
|
/>
|
||||||
|
|||||||
+23
-13
@@ -1,23 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/chemoteraphy'
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
|
import Content from '~/components/content/encounter/list.vue'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
roles: ['room|resp'],
|
||||||
title: 'Daftar Kempterapi',
|
title: 'Daftar Kunjungan',
|
||||||
contentFrame: 'cf-full-width',
|
contentFrame: 'cf-full-width',
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
// Preps role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/chemotherapy'] || {}
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
@@ -28,13 +24,27 @@ if (!hasAccess) {
|
|||||||
|
|
||||||
// Define permission-based computed properties
|
// Define permission-based computed properties
|
||||||
const canRead = hasReadAccess(roleAccess)
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
console.log('canRead', canRead)
|
||||||
|
|
||||||
|
// Page needs
|
||||||
|
const route = useRoute()
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="canRead">
|
<div v-if="canRead">
|
||||||
<ContentChemotherapyList />
|
<Content
|
||||||
|
class-code="ambulatory"
|
||||||
|
sub-class-code="chemo"
|
||||||
|
type="encounter"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Error v-else :status-code="403" />
|
<Error
|
||||||
|
v-else
|
||||||
|
:status-code="403"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RoleAccesses } from '~/models/role'
|
import type { RoleAccesses } from '~/models/role'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
import { permissions } from '~/const/page-permission/client'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
@@ -16,7 +16,7 @@ useHead({
|
|||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const roleAccess: RoleAccesses = PAGE_PERMISSIONS['/client/patient']
|
const roleAccess: RoleAccesses = permissions['/client/patient/add'] ?? {}
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah Assessment Education',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
// const hasAccess = checkRole(roleAccess)
|
|
||||||
const hasAccess = true
|
|
||||||
if (!hasAccess) {
|
|
||||||
navigateTo('/403')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canRead = hasReadAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentAssessmentEducationAdd />
|
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentKfrEntry mode="edit" />
|
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentKfrEntry />
|
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Update PRB',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
|
||||||
const canRead = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentPrbEntry />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Detail PRB',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
|
||||||
const canRead = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentPrbDetail :patient-id="Number(route.params.id)" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah PRB',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, getPagePermissions } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const pagePermission = getPagePermissions(roleAccess)
|
|
||||||
const callbackUrl = route.query['return-path'] as string | undefined
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="pagePermission.canRead">
|
|
||||||
<ContentPrbEntry :callback-url="callbackUrl" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
import EncounterProcess from '~/components/content/encounter/process.vue'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah Kunjungan',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess, getPagePermissions } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const pagePermission = getPagePermissions(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="pagePermission.canRead">
|
|
||||||
<EncounterProcess class-code="ambulatory" sub-class-code="rehab" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Update Surat Kontrol',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
|
||||||
const canRead = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentSurgeryReportEntry />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Detail Surat Kontrol',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
|
||||||
const canRead = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentSurgeryReportDetail :patient-id="Number(route.params.id)" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah Surat Kontrol',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, getPagePermissions } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const pagePermission = getPagePermissions(roleAccess)
|
|
||||||
const callbackUrl = route.query['return-path'] as string | undefined
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="pagePermission.canRead">
|
|
||||||
<ContentSurgeryReportEntry :callback-url="callbackUrl" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentTherapyProtocolEdit />
|
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentTherapyProtocolAdd />
|
|
||||||
</div>
|
|
||||||
<Error
|
|
||||||
v-else
|
|
||||||
:status-code="403"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Detail Data Vaksin',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS[`/rehab/encounter`]
|
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
|
||||||
const canRead = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="canRead">
|
|
||||||
<ContentVaccineDataDetail :patient-id="Number(route.params.id)" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah Data Vaksin',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, getPagePermissions } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// navigateTo('/403')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const pagePermission = getPagePermissions(roleAccess)
|
|
||||||
const callbackUrl = route.query['return-path'] as string | undefined
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-if="pagePermission.canRead">
|
|
||||||
<ContentVaccineDataEntry :callback-url="callbackUrl" />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Resume',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentResumeAdd />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Detail Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentTherapyProtocolDetail/>
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Edit Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentTherapyProtocolEdit />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah Protokol Terapi',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
// if (!hasAccess) {
|
|
||||||
// throw createError({
|
|
||||||
// statusCode: 403,
|
|
||||||
// statusMessage: 'Access denied',
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = true // hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentTherapyProtocolAdd />
|
|
||||||
</div>
|
|
||||||
<Error v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useKeycloak } from "~/composables/useKeycloack"
|
||||||
|
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
const { initKeycloak, isAuthenticated, getResponse } = useKeycloak()
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
// Initialize Keycloak with login-required to ensure tokens set (Keycloak will process the code/state returned)
|
||||||
|
await initKeycloak('login-required')
|
||||||
|
// small delay to allow token propagation
|
||||||
|
if (isAuthenticated.value) {
|
||||||
|
await getResponse()
|
||||||
|
} else {
|
||||||
|
return navigateTo('/auth/login')
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
error.value = err?.message || String(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div style="max-width:720px;margin:40px auto">
|
||||||
|
<h2>Processing login...</h2>
|
||||||
|
<p v-if="error">Terjadi error: {{ error }}</p>
|
||||||
|
<p v-else>Mohon tunggu, sedang memproses otentikasi dan mengarahkan Anda ...</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
import { isValid } from "date-fns"
|
import { isValid } from "date-fns"
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
import { PatientSchema } from "./patient.schema"
|
||||||
|
import { PersonAddressSchema } from "./person-address.schema"
|
||||||
|
import { PersonContactBaseSchema } from "./person-contact.schema"
|
||||||
|
import { PersonAddressRelativeSchema } from "./person-address-relative.schema"
|
||||||
|
|
||||||
const ERROR_MESSAGES = {
|
const ERROR_MESSAGES = {
|
||||||
required: {
|
required: {
|
||||||
@@ -18,12 +22,15 @@ const ERROR_MESSAGES = {
|
|||||||
|
|
||||||
const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
|
const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
|
||||||
const isValidationSep = false
|
const isValidationSep = false
|
||||||
|
// const encounterPatientShcema = z.object({
|
||||||
|
// })
|
||||||
|
|
||||||
const IntegrationEncounterSchema = z
|
const IntegrationEncounterSchema = z
|
||||||
.object({
|
.object({
|
||||||
// Patient data (readonly, populated from selected patient)
|
// Patient data (readonly, populated from selected patient)
|
||||||
|
patientSource: z.string().optional(),
|
||||||
patientName: z.string().optional(),
|
patientName: z.string().optional(),
|
||||||
nationalIdentity: z.string().optional(),
|
residentIdentiyNumber: z.string().optional(),
|
||||||
medicalRecordNumber: z.string().optional(),
|
medicalRecordNumber: z.string().optional(),
|
||||||
|
|
||||||
// Visit data
|
// Visit data
|
||||||
@@ -129,8 +136,21 @@ const IntegrationEncounterSchema = z
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const IntegrationEncounterWPSchema = z
|
||||||
|
.object({
|
||||||
|
encounter: IntegrationEncounterSchema,
|
||||||
|
patient: z.object({
|
||||||
|
person: PatientSchema,
|
||||||
|
personAddresses: z.array(PersonAddressSchema),
|
||||||
|
personContacts: z.array(PersonContactBaseSchema),
|
||||||
|
personRelatives: z.array(PersonAddressRelativeSchema),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
type IntegrationEncounterFormData = z.infer<typeof IntegrationEncounterSchema>
|
type IntegrationEncounterFormData = z.infer<typeof IntegrationEncounterSchema>
|
||||||
|
type IntegrationEncounterWPFormData = z.infer<typeof IntegrationEncounterWPSchema>
|
||||||
|
|
||||||
export { IntegrationEncounterSchema }
|
export { IntegrationEncounterSchema, IntegrationEncounterWPSchema }
|
||||||
export type { IntegrationEncounterFormData }
|
export type { IntegrationEncounterFormData, IntegrationEncounterWPFormData }
|
||||||
|
|
||||||
@@ -102,7 +102,8 @@ const PatientSchema = z
|
|||||||
(data) => {
|
(data) => {
|
||||||
if (data.nationality === 'WNI') {
|
if (data.nationality === 'WNI') {
|
||||||
const nik = data.identityNumber?.trim()
|
const nik = data.identityNumber?.trim()
|
||||||
return !!nik && nik.length === 16 && /^\d+$/.test(nik)
|
if (!nik) return true
|
||||||
|
return nik && nik.length === 16 && /^\d+$/.test(nik)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -64,4 +64,17 @@ export async function checkIn(id: number, data: CheckInFormData) {
|
|||||||
console.error(`Error putting ${name}:`, error)
|
console.error(`Error putting ${name}:`, error)
|
||||||
throw new Error(`Failed to put ${name}`)
|
throw new Error(`Failed to put ${name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createWithPatient(data: any) {
|
||||||
|
try {
|
||||||
|
const resp = await xfetch(path + '/create-with-patient', 'POST', data)
|
||||||
|
const result: any = {}
|
||||||
|
result.success = resp.success
|
||||||
|
result.body = (resp.body as Record<string, any>) || {}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error putting ${name}:`, error)
|
||||||
|
throw new Error(`Failed to put ${name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Base
|
||||||
|
import * as base from './_crud-base'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { Ethnic } from '~/models/ethnic'
|
||||||
|
|
||||||
|
const path = '/api/v1/ethnic'
|
||||||
|
const name = 'ethnic'
|
||||||
|
|
||||||
|
export function create(data: any) {
|
||||||
|
return base.create(path, data, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getList(params: any = null) {
|
||||||
|
return base.getList(path, params, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDetail(id: number | string) {
|
||||||
|
return base.getDetail(path, id, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(id: number | string, data: any) {
|
||||||
|
return base.update(path, id, data, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove(id: number | string) {
|
||||||
|
return base.remove(path, id, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getValueLabelList(
|
||||||
|
params: any = null,
|
||||||
|
useCodeAsValue = false,
|
||||||
|
): Promise<{ value: string; label: string }[]> {
|
||||||
|
let data: { value: string; label: string }[] = []
|
||||||
|
const result = await getList(params)
|
||||||
|
if (result.success) {
|
||||||
|
const resultData = result.body?.data || []
|
||||||
|
data = resultData.map((item: Ethnic) => ({
|
||||||
|
value: useCodeAsValue ? item.code : item.id ? Number(item.id) : item.code,
|
||||||
|
label: item.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Base
|
||||||
|
import * as base from './_crud-base'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import type { Language } from '~/models/language'
|
||||||
|
|
||||||
|
const path = '/api/v1/language'
|
||||||
|
const name = 'language'
|
||||||
|
|
||||||
|
export function create(data: any) {
|
||||||
|
return base.create(path, data, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getList(params: any = null) {
|
||||||
|
return base.getList(path, params, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDetail(id: number | string) {
|
||||||
|
return base.getDetail(path, id, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(id: number | string, data: any) {
|
||||||
|
return base.update(path, id, data, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove(id: number | string) {
|
||||||
|
return base.remove(path, id, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getValueLabelList(
|
||||||
|
params: any = null,
|
||||||
|
useCodeAsValue = false,
|
||||||
|
): Promise<{ value: string; label: string }[]> {
|
||||||
|
let data: { value: string; label: string }[] = []
|
||||||
|
const result = await getList(params)
|
||||||
|
if (result.success) {
|
||||||
|
const resultData = result.body?.data || []
|
||||||
|
data = resultData.map((item: Language) => ({
|
||||||
|
value: useCodeAsValue ? item.code : item.id ? Number(item.id) : item.code,
|
||||||
|
label: item.name,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
@@ -101,7 +101,7 @@ export async function removePatient(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadAttachment(file: File, userId: number, key: UploadCodeKey) {
|
export async function uploadAttachment(file: File, personId: number, key: UploadCodeKey) {
|
||||||
try {
|
try {
|
||||||
const resolvedKey = uploadCode[key]
|
const resolvedKey = uploadCode[key]
|
||||||
if (!resolvedKey) {
|
if (!resolvedKey) {
|
||||||
@@ -110,11 +110,14 @@ export async function uploadAttachment(file: File, userId: number, key: UploadCo
|
|||||||
|
|
||||||
// siapkan form-data body
|
// siapkan form-data body
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('code', resolvedKey)
|
|
||||||
formData.append('content', file)
|
formData.append('content', file)
|
||||||
|
formData.append('entityType_code', 'person')
|
||||||
|
formData.append('ref_id', String(personId))
|
||||||
|
// formData.append('upload_employee_id', String(userId))
|
||||||
|
formData.append('type_code', resolvedKey)
|
||||||
|
|
||||||
// kirim via xfetch
|
// kirim via xfetch
|
||||||
const resp = await xfetch(`${mainUrl}/${userId}/upload`, 'POST', formData)
|
const resp = await xfetch(`/api/v1/upload-file`, 'POST', formData)
|
||||||
|
|
||||||
// struktur hasil sama seperti patchPatient
|
// struktur hasil sama seperti patchPatient
|
||||||
const result: any = {}
|
const result: any = {}
|
||||||
|
|||||||
+26
-10
@@ -6,9 +6,25 @@ export default defineNuxtConfig({
|
|||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
|
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
|
||||||
VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
|
VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
|
||||||
|
//SSO
|
||||||
|
X_AP_CODE: process.env.X_AP_CODE || 'rssa-sso',
|
||||||
|
X_AP_SECRET_KEY: process.env.X_AP_SECRET_KEY || 'sapiperah',
|
||||||
|
SSO_CONFIRM_URL: process.env.SSO_CONFIRM_URL || 'https://auth.rssa.top/realms/sandbox/protocol/openid-connect/userinfo',
|
||||||
|
KEYCLOAK_LOGOUT_REDIRECT: process.env.KEYCLOAK_LOGOUT_REDIRECT || 'http://localhost:3000',
|
||||||
|
KEYCLOAK_REALM: process.env.KEYCLOAK_REALM || 'sandbox',
|
||||||
|
KEYCLOAK_URL: process.env.KEYCLOAK_URL || 'https://auth.dev.rssa.id/',
|
||||||
|
CLIENT_ID: process.env.CLIENT_ID || 'portal-simrs-new',
|
||||||
public: {
|
public: {
|
||||||
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
|
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
|
||||||
VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
|
VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
|
||||||
|
//SSO
|
||||||
|
X_AP_CODE: process.env.X_AP_CODE || 'rssa-sso',
|
||||||
|
X_AP_SECRET_KEY: process.env.X_AP_SECRET_KEY || 'sapiperah',
|
||||||
|
SSO_CONFIRM_URL: process.env.SSO_CONFIRM_URL || 'https://auth.rssa.top/realms/sandbox/protocol/openid-connect/userinfo',
|
||||||
|
KEYCLOAK_LOGOUT_REDIRECT: process.env.KEYCLOAK_LOGOUT_REDIRECT || 'http://localhost:3000',
|
||||||
|
KEYCLOAK_REALM: process.env.KEYCLOAK_REALM || 'sandbox',
|
||||||
|
KEYCLOAK_URL: process.env.KEYCLOAK_URL || 'https://auth.dev.rssa.id/',
|
||||||
|
CLIENT_ID: process.env.CLIENT_ID || 'portal-simrs-new',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@@ -48,21 +64,11 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
css: ['@unocss/reset/tailwind.css', '~/assets/css/main.css'],
|
css: ['@unocss/reset/tailwind.css', '~/assets/css/main.css'],
|
||||||
|
|
||||||
colorMode: {
|
|
||||||
classSuffix: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
features: {
|
features: {
|
||||||
// For UnoCSS
|
// For UnoCSS
|
||||||
inlineStyles: false,
|
inlineStyles: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
eslint: {
|
|
||||||
config: {
|
|
||||||
standalone: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
imports: {
|
imports: {
|
||||||
dirs: ['./app/lib'],
|
dirs: ['./app/lib'],
|
||||||
},
|
},
|
||||||
@@ -71,5 +77,15 @@ export default defineNuxtConfig({
|
|||||||
componentDir: './app/components/pub/ui',
|
componentDir: './app/components/pub/ui',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
colorMode: {
|
||||||
|
classSuffix: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
eslint: {
|
||||||
|
config: {
|
||||||
|
standalone: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
compatibilityDate: '2025-07-15',
|
compatibilityDate: '2025-07-15',
|
||||||
})
|
})
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"format": "eslint --fix ."
|
"format": "eslint --fix ."
|
||||||
},
|
},
|
||||||
|
"main": "./lib/keycloak.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/lucide": "^1.2.30",
|
"@iconify-json/lucide": "^1.2.30",
|
||||||
"@iconify-json/radix-icons": "^1.2.2",
|
"@iconify-json/radix-icons": "^1.2.2",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
"embla-carousel-vue": "^8.5.2",
|
"embla-carousel-vue": "^8.5.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"h3": "^1.15.4",
|
"h3": "^1.15.4",
|
||||||
|
"nuxt-openid-connect": "^0.8.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.4.1",
|
"pinia-plugin-persistedstate": "^4.4.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
@@ -53,7 +55,9 @@
|
|||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-format": "^1.0.1",
|
"eslint-plugin-format": "^1.0.1",
|
||||||
"happy-dom": "^18.0.1",
|
"happy-dom": "^18.0.1",
|
||||||
|
"keycloak-js": "^26.2.1",
|
||||||
"lucide-vue-next": "^0.482.0",
|
"lucide-vue-next": "^0.482.0",
|
||||||
|
"next-auth": "~4.21.1",
|
||||||
"nuxt": "^4.0.3",
|
"nuxt": "^4.0.3",
|
||||||
"playwright-core": "^1.54.2",
|
"playwright-core": "^1.54.2",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
|
|||||||
Generated
+8544
-6636
File diff suppressed because it is too large
Load diff
@@ -20,6 +20,11 @@
|
|||||||
"title": "Konsultasi",
|
"title": "Konsultasi",
|
||||||
"icon": "i-lucide-building-2",
|
"icon": "i-lucide-building-2",
|
||||||
"link": "/ambulatory/consultation"
|
"link": "/ambulatory/consultation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Triase",
|
||||||
|
"icon": "i-lucide-stethoscope",
|
||||||
|
"link": "/ambulatory/triage"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -47,7 +52,42 @@
|
|||||||
{
|
{
|
||||||
"title": "Kemoterapi",
|
"title": "Kemoterapi",
|
||||||
"icon": "i-lucide-droplets",
|
"icon": "i-lucide-droplets",
|
||||||
"link": "/chemotherapy"
|
"link": "/chemotherapy",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"title": "Administrasi",
|
||||||
|
"link": "/chemotherapy/early-encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Kunjungan",
|
||||||
|
"link": "/chemotherapy/encounter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Hemofilia",
|
||||||
|
"icon": "i-lucide-droplet-off",
|
||||||
|
"link": "/hemophilia"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"heading": "Ruang Tindakan Anak",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"title": "Thalasemi",
|
||||||
|
"icon": "i-lucide-baby",
|
||||||
|
"link": "/thalasemia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Echocardiography",
|
||||||
|
"icon": "i-lucide-baby",
|
||||||
|
"link": "/echocardiography"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Spirometri",
|
||||||
|
"icon": "i-lucide-baby",
|
||||||
|
"link": "/spirometry"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"heading": "Menu Utama",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Dashboard",
|
|
||||||
"icon": "i-lucide-home",
|
|
||||||
"link": "/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rawat Jalan",
|
|
||||||
"icon": "i-lucide-stethoscope",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"icon": "i-lucide-stethoscope",
|
|
||||||
"link": "/ambulatory/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"link": "/ambulatory/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Triase",
|
|
||||||
"icon": "i-lucide-stethoscope",
|
|
||||||
"link": "/emergency/triage"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"link": "/emergency/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"link": "/emergency/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rawat Inap",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"link": "/inpatient/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"link": "/inpatient/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Ruang Tindakan Rajal",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Kemoterapi",
|
|
||||||
"icon": "i-lucide-droplets",
|
|
||||||
"link": "/chemotherapy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Hemofilia",
|
|
||||||
"icon": "i-lucide-droplet-off",
|
|
||||||
"link": "/hemophilia"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Ruang Tindakan Anak",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Thalasemi",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/thalasemia"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Echocardiography",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/echocardiography"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Spirometri",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/spirometry"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -12,16 +12,6 @@
|
|||||||
"icon": "i-lucide-stethoscope",
|
"icon": "i-lucide-stethoscope",
|
||||||
"link": "/ambulatory/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"link": "/emergency/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rehabilitasi Medik",
|
|
||||||
"icon": "i-lucide-bike",
|
|
||||||
"link": "/rehab/encounter-queue"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Rawat Inap",
|
"title": "Rawat Inap",
|
||||||
"icon": "i-lucide-building-2",
|
"icon": "i-lucide-building-2",
|
||||||
@@ -18,20 +18,10 @@
|
|||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"link": "/ambulatory/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Triase",
|
|
||||||
"link": "/emergency/triage"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Triase",
|
||||||
"link": "/emergency/encounter"
|
"link": "/ambulatory/triage"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,11 +21,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"link": "/emergency/encounter"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Rawat Inap",
|
"title": "Rawat Inap",
|
||||||
"icon": "i-lucide-building-2",
|
"icon": "i-lucide-building-2",
|
||||||
|
|||||||
@@ -1,350 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"heading": "Menu Utama",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Dashboard",
|
|
||||||
"icon": "i-lucide-home",
|
|
||||||
"link": "/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rawat Jalan",
|
|
||||||
"icon": "i-lucide-stethoscope",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Antrian Pendaftaran",
|
|
||||||
"link": "/ambulatory/registration-queue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Antrian Poliklinik",
|
|
||||||
"link": "/ambulatory/encounter-queue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"link": "/ambulatory/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"link": "/ambulatory/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Triase",
|
|
||||||
"link": "/emergency/triage"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"link": "/emergency/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"link": "/emergency/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rawat Inap",
|
|
||||||
"icon": "i-lucide-building-2",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Permintaan",
|
|
||||||
"link": "/inpatient/request"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"link": "/inpatient/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"link": "/inpatient/consultation"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Obat - Order",
|
|
||||||
"icon": "i-lucide-briefcase-medical",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Permintaan",
|
|
||||||
"link": "/medication/order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Standing Order",
|
|
||||||
"link": "/medication/standing-order"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Radiologi - Order",
|
|
||||||
"icon": "i-lucide-radio",
|
|
||||||
"link": "/radiology-order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Lab - Order",
|
|
||||||
"icon": "i-lucide-microscope",
|
|
||||||
"link": "/cp-lab-order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Lab Mikro - Order",
|
|
||||||
"icon": "i-lucide-microscope",
|
|
||||||
"link": "/micro-lab-order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Lab PA - Order",
|
|
||||||
"icon": "i-lucide-microscope",
|
|
||||||
"link": "/ap-lab-order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Gizi",
|
|
||||||
"icon": "i-lucide-egg-fried",
|
|
||||||
"link": "/nutrition-order"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Pembayaran",
|
|
||||||
"icon": "i-lucide-banknote-arrow-up",
|
|
||||||
"link": "/payment"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Ruang Tindakan Rajal",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Kemoterapi",
|
|
||||||
"icon": "i-lucide-droplets",
|
|
||||||
"link": "/outpation-action/cemotherapy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Hemofilia",
|
|
||||||
"icon": "i-lucide-droplet-off",
|
|
||||||
"link": "/outpation-action/hemophilia"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Ruang Tindakan Anak",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Thalasemi",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/children-action/thalasemia"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Echocardiography",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/children-action/echocardiography"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Spirometri",
|
|
||||||
"icon": "i-lucide-baby",
|
|
||||||
"link": "/children-action/spirometry"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Client",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Pasien",
|
|
||||||
"icon": "i-lucide-users",
|
|
||||||
"link": "/client/patient"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Rekam Medis",
|
|
||||||
"icon": "i-lucide-file-text",
|
|
||||||
"link": "/client/medical-record"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Integrasi",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "BPJS",
|
|
||||||
"icon": "i-lucide-circuit-board",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "SEP",
|
|
||||||
"icon": "i-lucide-circuit-board",
|
|
||||||
"link": "/integration/bpjs-vclaim/sep"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Peserta",
|
|
||||||
"icon": "i-lucide-circuit-board",
|
|
||||||
"link": "/integration/bpjs-vclaim/member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Surat Kontrol",
|
|
||||||
"icon": "i-lucide-circuit-board",
|
|
||||||
"link": "/integration/bpjs-vclaim/control-letter"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "SATUSEHAT",
|
|
||||||
"icon": "i-lucide-database",
|
|
||||||
"link": "/integration/satusehat"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"heading": "Source",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"title": "Peralatan dan Perlengkapan",
|
|
||||||
"icon": "i-lucide-layout-dashboard",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Obat",
|
|
||||||
"link": "/tools-equipment-src/medicine"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Peralatan",
|
|
||||||
"link": "/tools-equipment-src/tools"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Perlengkapan (BMHP)",
|
|
||||||
"link": "/tools-equipment-src/equipment"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Metode Obat",
|
|
||||||
"link": "/tools-equipment-src/medicine-method"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Jenis Obat",
|
|
||||||
"link": "/tools-equipment-src/medicine-type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Sediaan Obat",
|
|
||||||
"link": "/tools-equipment-src/medicine-form"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Pengguna",
|
|
||||||
"icon": "i-lucide-user",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Pegawai",
|
|
||||||
"link": "/human-src/employee"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "PPDS",
|
|
||||||
"link": "/human-src/specialist-intern"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Pemeriksaan Penunjang",
|
|
||||||
"icon": "i-lucide-layout-list",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Checkup",
|
|
||||||
"link": "/mcu-src/mcu"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Prosedur",
|
|
||||||
"link": "/mcu-src/procedure"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Diagnosis",
|
|
||||||
"link": "/mcu-src/diagnose"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Medical Action",
|
|
||||||
"link": "/mcu-src/medical-action"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Infrastruktur",
|
|
||||||
"icon": "i-lucide-layout-list",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Kasur",
|
|
||||||
"link": "/infra-src/bed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kamar",
|
|
||||||
"link": "/infra-src/chamber"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Ruang",
|
|
||||||
"link": "/infra-src/room"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Depo",
|
|
||||||
"link": "/infra-src/warehouse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Lantai",
|
|
||||||
"link": "/infra-src/floor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Gedung",
|
|
||||||
"link": "/infra-src/building"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Counter",
|
|
||||||
"link": "/infra-src/counter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Public Screen (Big Screen)",
|
|
||||||
"link": "/infra-src/public-screen"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Organisasi",
|
|
||||||
"icon": "i-lucide-network",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Divisi",
|
|
||||||
"link": "/org-src/division"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Instalasi",
|
|
||||||
"link": "/org-src/installation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Unit",
|
|
||||||
"link": "/org-src/unit"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Spesialis",
|
|
||||||
"link": "/org-src/specialist"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Sub Spesialis",
|
|
||||||
"link": "/org-src/subspecialist"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Umum",
|
|
||||||
"icon": "i-lucide-airplay",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Uom",
|
|
||||||
"link": "/common/uom"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Keuangan",
|
|
||||||
"icon": "i-lucide-airplay",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Item & Pricing",
|
|
||||||
"link": "/common/item"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -26,24 +26,10 @@
|
|||||||
{
|
{
|
||||||
"title": "Konsultasi",
|
"title": "Konsultasi",
|
||||||
"link": "/ambulatory/consultation"
|
"link": "/ambulatory/consultation"
|
||||||
}
|
},
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "IGD",
|
|
||||||
"icon": "i-lucide-zap",
|
|
||||||
"children": [
|
|
||||||
{
|
{
|
||||||
"title": "Triase",
|
"title": "Triase",
|
||||||
"link": "/emergency/triage"
|
"link": "/emergency/triage"
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Kunjungan",
|
|
||||||
"link": "/emergency/encounter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Konsultasi",
|
|
||||||
"link": "/emergency/consultation"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -139,12 +125,22 @@
|
|||||||
{
|
{
|
||||||
"title": "Kemoterapi",
|
"title": "Kemoterapi",
|
||||||
"icon": "i-lucide-droplets",
|
"icon": "i-lucide-droplets",
|
||||||
"link": "/outpation-action/chemotherapy"
|
"link": "/chemotherapy",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"title": "Administrasi",
|
||||||
|
"link": "/chemotherapy/early-encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Kunjungan",
|
||||||
|
"link": "/chemotherapy/encounter"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Hemofilia",
|
"title": "Hemofilia",
|
||||||
"icon": "i-lucide-droplet-off",
|
"icon": "i-lucide-droplet-off",
|
||||||
"link": "/outpation-action/hemophilia"
|
"link": "/hemophilia"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -154,17 +150,17 @@
|
|||||||
{
|
{
|
||||||
"title": "Thalasemi",
|
"title": "Thalasemi",
|
||||||
"icon": "i-lucide-baby",
|
"icon": "i-lucide-baby",
|
||||||
"link": "/children-action/thalasemia"
|
"link": "/thalasemia"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Echocardiography",
|
"title": "Echocardiography",
|
||||||
"icon": "i-lucide-baby",
|
"icon": "i-lucide-baby",
|
||||||
"link": "/children-action/echocardiography"
|
"link": "/echocardiography"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Spirometri",
|
"title": "Spirometri",
|
||||||
"icon": "i-lucide-baby",
|
"icon": "i-lucide-baby",
|
||||||
"link": "/children-action/spirometry"
|
"link": "/spirometry"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -252,20 +248,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "Pengguna",
|
|
||||||
"icon": "i-lucide-user",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"title": "Pegawai",
|
|
||||||
"link": "/human-src/employee"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "PPDS",
|
|
||||||
"link": "/human-src/specialist-intern"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Pemeriksaan Penunjang",
|
"title": "Pemeriksaan Penunjang",
|
||||||
"icon": "i-lucide-layout-list",
|
"icon": "i-lucide-layout-list",
|
||||||
@@ -288,6 +270,20 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Pengguna",
|
||||||
|
"icon": "i-lucide-user",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"title": "Pegawai",
|
||||||
|
"link": "/human-src/employee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "PPDS",
|
||||||
|
"link": "/human-src/intern"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Layanan",
|
"title": "Layanan",
|
||||||
"icon": "i-lucide-layout-list",
|
"icon": "i-lucide-layout-list",
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { getRequestURL, readBody, setCookie } from 'h3'
|
||||||
|
|
||||||
|
// Function to verify JWT token with the userinfo endpoint
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event)
|
||||||
|
const url = getRequestURL(event)
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
|
||||||
|
const apiSSOConfirm = config.public.SSO_CONFIRM_URL
|
||||||
|
const token = 'Bearer ' + body.data.token
|
||||||
|
|
||||||
|
const res_sso = await fetch(apiSSOConfirm,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': token,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(res_sso)
|
||||||
|
if (res_sso.status === 200) {
|
||||||
|
const apiOrigin = config.public.API_ORIGIN
|
||||||
|
|
||||||
|
const cleanOrigin = apiOrigin.replace(/\/+$/, '')
|
||||||
|
const cleanPath = url.pathname.replace(/^\/api\//, '').replace(/^\/+/, '')
|
||||||
|
const externalUrl = `${cleanOrigin}/${cleanPath}${url.search}`
|
||||||
|
|
||||||
|
const resp = await fetch(externalUrl,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: body.data.user.username,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-AuthPartner-Code': config.public.X_AP_CODE,
|
||||||
|
'X-AuthPartner-SecretKey': config.public.X_AP_SECRET_KEY,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const data = await resp.json()
|
||||||
|
|
||||||
|
if (data?.data?.accessToken) {
|
||||||
|
setCookie(event, 'authentication', data.data.accessToken, {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'strict',
|
||||||
|
maxAge: 60 * 60 * 24,
|
||||||
|
})
|
||||||
|
|
||||||
|
delete data.data.accessToken
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(await resp.text(), {
|
||||||
|
status: resp.status,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': resp.headers.get('content-type') || 'text/plain',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(await res_sso.text(), {
|
||||||
|
status: res_sso.status,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': res_sso.headers.get('content-type') || 'text/plain',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user