first commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<v-empty-state
|
||||
headline="Whoops, 404"
|
||||
title="Page not found"
|
||||
text="The page you were looking for does not exist"
|
||||
icon="custom:nustar"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
icon: 'mdi-security',
|
||||
title: 'Auth',
|
||||
drawerIndex: 4,
|
||||
})
|
||||
|
||||
const { data, status, getCsrfToken, getProviders, signOut } = useAuth()
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
const providers = await getProviders()
|
||||
const csrfToken = await getCsrfToken()
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
// const returnTo = encodeURIComponent('http://localhost:3000/auth/login');
|
||||
const returnTo = encodeURIComponent(window.location.origin);
|
||||
|
||||
const logoutUrl = `${runtimeConfig.public.keycloakIssuer}/protocol/openid-connect/logout?client_id=${runtimeConfig.public.keycloakClient}&post_logout_redirect_uri=${returnTo}`;
|
||||
window.open(logoutUrl, '_blank'); // Sign out dari aplikasi sebelum redirect
|
||||
await signOut({ callbackUrl: '/auth/login' });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-item>
|
||||
<v-card-title>Authentication Overview</v-card-title>
|
||||
<v-card-subtitle>See all available authentication & session information
|
||||
below</v-card-subtitle>
|
||||
</v-card-item>
|
||||
|
||||
<v-card-text>
|
||||
<pre v-if="status"><span>Status:</span> {{ status }}</pre>
|
||||
<pre v-if="data"><span>Data:</span> {{ data }}</pre>
|
||||
<pre v-if="csrfToken"><span>CSRF Token:</span> {{ csrfToken }}</pre>
|
||||
<pre v-if="providers"><span>Providers:</span> {{ providers }}</pre>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-btn text="Logout" @click="handleLogout"></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<!-- <VMain class="main d-flex align-center justify-center"> -->
|
||||
<VRow no-gutters class="main">
|
||||
<VCol cols="6" class="d-flex align-center justify-center">
|
||||
<VCard class="card" title="Login Area" width="400" rounded="lg">
|
||||
<VCardText>
|
||||
<VBtn v-for="provider in providers" :key="provider.id" @click="signIn(provider.id)" class="mb-8" color="blue"
|
||||
size="large" block>
|
||||
Sign in with {{ provider.name }}
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<!-- </VMain> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import { useField, useForm } from 'vee-validate'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'auth',
|
||||
auth: {
|
||||
unauthenticatedOnly: true,
|
||||
navigateAuthenticatedTo: '/homepage',
|
||||
},
|
||||
})
|
||||
|
||||
const { signIn, getProviders } = useAuth()
|
||||
const providers = await getProviders()
|
||||
|
||||
// const { handleSubmit, handleReset } = useForm({
|
||||
// validationSchema: {
|
||||
// username (value) {
|
||||
// if (value?.length >= 2) return true
|
||||
|
||||
// return 'Name needs to be at least 2 characters.'
|
||||
// },
|
||||
// password (value) {
|
||||
// return true
|
||||
// if (value?.length > 9 && /[0-9-]+/.test(value)) return true
|
||||
|
||||
// return 'Phone number needs to be at least 9 digits.'
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
// const username = useField('username')
|
||||
// const password = useField('password')
|
||||
|
||||
// const submit = handleSubmit(values => {
|
||||
// alert(JSON.stringify(values, null, 2))
|
||||
// })
|
||||
|
||||
// const show = ref(false)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
min-height: 300px;
|
||||
background-image: url('/background.jpg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.card {
|
||||
/* From https://css.glass */
|
||||
background: rgba(0, 0, 0, 0.28);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(11.1px);
|
||||
-webkit-backdrop-filter: blur(11.1px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
icon: 'mdi-monitor-dashboard',
|
||||
title: 'Dashboard',
|
||||
drawerIndex: 1,
|
||||
})
|
||||
const stats = ref([
|
||||
{
|
||||
icon: 'mdi-web',
|
||||
title: 'Bandwidth',
|
||||
value: 23,
|
||||
unit: 'GB',
|
||||
color: 'primary',
|
||||
caption: 'Up: 13, Down: 10',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-rss',
|
||||
title: 'Submissions',
|
||||
value: 108,
|
||||
color: 'primary',
|
||||
caption: 'Too young, too naive',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-send',
|
||||
title: 'Requests',
|
||||
value: 1238,
|
||||
color: 'warning',
|
||||
caption: 'Limit: 1320',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-bell',
|
||||
title: 'Messages',
|
||||
value: 9042,
|
||||
color: 'primary',
|
||||
caption: 'Warnings: 300, erros: 47',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-github',
|
||||
title: 'Github Stars',
|
||||
value: NaN,
|
||||
color: 'grey',
|
||||
caption: 'API has no response',
|
||||
},
|
||||
{
|
||||
icon: 'mdi-currency-cny',
|
||||
title: 'Total Fee',
|
||||
value: 2300,
|
||||
unit: '¥',
|
||||
color: 'error',
|
||||
caption: 'Upper Limit: 2000 ¥',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="stat in stats"
|
||||
:key="stat.title"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
lg="2"
|
||||
>
|
||||
<StatsCard
|
||||
:title="stat.title"
|
||||
:unit="stat.unit"
|
||||
:color="stat.color"
|
||||
:icon="stat.icon"
|
||||
:value="stat.value"
|
||||
>
|
||||
<template #footer>
|
||||
{{ stat.caption }}
|
||||
</template>
|
||||
</StatsCard>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" lg="12">
|
||||
<v-card class="pa-2">
|
||||
<ChartLine />
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" lg="4">
|
||||
<v-card class="pa-2">
|
||||
<ChartRadar />
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" lg="4">
|
||||
<v-card class="pa-2">
|
||||
<ChartPie />
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6" lg="4">
|
||||
<v-card class="pa-2">
|
||||
<ChartBar />
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-card:not(.stats-card) {
|
||||
height: 340px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Forms Routes',
|
||||
icon: 'mdi-view-list',
|
||||
drawerIndex: 4,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<NuxtPage />
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<IndexPage />
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script>
|
||||
import { VCard } from 'vuetify/components'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Vue Form',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 2,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<v-card
|
||||
class="mx-auto"
|
||||
prepend-icon="mdi-account-injury-outline"
|
||||
subtitle="Isi dan lengkapi dengan data yang sesuai"
|
||||
width="600"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<span class="font-weight-black">Formulir Pasien Baru</span>
|
||||
</template>
|
||||
|
||||
<v-card-text class="bg-surface-light pt-4">
|
||||
<FormPatient />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script>
|
||||
import { VCard } from 'vuetify/components'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Practitioner Basic',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 4,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<v-card
|
||||
class="mx-auto"
|
||||
prepend-icon="mdi-account-injury-outline"
|
||||
subtitle="Isi dan lengkapi dengan data yang sesuai"
|
||||
width="80%"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<span class="font-weight-black">Formulir Praktisi Baru</span>
|
||||
</template>
|
||||
|
||||
<v-card-text class="bg-surface-light pt-4">
|
||||
<FormPractitionerBasic />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Vue Form',
|
||||
icon: 'mdi-checkbox-blank-off-outline',
|
||||
drawerIndex: 0,
|
||||
})
|
||||
|
||||
const data = ref({})
|
||||
const humanName = ref({})
|
||||
const onChange = () => {
|
||||
humanName.value = parseName(data.value.nama)
|
||||
}
|
||||
|
||||
const handleResponse = (response, form$) => {
|
||||
console.log(response) // axios response
|
||||
console.log(response.status) // HTTP status code
|
||||
console.log(response.data) // response data
|
||||
|
||||
console.log(form$) // <Vueform> instance
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ma-4">
|
||||
<Vueform
|
||||
v-model="data"
|
||||
@change="onChange"
|
||||
endpoint="/api/practitioner/test"
|
||||
method="post"
|
||||
@response="handleResponse"
|
||||
sync
|
||||
>
|
||||
<TextElement
|
||||
name="nama"
|
||||
placeholder="Nama Lengkap"
|
||||
:rules="['required', 'min:3', 'max:100']"
|
||||
/>
|
||||
<StaticElement name="parsed-name" size="sm">
|
||||
<div class="d-flex flex-row">
|
||||
<v-chip
|
||||
v-for="prefix in humanName.prefix"
|
||||
size="x-small"
|
||||
class="mr-1 bg-indigo-lighten-3"
|
||||
>{{ prefix }}</v-chip
|
||||
><v-chip
|
||||
v-for="given in humanName.given"
|
||||
size="x-small"
|
||||
class="mr-1 bg-blue-darken-3"
|
||||
>{{ given }}</v-chip
|
||||
>
|
||||
<v-chip
|
||||
v-show="humanName.family"
|
||||
size="x-small"
|
||||
class="mr-1 bg-blue"
|
||||
>{{ humanName.family }}</v-chip
|
||||
>
|
||||
<v-chip
|
||||
v-for="suffix in humanName.suffix"
|
||||
size="x-small"
|
||||
class="mr-1 bg-indigo-lighten-3"
|
||||
>{{ suffix }}</v-chip
|
||||
>
|
||||
</div>
|
||||
</StaticElement>
|
||||
<RadiogroupElement
|
||||
name="gender"
|
||||
view="tabs"
|
||||
label="Jenis Kelamin"
|
||||
:items="[
|
||||
{
|
||||
value: 'unknown',
|
||||
label: '⭕',
|
||||
},
|
||||
{
|
||||
value: 'male',
|
||||
label: '♂️ Laki-laki',
|
||||
},
|
||||
{
|
||||
value: 'female',
|
||||
label: '♀️ Perempuan',
|
||||
},
|
||||
{
|
||||
value: 'other',
|
||||
label: '⚧️ Lainnya',
|
||||
},
|
||||
]"
|
||||
default="unknown"
|
||||
/>
|
||||
|
||||
<ButtonElement
|
||||
name="submit"
|
||||
button-label="Submit"
|
||||
:submits="true"
|
||||
align="right"
|
||||
/>
|
||||
</Vueform>
|
||||
</div>
|
||||
<span>{{ data }}</span>
|
||||
</template>
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
<script setup lang="ts">
|
||||
import type { UseFuseOptions } from './index'
|
||||
import { computed, shallowRef, watch } from 'vue'
|
||||
import { useFuse } from './index'
|
||||
|
||||
interface DataItem {
|
||||
firstName: string
|
||||
lastName: string
|
||||
}
|
||||
|
||||
const data = shallowRef<DataItem[]>([
|
||||
{
|
||||
firstName: 'Roslyn',
|
||||
lastName: 'Mitchell',
|
||||
},
|
||||
{
|
||||
firstName: 'Cathleen',
|
||||
lastName: 'Matthews',
|
||||
},
|
||||
{
|
||||
firstName: 'Carleton',
|
||||
lastName: 'Harrelson',
|
||||
},
|
||||
{
|
||||
firstName: 'Allen',
|
||||
lastName: 'Moores',
|
||||
},
|
||||
{
|
||||
firstName: 'John',
|
||||
lastName: 'Washington',
|
||||
},
|
||||
{
|
||||
firstName: 'Brooke',
|
||||
lastName: 'Colton',
|
||||
},
|
||||
{
|
||||
firstName: 'Mary',
|
||||
lastName: 'Rennold',
|
||||
},
|
||||
{
|
||||
firstName: 'Nanny',
|
||||
lastName: 'Field',
|
||||
},
|
||||
{
|
||||
firstName: 'Chasity',
|
||||
lastName: 'Michael',
|
||||
},
|
||||
{
|
||||
firstName: 'Oakley',
|
||||
lastName: 'Giles',
|
||||
},
|
||||
{
|
||||
firstName: 'Johanna',
|
||||
lastName: 'Shepherd',
|
||||
},
|
||||
{
|
||||
firstName: 'Maybelle',
|
||||
lastName: 'Wilkie',
|
||||
},
|
||||
{
|
||||
firstName: 'Dawson',
|
||||
lastName: 'Rowntree',
|
||||
},
|
||||
{
|
||||
firstName: 'Manley',
|
||||
lastName: 'Pond',
|
||||
},
|
||||
{
|
||||
firstName: 'Lula',
|
||||
lastName: 'Sawyer',
|
||||
},
|
||||
{
|
||||
firstName: 'Hudson',
|
||||
lastName: 'Hext',
|
||||
},
|
||||
{
|
||||
firstName: 'Alden',
|
||||
lastName: 'Senior',
|
||||
},
|
||||
{
|
||||
firstName: 'Tory',
|
||||
lastName: 'Hyland',
|
||||
},
|
||||
{
|
||||
firstName: 'Constance',
|
||||
lastName: 'Josephs',
|
||||
},
|
||||
{
|
||||
firstName: 'Larry',
|
||||
lastName: 'Kinsley',
|
||||
},
|
||||
])
|
||||
|
||||
const search = shallowRef('')
|
||||
const filterBy = shallowRef('both')
|
||||
const keys = computed(() => {
|
||||
if (filterBy.value === 'first') return ['firstName']
|
||||
else if (filterBy.value === 'last') return ['lastName']
|
||||
else return ['firstName', 'lastName']
|
||||
})
|
||||
|
||||
const resultLimit = shallowRef<number | undefined>(undefined)
|
||||
const resultLimitString = shallowRef<string>('')
|
||||
watch(resultLimitString, () => {
|
||||
if (resultLimitString.value === '') {
|
||||
resultLimit.value = undefined
|
||||
} else {
|
||||
const float = Number.parseFloat(resultLimitString.value)
|
||||
if (!Number.isNaN(float)) {
|
||||
resultLimit.value = Math.round(float)
|
||||
resultLimitString.value = resultLimit.value.toString()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const exactMatch = shallowRef(false)
|
||||
const isCaseSensitive = shallowRef(false)
|
||||
const matchAllWhenSearchEmpty = shallowRef(true)
|
||||
|
||||
const options = computed<UseFuseOptions<DataItem>>(() => ({
|
||||
fuseOptions: {
|
||||
keys: keys.value,
|
||||
isCaseSensitive: isCaseSensitive.value,
|
||||
threshold: exactMatch.value ? 0 : undefined,
|
||||
},
|
||||
resultLimit: resultLimit.value,
|
||||
matchAllWhenSearchEmpty: matchAllWhenSearchEmpty.value,
|
||||
}))
|
||||
|
||||
const { results } = useFuse(search, data, options)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<input
|
||||
v-model="search"
|
||||
placeholder="Search for someone..."
|
||||
type="text"
|
||||
w-full
|
||||
/>
|
||||
<div flex flex-wrap>
|
||||
<div
|
||||
bg="dark:(dark-300) light-700"
|
||||
mr-2
|
||||
border="1 light-900 dark:(dark-700)"
|
||||
rounded
|
||||
relative
|
||||
flex
|
||||
items-center
|
||||
>
|
||||
<i i-carbon-filter absolute left-2 opacity-70 />
|
||||
<select v-model="filterBy" px-8 bg-transparent>
|
||||
<option bg="dark:(dark-300) light-700" value="both">Full Name</option>
|
||||
<option bg="dark:(dark-300) light-700" value="first">
|
||||
First Name
|
||||
</option>
|
||||
<option bg="dark:(dark-300) light-700" value="last">Last Name</option>
|
||||
</select>
|
||||
<i
|
||||
i-carbon-chevron-down
|
||||
absolute
|
||||
right-2
|
||||
pointer-events-none
|
||||
opacity-70
|
||||
/>
|
||||
</div>
|
||||
<span flex-1 />
|
||||
<div flex flex-row flex-wrap gap-x-4>
|
||||
<label class="checkbox">
|
||||
<input v-model="exactMatch" type="checkbox" />
|
||||
<span>Exact Match</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input v-model="isCaseSensitive" type="checkbox" />
|
||||
<span>Case Sensitive</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input v-model="matchAllWhenSearchEmpty" type="checkbox" />
|
||||
<span>Match all when empty</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mt-4>
|
||||
<template v-if="results.length > 0">
|
||||
<div
|
||||
v-for="result in results"
|
||||
:key="result.item.firstName + result.item.lastName"
|
||||
py-2
|
||||
>
|
||||
<div flex flex-col>
|
||||
<span> {{ result.item.firstName }} {{ result.item.lastName }} </span>
|
||||
<span text-sm opacity-50> Score Index: {{ result.refIndex }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div text-center pt-8 pb-4 opacity-80>No Results Found</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
input {
|
||||
--tw-ring-offset-width: 1px !important;
|
||||
--tw-ring-color: #8885 !important;
|
||||
--tw-ring-offset-color: transparent !important;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
@apply inline-flex items-center my-auto cursor-pointer select-none;
|
||||
}
|
||||
|
||||
.checkbox input {
|
||||
appearance: none;
|
||||
padding: 0;
|
||||
-webkit-print-color-adjust: exact;
|
||||
color-adjust: exact;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-origin: border-box;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
@apply bg-gray-400/30;
|
||||
@apply rounded-md h-4 w-4 select-none;
|
||||
}
|
||||
|
||||
.checkbox input:checked {
|
||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.checkbox span {
|
||||
@apply ml-1.5 text-13px opacity-70;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
const name = ref('')
|
||||
function sayHi() {
|
||||
Notify.success(`Hi, ${name.value}!`)
|
||||
}
|
||||
function warning() {
|
||||
Notify.warning(`How dare you refuse me, ${name.value}.`)
|
||||
}
|
||||
definePageMeta({
|
||||
icon: 'mdi-home',
|
||||
title: 'Homepage',
|
||||
drawerIndex: 0,
|
||||
})
|
||||
// const { user } = useUserSession()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container
|
||||
fluid
|
||||
class="d-flex align-items-center justify-center fill-height"
|
||||
>
|
||||
<div class="text-center">
|
||||
<!-- <div>Welcome {{ user?.login }}!</div> -->
|
||||
<v-icon
|
||||
icon="custom:vitify-nuxt"
|
||||
size="3em"
|
||||
color="primary"
|
||||
class="mb-4"
|
||||
/>
|
||||
<p>Opinionated Starter Template</p>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
max-width="300"
|
||||
placeholder="Hello World"
|
||||
label="What's your name?"
|
||||
class="mt-8"
|
||||
/>
|
||||
<v-btn :disabled="!name" class="mr-2" color="primary" @click="sayHi">
|
||||
Confirm
|
||||
</v-btn>
|
||||
<v-btn :disabled="!name" @click="warning"> Cancel </v-btn>
|
||||
<VIcon icon="i-simple-icons-keycloak" />
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
redirect: 'homepage',
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Nested Routes',
|
||||
icon: 'mdi-view-list',
|
||||
drawerIndex: 2,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<NuxtPage />
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<IndexPage />
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { useFormTest } from '~/composables/forms/test'
|
||||
import { JsonForms } from '@jsonforms/vue'
|
||||
import { vuetifyRenderers } from '@jsonforms/vue-vuetify'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Test Form',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
|
||||
const { schema, uischema, data } = useFormTest()
|
||||
|
||||
const onChange = (event: JsonFormsChangeEvent) => {
|
||||
data.value = event.data
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img alt="Vue logo" src="./assets/logo.png" />
|
||||
<h1>JSON Forms Vue 3</h1>
|
||||
<div class="myform">
|
||||
<json-forms
|
||||
:data="data"
|
||||
:renderers="vuetifyRenderers"
|
||||
:schema="schema"
|
||||
:uischema="uischema"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
<pre>{{ data }}</pre>
|
||||
<!-- <pre>{{ typeof schema }}</pre>
|
||||
<pre>{{ schema }}</pre> -->
|
||||
</template>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { JsonForms } from '@jsonforms/vue'
|
||||
import { vuetifyRenderers } from '@jsonforms/vue-vuetify'
|
||||
import { useFormBasic } from '~/composables/forms/basic'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Basic Form',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
|
||||
const { schema, uischema, data } = useFormBasic()
|
||||
|
||||
const onChange = (event: JsonFormsChangeEvent) => {
|
||||
data.value = event.data
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img alt="Vue logo" src="./assets/logo.png" />
|
||||
<h1>JSON Forms Vue 3</h1>
|
||||
<div class="myform">
|
||||
<json-forms
|
||||
:data="data"
|
||||
:renderers="vuetifyRenderers"
|
||||
:schema="schema"
|
||||
:uischema="uischema"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
<pre>{{ data }}</pre>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script>
|
||||
definePageMeta({
|
||||
title: 'Menu 3',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
</script>
|
||||
<template></template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<IndexPage />
|
||||
</template>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Menu1 Form',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
|
||||
const api_data = useFetch('/api/forms/basic/data')
|
||||
const api_i18n = useFetch('/api/forms/basic/i18n')
|
||||
const api_schema = useFetch('/api/forms/basic/schema')
|
||||
const api_uischema = useFetch('/api/forms/basic/uischema')
|
||||
|
||||
// In Vue 3, markRaw is used directly without needing to import it from a specific package
|
||||
// const renderers = markRaw([
|
||||
// ...extendedVuetifyRenderers,
|
||||
// // here you can add custom renderers
|
||||
// ]);
|
||||
|
||||
// Using ref for reactive data in Vue 3
|
||||
const data = ref(api_data) // You'll need to replace this with your actual data
|
||||
const schema = ref(api_schema) // Replace with your actual schema
|
||||
const uischema = ref(api_uischema) // Replace with your actual UI schema
|
||||
|
||||
// Event handlers are defined as functions in the setup
|
||||
// const onChange = (event) => {
|
||||
// data.value = event.data;
|
||||
// };
|
||||
|
||||
const demo = {
|
||||
properties: {
|
||||
firstName: {
|
||||
type: 'string',
|
||||
description: "The person's first name.",
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
description: "The person's last name.",
|
||||
},
|
||||
age: {
|
||||
description: 'Age in years which must be equal to or greater than zero.',
|
||||
type: 'integer',
|
||||
minimum: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var form = document.getElementById('vuetify-json-forms')
|
||||
// form.setAttribute("schema", JSON.stringify(demo));
|
||||
|
||||
// type="module"
|
||||
// src="https://cdn.jsdelivr.net/npm/@chobantonov/jsonforms-vuetify-webcomponent@3.5.1/dist/vuetify-json-forms.min.js"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<vuetify-json-forms id="vuetify-json-forms"></vuetify-json-forms>
|
||||
<v-container fluid> {{ data.data }} </v-container>
|
||||
<v-container fluid> {{ api_i18n.data }} </v-container>
|
||||
<v-container fluid> {{ schema.data }} </v-container>
|
||||
<v-container fluid> {{ uischema.data }} </v-container>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Menu 2-2',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<v-container fluid> empty page </v-container>
|
||||
</template>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Vuetify Form',
|
||||
icon: 'mdi-animation',
|
||||
})
|
||||
|
||||
import { ref, provide, onBeforeMount } from 'vue'
|
||||
import { JsonForms } from '@jsonforms/vue'
|
||||
|
||||
import {
|
||||
vuetifyRenderers,
|
||||
defaultStyles,
|
||||
mergeStyles,
|
||||
} from '@jsonforms/vue-vuetify'
|
||||
|
||||
const renderers = Object.freeze([
|
||||
...vuetifyRenderers,
|
||||
// here you can add custom renderers
|
||||
])
|
||||
|
||||
var uischema = useFetch('/api/forms/example/uischema').data
|
||||
var schema = useFetch('/api/forms/example/schema').data
|
||||
|
||||
console.log(JSON.parse(schema.trim))
|
||||
|
||||
// onBeforeMount(async () => {
|
||||
// uischema = JSON.stringify(useFetch('/api/forms/example/uischema').data)
|
||||
// console.log(uischema)
|
||||
// // schema = JSON.stringify(useFetch('/api/forms/example/schema'))
|
||||
// // console.log(schema)
|
||||
// })
|
||||
|
||||
const data = ref({
|
||||
name: 'Send email to Adrian',
|
||||
description: 'Confirm if you have passed the subject\nHereby ...',
|
||||
done: true,
|
||||
recurrence: 'Daily',
|
||||
rating: 3,
|
||||
})
|
||||
|
||||
const onChange = (event: JsonFormsChangeEvent) => {
|
||||
data.value = event.data
|
||||
}
|
||||
|
||||
// mergeStyles combines all classes from both styles definitions into one
|
||||
const myStyles = mergeStyles(defaultStyles, { control: { label: 'mylabel' } })
|
||||
|
||||
// Provide styles to child components
|
||||
provide('styles', myStyles)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img alt="Vue logo" src="./assets/logo.png" />
|
||||
<h1>JSON Forms Vue 3</h1>
|
||||
<!-- <div class="myform">
|
||||
<json-forms
|
||||
:data="data"
|
||||
:renderers="renderers"
|
||||
:schema="schema"
|
||||
:uischema="uischema"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div> -->
|
||||
<pre>{{ data }}</pre>
|
||||
<pre>{{ typeof schema }}</pre>
|
||||
<pre>{{ schema }}</pre>
|
||||
</template>
|
||||
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Pasien',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 4,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<NuxtPage />
|
||||
</template>
|
||||
@@ -0,0 +1,111 @@
|
||||
<script>
|
||||
|
||||
definePageMeta({
|
||||
title: 'Data Pasien',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 0,
|
||||
})
|
||||
const randomData = ref(generateRandomDataPasien(10));
|
||||
|
||||
const headers = [
|
||||
{ text: 'No RM' },
|
||||
{ text: 'Nama' },
|
||||
{ text: 'tanggal Lahir' },
|
||||
{ text: 'Alamat' },
|
||||
{ text: 'No KTP' },
|
||||
{ text: 'No JKN' },
|
||||
{ text: 'Kelamin' },
|
||||
{ text: 'Aksi' },
|
||||
// { text: 'Aksi' },
|
||||
];
|
||||
|
||||
|
||||
const searchQuery = ref('');
|
||||
const filteredData = computed(() => {
|
||||
if (searchQuery.value.length >= 3) {
|
||||
return randomData.value.filter(item =>
|
||||
item.norm.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
item.no_ktp.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
||||
item.nama.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
);
|
||||
}
|
||||
return randomData.value;
|
||||
});
|
||||
|
||||
console.log(randomData.value)
|
||||
console.log(randomData.value.length)
|
||||
</script>
|
||||
<template>
|
||||
<v-container>
|
||||
<v-card>
|
||||
<div class="py-4 px-3">
|
||||
<div class="d-flex justify-space-between mb-6">
|
||||
<h5 class="text-h5 ">Kunjungan Pasien</h5>
|
||||
<div class="w-sm-25">
|
||||
<v-text-field label="Cari" v-model="searchField" variant="outlined" density="compact" type="text"
|
||||
hide-details color="primary"></v-text-field>
|
||||
</div>
|
||||
</div>
|
||||
<v-row>
|
||||
<div class="d-flex flex-wrap">
|
||||
<!-- <v-col v-for="item in filteredData" :key="item.id" cols="12" md="4">
|
||||
<v-lazy :min-height="200" :options="{ 'threshold': 1 }" transition="fade-transition">
|
||||
<v-card>
|
||||
<template v-slot:append>
|
||||
<h3>{{ item.norm }}</h3>
|
||||
</template>
|
||||
|
||||
<template v-slot:prepend>
|
||||
<v-avatar size="50" class="rounded-md bg-lightsecondary">
|
||||
<Icon icon="solar:user-circle-broken" class="text-secondary" height="36" />
|
||||
</v-avatar>
|
||||
</template>
|
||||
|
||||
<template v-slot:title>
|
||||
<h4>{{ capitalizeEachWord(item.nama) }}</h4>
|
||||
</template>
|
||||
|
||||
<template v-slot:subtitle>
|
||||
<p>{{ item.no_ktp }}</p>
|
||||
</template>
|
||||
|
||||
<template v-slot:text>
|
||||
<p>{{ capitalizeEachWord(item.alamat) }}</p>
|
||||
</template>
|
||||
|
||||
<template v-slot:actions>
|
||||
<div class="d-flex justify-end">
|
||||
<v-btn color="primary" @click="editItem(item.norm)">Ubah</v-btn>
|
||||
<v-btn color="error" @click="deleteItem(item.norm)">Hapus</v-btn>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</v-card>
|
||||
</v-lazy>
|
||||
</v-col> -->
|
||||
<div v-for="(data, i) in filteredData" :key="i">
|
||||
<p>s</p>
|
||||
|
||||
</div>
|
||||
<v-card class="mx-4">
|
||||
<template v-slot:prepend>
|
||||
left
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
right
|
||||
</template>
|
||||
</v-card>
|
||||
<v-card>
|
||||
<template v-slot:prepend>
|
||||
left
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
right
|
||||
</template>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<IndexPage />
|
||||
</template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script>
|
||||
|
||||
definePageMeta({
|
||||
title: 'Kunjungan Pasien',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 1,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<pre>dddddddd</pre>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { VCard } from 'vuetify/components'
|
||||
|
||||
definePageMeta({
|
||||
title: 'Tambah Pasien',
|
||||
icon: 'mdi-account-injury-outline',
|
||||
drawerIndex: 2,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<v-card class="mx-auto" prepend-icon="mdi-account-injury-outline" subtitle="Isi dan lengkapi dengan data yang sesuai"
|
||||
width="80%">
|
||||
<template v-slot:title>
|
||||
<span class="font-weight-black">Formulir Pasien Baru</span>
|
||||
</template>
|
||||
|
||||
<v-card-text class="bg-surface-light pt-4">
|
||||
<FormPatientCreate />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -0,0 +1,234 @@
|
||||
<script setup lang="ts">
|
||||
import type { DataTableHeaders } from '~/plugins/vuetify'
|
||||
|
||||
definePageMeta({
|
||||
icon: 'mdi-table',
|
||||
title: 'Data Praktisi',
|
||||
drawerIndex: 3,
|
||||
})
|
||||
|
||||
const search = ref('')
|
||||
const dialogDelete = useTemplateRef('dialogDelete')
|
||||
function showDialogDelete(name: string) {
|
||||
dialogDelete.value
|
||||
?.open('Are you sure you want to delete this dessert?')
|
||||
.then(async (confirmed: boolean) => {
|
||||
if (confirmed) {
|
||||
try {
|
||||
const index = desserts.value!.findIndex((v) => v.name === name)
|
||||
desserts.value!.splice(index, 1)
|
||||
Notify.success('Deleted')
|
||||
} catch (e) {
|
||||
Notify.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const practitioners = ref([])
|
||||
const loading = ref(true)
|
||||
const error = ref(null)
|
||||
const snackbar = ref({
|
||||
show: false,
|
||||
text: '',
|
||||
color: 'info',
|
||||
})
|
||||
|
||||
// Mengambil data dari API
|
||||
const fetchPractitioners = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
const response = await fetch('/api/practitioner')
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.status} - ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
console.log(data)
|
||||
practitioners.value = data
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch practitioners:', err)
|
||||
error.value = `Gagal memuat data praktisi: ${err.message}`
|
||||
showSnackbar('Gagal memuat data praktisi', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Mendapatkan nomor telepon praktisi
|
||||
const getPractitionerPhone = (practitioner) => {
|
||||
if (!practitioner.telecom || practitioner.telecom.length === 0) {
|
||||
return 'Tidak ada data'
|
||||
}
|
||||
|
||||
const phone = practitioner.telecom.find((t) => t.system === 'phone')
|
||||
return phone ? phone.value : 'Tidak ada data'
|
||||
}
|
||||
|
||||
// Mendapatkan email praktisi
|
||||
const getPractitionerEmail = (practitioner) => {
|
||||
if (!practitioner.telecom || practitioner.telecom.length === 0) {
|
||||
return 'Tidak ada data'
|
||||
}
|
||||
|
||||
const email = practitioner.telecom.find((t) => t.system === 'email')
|
||||
return email ? email.value : 'Tidak ada data'
|
||||
}
|
||||
|
||||
// Menampilkan detail praktisi
|
||||
const showDetail = (practitioner) => {
|
||||
console.log('Menampilkan detail praktisi:', getPractitionerName(practitioner))
|
||||
showSnackbar(`Detail ${getPractitionerName(practitioner)}`, 'primary')
|
||||
// Di sini bisa ditambahkan navigasi ke halaman detail atau membuka dialog
|
||||
}
|
||||
|
||||
// Menampilkan snackbar
|
||||
const showSnackbar = (text, color = 'success') => {
|
||||
snackbar.value = {
|
||||
show: true,
|
||||
text,
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
// Panggil API saat komponen dimuat
|
||||
onMounted(() => {
|
||||
fetchPractitioners()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card>
|
||||
<client-only>
|
||||
<teleport to="#app-bar">
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
label="Search"
|
||||
single-line
|
||||
hide-details
|
||||
density="compact"
|
||||
class="mr-2"
|
||||
rounded="xl"
|
||||
flat
|
||||
variant="solo"
|
||||
style="width: 250px"
|
||||
/>
|
||||
</teleport>
|
||||
</client-only>
|
||||
<v-container>
|
||||
<h1 class="text-h4 mb-4">Daftar Praktisi</h1>
|
||||
|
||||
<v-alert v-if="error" type="error" class="mb-4">
|
||||
{{ error }}
|
||||
</v-alert>
|
||||
|
||||
<v-progress-circular
|
||||
v-if="loading"
|
||||
indeterminate
|
||||
color="primary"
|
||||
size="64"
|
||||
class="my-8 mx-auto d-block"
|
||||
></v-progress-circular>
|
||||
|
||||
<v-row v-else>
|
||||
<v-col
|
||||
v-for="(practitioner, index) in practitioners"
|
||||
:key="index"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
>
|
||||
<v-card
|
||||
class="mx-auto mb-4"
|
||||
max-width="400"
|
||||
elevation="3"
|
||||
shaped
|
||||
>
|
||||
<v-card-title class="text-h5">
|
||||
{{ joinName(practitioner.name, ['official', 'usual']) }}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-row align="center" class="mx-0">
|
||||
<DivAvatar
|
||||
size="80"
|
||||
class="mr-3"
|
||||
:gender="practitioner.gender"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<DivIconText
|
||||
icon="mdi-map-marker"
|
||||
:text="practitioner.birthPlace || 'Tidak ada data'"
|
||||
/>
|
||||
<DivIconText
|
||||
icon="mdi-calendar"
|
||||
:text="formatDate(practitioner.birthDate, 'full')"
|
||||
/>
|
||||
<DivIconText
|
||||
icon="mdi-phone"
|
||||
:text="
|
||||
getContactPoints(
|
||||
practitioner.telecom,
|
||||
'phone',
|
||||
).join(',')
|
||||
"
|
||||
/>
|
||||
<DivIconText
|
||||
icon="mdi-email"
|
||||
:text="
|
||||
getContactPoints(
|
||||
practitioner.telecom,
|
||||
'email',
|
||||
).join(',')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="primary"
|
||||
text
|
||||
@click="showDetail(practitioner)"
|
||||
>
|
||||
Detail
|
||||
<v-icon small class="ml-1">mdi-arrow-right</v-icon>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-snackbar
|
||||
v-model="snackbar.show"
|
||||
:color="snackbar.color"
|
||||
:timeout="3000"
|
||||
>
|
||||
{{ snackbar.text }}
|
||||
</v-snackbar>
|
||||
</v-container>
|
||||
<DialogConfirm ref="dialogDelete" />
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-card {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.v-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
</style>
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
<script setup lang="ts">
|
||||
import type { DataTableHeaders } from '~/plugins/vuetify'
|
||||
|
||||
definePageMeta({
|
||||
icon: 'mdi-table',
|
||||
title: 'Data Table',
|
||||
drawerIndex: 3,
|
||||
})
|
||||
|
||||
const search = ref('')
|
||||
const dialogDelete = useTemplateRef('dialogDelete')
|
||||
function showDialogDelete(name: string) {
|
||||
dialogDelete.value
|
||||
?.open('Are you sure you want to delete this dessert?')
|
||||
.then(async (confirmed: boolean) => {
|
||||
if (confirmed) {
|
||||
try {
|
||||
const index = desserts.value!.findIndex((v) => v.name === name)
|
||||
desserts.value!.splice(index, 1)
|
||||
Notify.success('Deleted')
|
||||
} catch (e) {
|
||||
Notify.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const headers: DataTableHeaders = [
|
||||
{
|
||||
title: 'Dessert (100g serving)',
|
||||
key: 'name',
|
||||
},
|
||||
{ title: 'Calories', key: 'calories' },
|
||||
{ title: 'Fat (g)', key: 'fat' },
|
||||
{ title: 'Carbs (g)', key: 'carbs' },
|
||||
{ title: 'Protein (g)', key: 'protein' },
|
||||
{ title: 'Iron (%)', key: 'iron' },
|
||||
{ title: 'Actions', key: 'actions', sortable: false },
|
||||
]
|
||||
const desserts = ref([
|
||||
{
|
||||
name: 'Frozen Yogurt',
|
||||
calories: 159,
|
||||
fat: 6.0,
|
||||
carbs: 24,
|
||||
protein: 4.0,
|
||||
iron: '1',
|
||||
},
|
||||
{
|
||||
name: 'Jelly bean',
|
||||
calories: 375,
|
||||
fat: 0.0,
|
||||
carbs: 94,
|
||||
protein: 0.0,
|
||||
iron: '0',
|
||||
},
|
||||
{
|
||||
name: 'KitKat',
|
||||
calories: 518,
|
||||
fat: 26.0,
|
||||
carbs: 65,
|
||||
protein: 7,
|
||||
iron: '6',
|
||||
},
|
||||
{
|
||||
name: 'Eclair',
|
||||
calories: 262,
|
||||
fat: 16.0,
|
||||
carbs: 23,
|
||||
protein: 6.0,
|
||||
iron: '7',
|
||||
},
|
||||
{
|
||||
name: 'Gingerbread',
|
||||
calories: 356,
|
||||
fat: 16.0,
|
||||
carbs: 49,
|
||||
protein: 3.9,
|
||||
iron: '16',
|
||||
},
|
||||
{
|
||||
name: 'Ice cream sandwich',
|
||||
calories: 237,
|
||||
fat: 9.0,
|
||||
carbs: 37,
|
||||
protein: 4.3,
|
||||
iron: '1',
|
||||
},
|
||||
{
|
||||
name: 'Lollipop',
|
||||
calories: 392,
|
||||
fat: 0.2,
|
||||
carbs: 98,
|
||||
protein: 0,
|
||||
iron: '2',
|
||||
},
|
||||
{
|
||||
name: 'Cupcake',
|
||||
calories: 305,
|
||||
fat: 3.7,
|
||||
carbs: 67,
|
||||
protein: 4.3,
|
||||
iron: '8',
|
||||
},
|
||||
{
|
||||
name: 'Honeycomb',
|
||||
calories: 408,
|
||||
fat: 3.2,
|
||||
carbs: 87,
|
||||
protein: 6.5,
|
||||
iron: '45',
|
||||
},
|
||||
{
|
||||
name: 'Donut',
|
||||
calories: 452,
|
||||
fat: 25.0,
|
||||
carbs: 51,
|
||||
protein: 4.9,
|
||||
iron: '22',
|
||||
},
|
||||
{
|
||||
name: 'Donut2',
|
||||
calories: 452,
|
||||
fat: 25.0,
|
||||
carbs: 51,
|
||||
protein: 4.9,
|
||||
iron: '22',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card>
|
||||
<client-only>
|
||||
<teleport to="#app-bar">
|
||||
<v-text-field
|
||||
v-model="search"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
label="Search"
|
||||
single-line
|
||||
hide-details
|
||||
density="compact"
|
||||
class="mr-2"
|
||||
rounded="xl"
|
||||
flat
|
||||
variant="solo"
|
||||
style="width: 250px"
|
||||
/>
|
||||
</teleport>
|
||||
</client-only>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="desserts"
|
||||
item-value="name"
|
||||
:search="search"
|
||||
>
|
||||
<template #item.actions="{ item }">
|
||||
<v-defaults-provider
|
||||
:defaults="{
|
||||
VBtn: {
|
||||
size: 20,
|
||||
rounded: 'sm',
|
||||
variant: 'text',
|
||||
class: 'ml-1',
|
||||
color: '',
|
||||
},
|
||||
VIcon: {
|
||||
size: 20,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<v-btn
|
||||
v-tooltip="{ text: 'Delete', location: 'top' }"
|
||||
icon="mdi-delete-outline"
|
||||
@click.stop="showDialogDelete(item.name)"
|
||||
/>
|
||||
</v-defaults-provider>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<DialogConfirm ref="dialogDelete" />
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
Reference in New Issue
Block a user