Merge branch 'dev' of https://github.com/dikstub-rssa/simrs-fe into feat/user

This commit is contained in:
Abizrh
2025-08-28 12:01:55 +07:00
225 changed files with 2110 additions and 421 deletions
+14 -9
View File
@@ -11,8 +11,9 @@ import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/custom-ui/data/dropdown-action-dud.vue'))
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
const doctorStatus = {
const _doctorStatus = {
0: 'Tidak Aktif',
1: 'Aktif',
}
@@ -26,10 +27,10 @@ export const cols: Col[] = [
{},
{},
{},
{ width: 120 },
{ width: 100 },
{ width: 100 },
{ width: 100 },
{ width: 50 },
{},
{},
]
export const header: Th[][] = [
@@ -43,6 +44,7 @@ export const header: Th[][] = [
{ label: 'Fee Ranap' },
{ label: 'Fee Rajal' },
{ label: 'Status' },
{ label: '' },
],
]
@@ -66,7 +68,6 @@ export const delKeyNames: KeyLabel[] = [
export const funcParsed: RecStrFuncUnknown = {
name: (rec: unknown): unknown => {
console.log(rec)
const recX = rec as SmallDetailDto
return `${recX.frontTitle} ${recX.name} ${recX.endTitle}`.trim()
},
@@ -85,10 +86,6 @@ export const funcParsed: RecStrFuncUnknown = {
const recX = rec as SmallDetailDto
return Number(recX.outPatient_itemPrice.price).toLocaleString('id-ID')
},
status: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return doctorStatus[recX.status_code as keyof typeof doctorStatus]
},
}
export const funcComponent: RecStrFuncComponent = {
@@ -100,6 +97,14 @@ export const funcComponent: RecStrFuncComponent = {
}
return res
},
status(rec, idx) {
const res: RecComponent = {
idx,
rec: rec as object,
component: statusBadge,
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { Badge } from '~/components/pub/ui/badge'
const props = defineProps<{
rec: any
idx?: number
}>()
const doctorStatus = {
0: 'Tidak Aktif',
1: 'Aktif',
}
const statusText = computed(() => {
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
})
const badgeVariant = computed(() => {
return props.rec.status_code === 1 ? 'default' : 'destructive'
})
</script>
<template>
<div class="flex justify-center">
<Badge :variant="badgeVariant">
{{ statusText }}
</Badge>
</div>
</template>
+12
View File
@@ -11,6 +11,7 @@ import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/custom-ui/data/dropdown-action-dud.vue'))
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
export const cols: Col[] = [
{},
@@ -110,6 +111,17 @@ export const funcComponent: RecStrFuncComponent = {
}
return res
},
status(rec, idx) {
if (rec.status === null) {
rec.status_code = 0
}
const res: RecComponent = {
idx,
rec: rec as object,
component: statusBadge,
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { Badge } from '~/components/pub/ui/badge'
const props = defineProps<{
rec: any
idx?: number
}>()
const doctorStatus = {
0: 'Tidak Aktif',
1: 'Aktif',
}
const statusText = computed(() => {
return doctorStatus[props.rec.status_code as keyof typeof doctorStatus]
})
const badgeVariant = computed(() => {
return props.rec.status_code === 1 ? 'default' : 'destructive'
})
</script>
<template>
<div class="flex justify-center">
<Badge :variant="badgeVariant">
{{ statusText }}
</Badge>
</div>
</template>
@@ -1,9 +1,9 @@
import type { Col, KeyLabel, RecComponent, RecStrFuncComponent, RecStrFuncUnknown, Th } from '../../pub/nav/types'
import type { Col, KeyLabel, RecComponent, RecStrFuncComponent, RecStrFuncUnknown, Th } from '~/components/pub/custom-ui/data/types'
import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/nav/dropdown-action-dud.vue'))
const action = defineAsyncComponent(() => import('~/components/pub/custom-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [
{},
@@ -0,0 +1,13 @@
<script setup lang="ts">
const props = defineProps<{
rec: any
idx?: number
}>()
</script>
<template>
<div class="flex flex-col justify-center">
<p class="font-semibold text-sm">{{ props.rec.patient.name }}</p>
<p class="text-xs text-muted-foreground">{{ props.rec.patient.mrn }}</p>
</div>
</template>
@@ -0,0 +1,29 @@
<script setup lang="ts">
import Badge from './badge.vue'
import { rowStatus } from './list-cfg'
const props = defineProps<{
rec: { status: number }
idx?: number
}>()
const variants = {
0: 'error',
1: 'warning',
2: 'info',
} as const
const statusText = computed(() => {
return rowStatus[props.rec.status as keyof typeof rowStatus]
})
const badgeStatus = computed((): 'error' | 'warning' | 'success' | 'info' => {
return variants[props.rec.status as keyof typeof variants] ?? 'info'
})
</script>
<template>
<div class="flex">
<Badge :status="badgeStatus" :text="statusText" />
</div>
</template>
+36
View File
@@ -0,0 +1,36 @@
<script setup lang="ts">
import { cva } from 'class-variance-authority'
import { cn } from '~/lib/utils'
interface BadgeProps {
status: 'success' | 'info' | 'warning' | 'error'
text: string
}
const props = defineProps<BadgeProps>()
const badgeVariants = cva(
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
success: 'rounded-full border-transparent bg-green-500 text-white hover:bg-green-600',
info: 'rounded-full border-transparent bg-blue-500 text-white hover:bg-blue-600',
warning: 'rounded-full border-transparent bg-yellow-500 text-white hover:bg-yellow-600',
error: 'rounded-full border-transparent bg-red-500 text-white hover:bg-red-600',
},
},
defaultVariants: {
variant: 'info',
},
},
)
</script>
<template>
<div class="flex">
<div :class="cn(badgeVariants({ variant: props.status }))">
{{ props.text }}
</div>
</div>
</template>
@@ -0,0 +1,16 @@
<script setup lang="ts">
const props = defineProps<{
text: string
icon: string
}>()
</script>
<template>
<Button
variant="outline" class="text-primary border-primary bg-white hover:bg-gray-50 focus:bg-gray-150
focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
>
<Icon :name="props.icon" class="size-4 me-2" />
{{ props.text }}
</Button>
</template>
@@ -0,0 +1,19 @@
<script setup lang="ts">
import type { Summary } from '~/components/pub/base/summary-card/type'
const props = defineProps<{
isLoading: boolean
summaryData: Summary[]
}>()
</script>
<template>
<div class="grid gap-4 md:grid-cols-2 md:gap-8 lg:grid-cols-4">
<template v-if="props.isLoading">
<PubBaseSummaryCard v-for="n in 4" :key="n" is-skeleton :stat="summaryData[n]" />
</template>
<template v-else>
<PubBaseSummaryCard v-for="card in summaryData" :key="card.title" :stat="card" />
</template>
</div>
</template>
@@ -0,0 +1,47 @@
<script setup lang="ts">
import Block from '~/components/pub/form/block.vue'
import FieldGroup from '~/components/pub/form/field-group.vue'
import Field from '~/components/pub/form/field.vue'
import Label from '~/components/pub/form/label.vue'
</script>
<template>
<form id="entry-form">
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<Icon name="i-lucide-user" class="me-2" />
<span class="font-semibold">Tambah</span> Pasien
</div>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<Block>
<FieldGroup :column="3">
<Label>Nama</Label>
<Field>
<Input type="text" name="name" />
</Field>
</FieldGroup>
<FieldGroup :column="3">
<Label>Nama</Label>
<Field>
<Input type="text" name="name" />
</Field>
</FieldGroup>
<FieldGroup :column="3">
<Label>Nomor RM</Label>
<Field>
<Input type="text" name="name" />
</Field>
</FieldGroup>
<FieldGroup>
<Label dynamic>Alamat</Label>
<Field>
<Input type="text" name="name" />
</Field>
</FieldGroup>
</Block>
</div>
<div class="my-2 flex justify-end py-2">
<PubNavFooterCsd />
</div>
</form>
</template>
+78
View File
@@ -0,0 +1,78 @@
import type { Col, KeyLabel, RecComponent, RecStrFuncComponent, RecStrFuncUnknown, Th } from '../../pub/nav/types'
import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
export const rowType = {
1: 'Patient',
2: 'Encounter',
3: 'Observation',
}
export const rowStatus = {
0: 'Gagal',
1: 'Pending',
2: 'Terkirim',
}
const patientBadge = defineAsyncComponent(() => import('./badge-patient.vue'))
const statusBadge = defineAsyncComponent(() => import('./badge-status.vue'))
export const cols: Col[] = [
{ width: 100 },
{ width: 100 },
{ width: 100 },
{ width: 100 },
{ width: 100 },
{ width: 100 },
]
export const header: Th[][] = [
[
{ label: 'ID' },
{ label: 'Jenis' },
{ label: 'Pasien' },
{ label: 'Status' },
{ label: 'Terakhir Update' },
{ label: 'FHIR ID' },
],
]
export const keys = ['id', 'resource_type', 'patient', 'status', 'updated_at', 'fhir_id']
export const delKeyNames: KeyLabel[] = [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
]
export const funcParsed: RecStrFuncUnknown = {
name: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return `${recX.firstName} ${recX.middleName || ''} ${recX.lastName || ''}`
},
}
export const funcComponent: RecStrFuncComponent = {
patient(rec, idx) {
const res: RecComponent = {
idx,
rec: rec as object,
component: patientBadge,
}
return res
},
status(rec, idx) {
const res: RecComponent = {
idx,
rec: rec as object,
component: statusBadge,
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {
patient_address(_rec) {
return '-'
},
}
+14
View File
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
defineProps<{
data: any[]
}>()
</script>
<template>
<PubBaseDataTable
:rows="data" :cols="cols" :header="header" :keys="keys" :func-parsed="funcParsed"
:func-html="funcHtml" :func-component="funcComponent"
/>
</template>
+51
View File
@@ -0,0 +1,51 @@
<script setup lang="ts">
import type { DateRange } from 'radix-vue'
import type { Ref } from 'vue'
import {
CalendarDate,
DateFormatter,
getLocalTimeZone,
} from '@internationalized/date'
import { Calendar as CalendarIcon } from 'lucide-vue-next'
import { cn } from '~/lib/utils'
const df = new DateFormatter('en-US', {
dateStyle: 'medium',
})
const value = ref({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 1, 20).add({ days: 20 }),
}) as Ref<DateRange>
</script>
<template>
<Popover>
<PopoverTrigger as-child>
<Button
variant="outline"
:class="cn(
'w-[280px] justify-start text-left font-normal',
!value && 'text-muted-foreground',
)"
>
<CalendarIcon class="mr-2 h-4 w-4" />
<template v-if="value.start">
<template v-if="value.end">
{{ df.format(value.start.toDate(getLocalTimeZone())) }} - {{ df.format(value.end.toDate(getLocalTimeZone())) }}
</template>
<template v-else>
{{ df.format(value.start.toDate(getLocalTimeZone())) }}
</template>
</template>
<template v-else>
Pick a date
</template>
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<RangeCalendar v-model="value" initial-focus :number-of-months="2" @update:start-value="(startDate) => value.start = startDate" />
</PopoverContent>
</Popover>
</template>
+12
View File
@@ -0,0 +1,12 @@
<script setup lang="ts">
import { Search } from 'lucide-vue-next'
</script>
<template>
<div class="relative w-full max-w-sm items-center">
<Input id="search" type="text" placeholder="Search..." class="pl-10" />
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-2">
<Search class="size-6 text-muted-foreground" />
</span>
</div>
</template>