Merge pull request #181 from dikstub-rssa/feat/kfr-kemoterapi-174
Feat/kfr kemoterapi 174
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '~/lib/utils';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
assesmentDate?: string
|
||||
class?: string
|
||||
}>(), {
|
||||
assesmentDate: new Date().toISOString(),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center gap-3 p-3 rounded-md text-orange-500 border border-orange-400 bg-orange-50',
|
||||
props.class
|
||||
)">
|
||||
<Icon name="i-lucide-triangle-alert" class="h-9 w-9 align-middle transition-colors" />
|
||||
<p class="font-medium">Pasien telah mencapai atau telah melampaui jadwal Asesment pada
|
||||
<b>{{ new Date(props.assesmentDate).toDateString() }}</b>
|
||||
<br>
|
||||
Harap melakukan Re-Asement sebelum melanjutkan Protocol Therapy</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
import Button from '~/components/pub/ui/button/Button.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
rec: ListItemDto
|
||||
}>()
|
||||
|
||||
const recId = inject<Ref<number>>('rec_id')!
|
||||
const recAction = inject<Ref<string>>('rec_action')!
|
||||
const recItem = inject<Ref<any>>('rec_item')!
|
||||
const recDate = inject<Ref<any>>('rec_date')!
|
||||
|
||||
function confirm() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showConfirmVerify
|
||||
recItem.value = props.rec
|
||||
recDate.value = new Date().getTime()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button type="button" variant="outline" class="text-orange-500 border border-orange-400 bg-orange-50"
|
||||
@click="confirm">
|
||||
<Icon name="i-lucide-circle-check" class="h-4 w-4 align-middle transition-colors" />
|
||||
Konfirmasi
|
||||
</Button>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
import Button from '~/components/pub/ui/button/Button.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
}>()
|
||||
const isModalOpen = inject<Ref<boolean>>('isHistoryDialogOpen')!
|
||||
|
||||
function openDialog() {
|
||||
isModalOpen.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button type="button" variant="outline" class="text-orange-500 border border-orange-400 bg-orange-50"
|
||||
@click="openDialog">
|
||||
<Icon name="i-lucide-history" class="h-4 w-4 align-middle transition-colors" />
|
||||
History
|
||||
</Button>
|
||||
</template>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
rec: any
|
||||
idx?: number
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>S : </b></td>
|
||||
<td>{{ props.rec.result.s }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Separator class="my-3" />
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>O : </b></td>
|
||||
<td>{{ props.rec.result.o }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Separator class="my-3" />
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>A : </b></td>
|
||||
<td>{{ props.rec.result.a }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Separator class="my-3" />
|
||||
<div>
|
||||
<h1><b>P : </b></h1>
|
||||
<ul class="pl-5 list-disc space-y-1">
|
||||
<li>
|
||||
<h1><b>Goal of Treatment</b></h1>
|
||||
<p>{{ props.rec.result.p.goal }}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h1><b>Tindakan/Program Rehabilitasi Medik</b></h1>
|
||||
<p>{{ props.rec.result.p.action }}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h1><b>Edukasi</b></h1>
|
||||
<p>{{ props.rec.result.p.education }}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h1><b>Frekuensi Kunjungan</b></h1>
|
||||
<p>{{ props.rec.result.p.frequency }} x Perminggu</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Separator class="my-3" />
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Rencana Tindak Lanjut : </b></td>
|
||||
<td>{{ props.rec.result.plan }} - {{ props.rec.result.planDesc }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type LinkItem, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
rec: ListItemDto
|
||||
}>()
|
||||
|
||||
const { getActiveRole } = useUserStore()
|
||||
const recId = inject<Ref<number>>('rec_id')!
|
||||
const recAction = inject<Ref<string>>('rec_action')!
|
||||
const recItem = inject<Ref<any>>('rec_item')!
|
||||
const timestamp = inject<Ref<any>>('timestamp')!
|
||||
|
||||
const activeKey = ref<string | null>(null)
|
||||
const linkItems = computed(() => {
|
||||
const role = getActiveRole()
|
||||
const isAdmin = role == "system"
|
||||
const isDoctorRole = role == "emp|doc"
|
||||
const isPhysioRole = role == "emp|doc"
|
||||
const isUnverified = true // recItem.id === 0
|
||||
const isUnvalidated = true // recItem.id
|
||||
|
||||
const items: LinkItem[] = [
|
||||
{ label: 'Print', onClick: print, icon: 'i-lucide-printer', }
|
||||
]
|
||||
if (isDoctorRole || isAdmin) {
|
||||
items.push({ label: 'Edit', onClick: edit, icon: 'i-lucide-pencil', })
|
||||
|
||||
if (isUnverified) {
|
||||
items.push({ label: 'Verify', onClick: verify, icon: 'i-lucide-check', })
|
||||
}
|
||||
if (!isUnverified && isUnvalidated) { // verified & unvalidated
|
||||
items.push({ label: 'Validate', onClick: validate, icon: 'i-lucide-check-check', })
|
||||
}
|
||||
|
||||
items.push({ label: 'Delete', onClick: del, icon: 'i-lucide-trash', })
|
||||
}
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
function edit() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showEdit
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
|
||||
function verify() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showVerify
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
function validate() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showValidate
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
|
||||
function print() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showPrint
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
function del() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showConfirmDelete
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<SidebarMenuButton size="lg"
|
||||
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800">
|
||||
<Icon name="i-lucide-chevrons-up-down" class="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||
align="end">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem v-for="item in linkItems" :key="item.label"
|
||||
class="hover:bg-gray-100 dark:hover:bg-slate-700" @click="item.onClick" @mouseenter="activeKey = item.label"
|
||||
@mouseleave="activeKey = null">
|
||||
<Icon :name="item.icon ?? ''" />
|
||||
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,93 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { Label as RadioLabel } from '~/components/pub/ui/label'
|
||||
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
radioGroupClass?: string
|
||||
radioItemClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'isNewBorn',
|
||||
label = 'Status Pasien',
|
||||
errors,
|
||||
class: containerClass,
|
||||
radioGroupClass,
|
||||
radioItemClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const newbornOptions = [
|
||||
{ value: 'EVALUASI', label: 'Evaluasi' },
|
||||
{ value: 'RUJUK', label: 'Rujuk' },
|
||||
{ value: 'SELESAI', label: 'Selesai' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DE.Cell :class="cn('radio-group-field', containerClass)" :col-span="2">
|
||||
<DE.Label
|
||||
:label-for="fieldName"
|
||||
:is-required="isRequired"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
v-bind="componentField"
|
||||
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in newbornOptions"
|
||||
:key="option.value"
|
||||
:class="cn('flex min-w-fit items-center space-x-2 pt-1', radioItemClass)"
|
||||
>
|
||||
<RadioGroupItem
|
||||
:id="`${fieldName}-${index}`"
|
||||
:value="option.value"
|
||||
:class="
|
||||
cn(
|
||||
'relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||
containerClass,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<RadioLabel
|
||||
:for="`${fieldName}-${index}`"
|
||||
:class="
|
||||
cn(
|
||||
'cursor-pointer select-none text-xs font-normal leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
||||
labelClass,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ option.label }}
|
||||
</RadioLabel>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage class="ml-0 mt-1" />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import { Badge } from '~/components/pub/ui/badge'
|
||||
|
||||
const props = defineProps<{
|
||||
rec: any
|
||||
idx?: number
|
||||
}>()
|
||||
|
||||
const verifyStatusCodes: Record<string, string> = {
|
||||
verified: 'Terverifikasi',
|
||||
unverified: 'Belum Verifikasi',
|
||||
}
|
||||
const validateStatusCodes: Record<string, string> = {
|
||||
validated: 'Tervalidasi',
|
||||
unvalidated: 'Belum Validasi',
|
||||
}
|
||||
|
||||
const verifyStatusText = computed(() => {
|
||||
const code: keyof typeof verifyStatusCodes = props.rec.status.verified === 1 ? `verified` : `unverified`
|
||||
return verifyStatusCodes[code]
|
||||
})
|
||||
const validateStatusText = computed(() => {
|
||||
const code: keyof typeof validateStatusCodes = props.rec.status.validated === 1 ? `validated` : `unvalidated`
|
||||
return validateStatusCodes[code]
|
||||
})
|
||||
|
||||
const verifyBadgeVariant = computed(() => {
|
||||
return props.rec.status.verified === 1 ? 'default' : 'outline'
|
||||
})
|
||||
const validateBadgeVariant = computed(() => {
|
||||
return props.rec.status.validated === 1 ? 'default' : 'outline'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 items-center justify-center">
|
||||
<Badge :variant="verifyBadgeVariant" class="w-fit rounded-2xl text-[0.6rem]" >
|
||||
{{ verifyStatusText }}
|
||||
</Badge>
|
||||
<Badge :variant="validateBadgeVariant" class="w-fit rounded-2xl text-[0.6rem]" >
|
||||
{{ validateStatusText }}
|
||||
</Badge>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,128 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import RadioFollowup from './_common/radio-followup.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
schema: any
|
||||
initialValues?: any
|
||||
errors?: FormErrors
|
||||
}>()
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
// const isMedicalDiagnosisPickerDialogOpen = ref<boolean>(false)
|
||||
// const isFunctionalDiagnosisPickerDialogOpen = ref<boolean>(false)
|
||||
// const isProcedurePickerDialogOpen = ref<boolean>(false)
|
||||
|
||||
// function toggleMedicalDiagnosisPickerDialog() {
|
||||
// isMedicalDiagnosisPickerDialogOpen.value = !isMedicalDiagnosisPickerDialogOpen.value
|
||||
// }
|
||||
// function toggleFunctionalDiagnosisPickerDialog() {
|
||||
// isFunctionalDiagnosisPickerDialogOpen.value = !isFunctionalDiagnosisPickerDialogOpen.value
|
||||
// }
|
||||
|
||||
// provide(`isDiagnosisPickerDialogOpen`, isDiagnosisPickerDialogOpen)
|
||||
// provide(`isProcedurePickerDialogOpen`, isProcedurePickerDialogOpen)
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues">
|
||||
|
||||
<!-- FORM 1 -->
|
||||
<DE.Block :col-count="2" :cell-flex="false">
|
||||
<DE.Cell :col-span="2">
|
||||
<TextAreaInput
|
||||
field-name="subjective"
|
||||
label="Subjective"
|
||||
placeholder="Subjective"
|
||||
class="w-1/2"
|
||||
:errors="errors" />
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="2" >
|
||||
<TextAreaInput
|
||||
field-name="objective"
|
||||
label="Objective"
|
||||
placeholder="Masukkan Objective"
|
||||
class="w-1/2"
|
||||
:errors="errors" />
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="2">
|
||||
<TextAreaInput
|
||||
field-name="assesment"
|
||||
label="Assesment"
|
||||
placeholder="Masukkan Assesment"
|
||||
class="w-1/2"
|
||||
:errors="errors" />
|
||||
</DE.Cell>
|
||||
|
||||
<DE.Cell class="mt-2 px-4 bg-gray-50 border rounded-lg" :col-span="2">
|
||||
<DE.Block :col-count="2" :cell-flex="false">
|
||||
<TextAreaInput
|
||||
field-name="planningGoal"
|
||||
label="Goal of Treatment"
|
||||
placeholder="Masukkan Goal of Treatment"
|
||||
:errors="errors" />
|
||||
<TextAreaInput
|
||||
field-name="planningAction"
|
||||
label="Tindakan/Program Rehabilitasi Medik"
|
||||
placeholder="Masukkan Tindakan/Program Rehabilitasi Medik"
|
||||
:errors="errors" />
|
||||
<TextAreaInput
|
||||
field-name="planningEducation"
|
||||
label="Edukasi"
|
||||
placeholder="Masukkan Edukasi"
|
||||
:errors="errors" />
|
||||
<InputBase
|
||||
field-name="planningFrequency"
|
||||
label="Frekuensi Kunjungan"
|
||||
right-label="x Minggu"
|
||||
placeholder="Masukkan Frekuensi Kunjungan"
|
||||
:errors="errors"
|
||||
numeric-only
|
||||
is-disabled
|
||||
/>
|
||||
</DE.Block>
|
||||
</DE.Cell>
|
||||
|
||||
<DE.Cell :col-span="2">
|
||||
<RadioFollowup
|
||||
field-name="followUpPlan"
|
||||
label="Rencana Tindak Lanjut"
|
||||
:errors="errors"
|
||||
is-required
|
||||
/>
|
||||
<TextAreaInput
|
||||
label=""
|
||||
field-name="followUpPlanDesc"
|
||||
placeholder="Masukkan Keterangan rencana tindak lanjut"
|
||||
class="w-1/2 mt-3"
|
||||
:errors="errors" />
|
||||
</DE.Cell>
|
||||
|
||||
|
||||
|
||||
</DE.Block>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dp.vue'))
|
||||
const resultData = defineAsyncComponent(() => import('./_common/card-result.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('./_common/verify-badge.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, { width: 800 }, {}, { width: 120 }, { width: 3 },],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Tanggal' },
|
||||
{ label: 'Hasil Asesmen Pasien Dan Pemberian Pelayanan' },
|
||||
{ label: 'Jenis Form' },
|
||||
{ label: 'Status' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'result', 'type', 'status', 'action'],
|
||||
|
||||
parses: {
|
||||
date: (rec: unknown): unknown => {
|
||||
const date = (rec as any).date
|
||||
|
||||
if (typeof date == 'object' && date) {
|
||||
return (date as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof date == 'string') {
|
||||
return (date as string).substring(0, 10)
|
||||
}
|
||||
return date
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
result(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: resultData,
|
||||
}
|
||||
},
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
status(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
import { config } from './history-list.cfg'
|
||||
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
import type { DateRange } from 'radix-vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
dateValue: DateRange
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
'update:dateValue': [value: DateRange]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||
!props.dateValue && 'text-muted-foreground')">
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
<template v-if="props.dateValue.start">
|
||||
<template v-if="props.dateValue.end">
|
||||
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||
</template>
|
||||
</template>
|
||||
<template v-else> Pick a date </template>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-auto p-0">
|
||||
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('./_common/dropdown-action.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('./_common/verify-badge.vue'))
|
||||
const resultData = defineAsyncComponent(() => import('./_common/card-result.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, { width: 800 }, {}, { width: 120 }, { width: 3 },],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Tanggal' },
|
||||
{ label: 'Hasil Asesmen Pasien Dan Pemberian Pelayanan' },
|
||||
{ label: 'Jenis Form' },
|
||||
{ label: 'Status' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'result', 'type', 'status', 'action'],
|
||||
|
||||
parses: {
|
||||
date: (rec: unknown): unknown => {
|
||||
const date = (rec as any).date
|
||||
|
||||
if (typeof date == 'object' && date) {
|
||||
return (date as Date).toLocaleDateString('id-ID')
|
||||
} else if (typeof date == 'string') {
|
||||
return (date as string).substring(0, 10)
|
||||
}
|
||||
return date
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
result(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: resultData,
|
||||
}
|
||||
},
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
status(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable v-bind="config" :rows="data" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
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 Select from '~/components/pub/my-ui/form/select.vue'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import type { InstallationFormData } from '~/schemas/installation.schema'
|
||||
import TextCaptcha from '~/components/pub/my-ui/form/text-captcha.vue'
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
schema: any
|
||||
errors?: FormErrors
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: InstallationFormData, resetForm: () => void]
|
||||
cancel: [resetForm: () => void]
|
||||
}>()
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
const captchaRef = ref<InstanceType<typeof TextCaptcha> | null>(null)
|
||||
const captchaValid = ref(false)
|
||||
|
||||
// Form submission handler
|
||||
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||
const formData: InstallationFormData = {
|
||||
name: values.name || '',
|
||||
code: values.code || '',
|
||||
}
|
||||
emit('submit', formData, resetForm)
|
||||
}
|
||||
|
||||
function onCaptchaUpdate(valid: boolean) {
|
||||
captchaValid.value = valid
|
||||
}
|
||||
|
||||
// Form cancel handler
|
||||
function onCancelForm({ resetForm }: { resetForm: () => void }) {
|
||||
emit('cancel', resetForm)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
validation-mode="onSubmit"
|
||||
>
|
||||
<div class="border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||
<div class="flex flex-col justify-between">
|
||||
|
||||
<InputBase
|
||||
field-name="name"
|
||||
label="Nama"
|
||||
placeholder="Masukkan Nama"
|
||||
:errors="errors"/>
|
||||
<InputBase
|
||||
field-name="email"
|
||||
label="Email"
|
||||
placeholder="Masukkan Email"
|
||||
:errors="errors"/>
|
||||
|
||||
<div class="mt-2">
|
||||
<Label class="" for="password">Password</Label>
|
||||
<Field class="" id="password" :errors="errors">
|
||||
<FormField v-slot="{ componentField }" name="password">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="password"
|
||||
v-bind="componentField"
|
||||
type="password"
|
||||
class="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<TextCaptcha
|
||||
ref="captchaRef"
|
||||
:length="5"
|
||||
:useSpacing="true"
|
||||
:noiseChars="true"
|
||||
@update:valid="onCaptchaUpdate"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
Reference in New Issue
Block a user