delete useless pages

This commit is contained in:
Yusron alamsyah
2026-03-17 09:22:24 +07:00
parent f2ed1b2556
commit c2fa3c160b
51 changed files with 1649 additions and 8842 deletions
+167
View File
@@ -0,0 +1,167 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import AppBaseCard from '@/components/shared/AppBaseCard.vue';
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- AppBaseCard -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="AppBaseCard — Layout Dua Panel (Kiri & Kanan)">
<p class="text-body-1 mb-4">
<strong>AppBaseCard</strong> adalah komponen layout yang membagi tampilan menjadi
dua panel: panel <em>kiri</em> (sidebar) dan panel <em>kanan</em> (konten utama).
Panel kiri hanya ditampilkan pada layar besar (<code>lgAndUp</code>).
Pada layar kecil, panel kiri dapat diakses melalui drawer navigasi yang dipicu tombol Menu.
</p>
<v-alert type="info" variant="tonal" class="mb-6">
Komponen ini digunakan pada aplikasi seperti <strong>Chat</strong> dan
<strong>Kanban</strong> yang membutuhkan layout dua panel responsif.
</v-alert>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Slot</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr>
<td><code>leftpart</code></td>
<td>Konten panel kiri (lebar tetap 320px), tampil di layar besar saja</td>
</tr>
<tr>
<td><code>rightpart</code></td>
<td>Konten panel kanan (mengisi sisa lebar)</td>
</tr>
<tr>
<td><code>mobileLeftContent</code></td>
<td>Konten dalam drawer navigasi untuk tampilan mobile</td>
</tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ appBaseCardCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo Sederhana" class="mb-6">
<AppBaseCard>
<template #leftpart>
<div class="pa-4">
<p class="text-subtitle-1 font-weight-bold mb-3">Panel Kiri (Sidebar)</p>
<v-list density="compact">
<v-list-item
v-for="item in ['Item 1', 'Item 2', 'Item 3']"
:key="item"
:title="item"
prepend-icon="mdi-circle-small"
/>
</v-list>
</div>
</template>
<template #rightpart>
<div class="pa-4">
<p class="text-subtitle-1 font-weight-bold mb-3">Panel Kanan (Konten Utama)</p>
<p class="text-body-2 text-medium-emphasis">
Ini adalah area konten utama. Pada layar kecil, panel kiri akan
disembunyikan dan dapat dibuka melalui tombol "Menu" yang muncul di atas.
</p>
<v-alert type="success" variant="tonal" class="mt-3">
Coba perkecil browser untuk melihat perilaku responsif!
</v-alert>
</div>
</template>
<template #mobileLeftContent>
<div class="pa-4">
<p class="text-subtitle-1 font-weight-bold mb-3">Menu Mobile</p>
<v-list density="compact">
<v-list-item
v-for="item in ['Item 1', 'Item 2', 'Item 3']"
:key="item"
:title="item"
prepend-icon="mdi-circle-small"
/>
</v-list>
</div>
</template>
</AppBaseCard>
</UiChildCard>
<UiChildCard title="Tips Penggunaan" class="mb-6">
<v-list lines="two">
<v-list-item
prepend-icon="mdi-cellphone"
title="Responsif otomatis"
subtitle="Panel kiri hilang di layar kecil dan muncul sebagai drawer nav ketika tombol Menu diklik."
/>
<v-list-item
prepend-icon="mdi-resize"
title="Lebar panel kiri tetap"
subtitle="Panel kiri memiliki lebar 320px yang tidak bisa diubah. Sesuaikan konten Anda dengan lebar ini."
/>
<v-list-item
prepend-icon="mdi-content-copy"
title="Duplikasi konten mobile"
subtitle="Slot mobileLeftContent harus memuat konten yang sama dengan leftpart agar konsisten di semua ukuran layar."
/>
</v-list>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
appBaseCardCode: `<template>
<AppBaseCard>
<!-- Panel kiri: daftar kontak, list, dsb. -->
<template #leftpart>
<div class="pa-4">
<v-list>
<v-list-item
v-for="contact in contacts"
:key="contact.id"
:title="contact.name"
@click="selectContact(contact)"
/>
</v-list>
</div>
</template>
<!-- Panel kanan: detail/percakapan/konten utama -->
<template #rightpart>
<div class="pa-4">
<h5>Detail Kontak</h5>
<p>{{ selectedContact?.name }}</p>
</div>
</template>
<!-- Konten drawer untuk mobile (sama dengan leftpart) -->
<template #mobileLeftContent>
<div class="pa-4">
<v-list>
<v-list-item
v-for="contact in contacts"
:key="contact.id"
:title="contact.name"
@click="selectContact(contact)"
/>
</v-list>
</div>
</template>
</AppBaseCard>
</template>
<script setup lang="ts">
import AppBaseCard from '@/components/shared/AppBaseCard.vue';
<\/script>`,
};
}
};
</script>
+142
View File
@@ -0,0 +1,142 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import BaseBreadcrumb from '@/components/shared/BaseBreadcrumb.vue';
const breadcrumbBasic = [
{ text: 'Dashboard', disabled: false, href: '/' },
{ text: 'Shared Components', disabled: true }
];
const breadcrumbDeep = [
{ text: 'Dashboard', disabled: false, href: '/' },
{ text: 'Apps', disabled: false, href: '/apps' },
{ text: 'Pengguna', disabled: false, href: '/apps/user' },
{ text: 'Profil', disabled: true }
];
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- BaseBreadcrumb -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="BaseBreadcrumb — Header Halaman dengan Breadcrumb">
<p class="text-body-1 mb-4">
<strong>BaseBreadcrumb</strong> adalah komponen header halaman yang menampilkan
<em>judul halaman</em> di sebelah kiri dan <em>navigasi breadcrumb</em> di sebelah kanan.
Setiap item breadcrumb ditampilkan sebagai chip berwarna primary. Ikon rumah (home)
otomatis ditambahkan di awal breadcrumb.
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop</th><th>Tipe</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr>
<td><code>title</code></td>
<td>String</td>
<td>Judul halaman yang ditampilkan di sebelah kiri</td>
</tr>
<tr>
<td><code>breadcrumbs</code></td>
<td>Array</td>
<td>
Array objek breadcrumb. Setiap objek: <code>{ text, disabled, href }</code>
</td>
</tr>
</tbody>
</v-table>
<UiChildCard title="Struktur Data breadcrumbs" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ breadcrumbDataStructure }}</pre>
</UiChildCard>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ breadcrumbCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo — Breadcrumb 2 Level" class="mb-6">
<BaseBreadcrumb
title="Shared Components"
:breadcrumbs="breadcrumbBasic"
/>
<p class="text-caption text-medium-emphasis mt-n2 mb-0">
BaseBreadcrumb di atas merupakan contoh dengan 2 level navigasi.
</p>
</UiChildCard>
<UiChildCard title="Demo — Breadcrumb 4 Level" class="mb-6">
<BaseBreadcrumb
title="Profil Pengguna"
:breadcrumbs="breadcrumbDeep"
/>
<p class="text-caption text-medium-emphasis mt-n2 mb-0">
BaseBreadcrumb dengan 4 level navigasi (Dashboard Apps Pengguna Profil).
</p>
</UiChildCard>
<UiChildCard title="Tips Penggunaan" class="mb-6">
<v-alert type="info" variant="tonal" class="mb-3">
<strong>Posisi terbaik:</strong> Tempatkan <code>BaseBreadcrumb</code> sebagai
elemen pertama di dalam <code>&lt;template&gt;</code> halaman, sebelum konten utama.
Biasanya diletakkan di luar <code>&lt;v-row&gt;</code> konten.
</v-alert>
<v-alert type="warning" variant="tonal">
Item breadcrumb terakhir sebaiknya diberi <code>disabled: true</code> untuk
menandakan halaman saat ini (tidak dapat diklik).
</v-alert>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
breadcrumbDataStructure: `// Struktur setiap item breadcrumbs:
const breadcrumbs = [
{
text: 'Dashboard', // Label yang ditampilkan
disabled: false, // false = dapat diklik (ada href)
href: '/' // Target navigasi
},
{
text: 'Halaman Ini',
disabled: true // true = tidak dapat diklik (halaman aktif)
// href tidak diperlukan jika disabled: true
}
]`,
breadcrumbCode: `<template>
<!-- Letakkan di awal halaman, sebelum konten utama -->
<BaseBreadcrumb
title="Judul Halaman"
:breadcrumbs="breadcrumbs"
/>
<!-- Konten halaman di bawahnya -->
<v-row>
<v-col cols="12">
<!-- ... -->
</v-col>
</v-row>
</template>
<script setup lang="ts">
import BaseBreadcrumb from '@/components/shared/BaseBreadcrumb.vue';
const breadcrumbs = [
{ text: 'Dashboard', disabled: false, href: '/' },
{ text: 'Apps', disabled: false, href: '/apps' },
{ text: 'Halaman Ini', disabled: true }
];
<\/script>`,
};
}
};
</script>
+230
View File
@@ -0,0 +1,230 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import CardHeaderFooter from '@/components/shared/CardHeaderFooter.vue';
import UiTableCard from '@/components/shared/UiTableCard.vue';
import UiParentCardLogo from '@/components/shared/UiParentCardLogo.vue';
const tableHeaders = [
{ title: 'Nama', key: 'name' },
{ title: 'Email', key: 'email' },
{ title: 'Status', key: 'status' },
];
const tableItems = [
{ name: 'Budi Santoso', email: 'budi@email.com', status: 'Aktif' },
{ name: 'Siti Rahayu', email: 'siti@email.com', status: 'Nonaktif' },
{ name: 'Ahmad Fauzi', email: 'ahmad@email.com', status: 'Aktif' },
];
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- CardHeaderFooter -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="CardHeaderFooter — Card dengan Header dan Footer">
<p class="text-body-1 mb-4">
<strong>CardHeaderFooter</strong> adalah card outlined yang menyediakan area
<em>header</em> (judul), <em>konten</em> (slot default), dan <em>footer</em> (slot footer)
yang dipisahkan oleh garis pembatas.
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop</th><th>Tipe</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul card di bagian header</td></tr>
</tbody>
</v-table>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Slot</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Konten utama antara header dan footer</td></tr>
<tr><td><code>footer</code></td><td>Konten footer (tombol aksi, info, dsb.)</td></tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ cardHeaderFooterCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo" class="mb-6">
<v-row>
<v-col cols="12" md="6">
<CardHeaderFooter title="Form Pendaftaran">
<v-text-field label="Nama Lengkap" variant="outlined" density="compact" class="mb-3" />
<v-text-field label="Email" variant="outlined" density="compact" />
<template #footer>
<v-btn color="primary">Simpan</v-btn>
<v-btn variant="text" class="ml-2">Batal</v-btn>
</template>
</CardHeaderFooter>
</v-col>
<v-col cols="12" md="6">
<CardHeaderFooter title="Konfirmasi Hapus">
<v-alert type="warning" variant="tonal" class="mb-0">
Apakah Anda yakin ingin menghapus data ini?
</v-alert>
<template #footer>
<v-spacer />
<v-btn color="error" class="mr-2">Hapus</v-btn>
<v-btn variant="outlined">Batal</v-btn>
</template>
</CardHeaderFooter>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
<!-- ============================================================= -->
<!-- UiTableCard -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="UiTableCard — Card Wrapper untuk Tabel">
<p class="text-body-1 mb-4">
<strong>UiTableCard</strong> adalah card khusus sebagai wrapper tabel data. Slot
<code>default</code> ditempatkan langsung setelah divider (tanpa padding ekstra)
agar tabel bisa full-width sesuai card.
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop</th><th>Tipe</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul di atas tabel</td></tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ uiTableCardCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo" class="mb-6">
<UiTableCard title="Daftar Pengguna">
<v-data-table
:headers="tableHeaders"
:items="tableItems"
hide-default-footer
density="compact"
/>
</UiTableCard>
</UiChildCard>
</UiParentCard>
</v-col>
<!-- ============================================================= -->
<!-- UiParentCardLogo -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="UiParentCardLogo — Card dengan Logo Aplikasi">
<p class="text-body-1 mb-4">
<strong>UiParentCardLogo</strong> sama seperti UiParentCard tetapi menampilkan
<em>Logo aplikasi</em> (komponen <code>Logo.vue</code>) di header alih-alih teks judul.
Cocok digunakan pada halaman auth (login, register, dsb.).
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Slot</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Konten utama di bawah logo</td></tr>
<tr><td><code>action</code></td><td>Aksi di sebelah kanan logo</td></tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ uiParentCardLogoCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo">
<v-row justify="center">
<v-col cols="12" md="5">
<UiParentCardLogo>
<template #action>
<v-chip color="primary" size="small">v1.0</v-chip>
</template>
<p class="text-body-2 text-center mt-2">
Silakan masuk untuk melanjutkan. Slot <code>default</code> berisi
form login atau konten lainnya.
</p>
</UiParentCardLogo>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
cardHeaderFooterCode: `<template>
<CardHeaderFooter title="Form Pendaftaran">
<!-- Slot default: konten di antara header & footer -->
<v-text-field label="Nama Lengkap" variant="outlined" />
<v-text-field label="Email" variant="outlined" />
<!-- Slot footer: tombol aksi -->
<template #footer>
<v-btn color="primary">Simpan</v-btn>
<v-btn variant="text">Batal</v-btn>
</template>
</CardHeaderFooter>
</template>
<script setup lang="ts">
import CardHeaderFooter from '@/components/shared/CardHeaderFooter.vue';
<\/script>`,
uiTableCardCode: `<template>
<UiTableCard title="Daftar Pengguna">
<!-- Letakkan v-data-table atau v-table langsung sebagai slot default -->
<v-data-table
:headers="headers"
:items="items"
hide-default-footer
/>
</UiTableCard>
</template>
<script setup lang="ts">
import UiTableCard from '@/components/shared/UiTableCard.vue';
const headers = [
{ title: 'Nama', key: 'name' },
{ title: 'Email', key: 'email' },
];
const items = [
{ name: 'Budi', email: 'budi@email.com' },
];
<\/script>`,
uiParentCardLogoCode: `<template>
<!-- Cocok untuk halaman auth / landing -->
<UiParentCardLogo>
<template #action>
<v-chip color="primary" size="small">v1.0</v-chip>
</template>
<!-- Konten form login/register di sini -->
<LoginForm />
</UiParentCardLogo>
</template>
<script setup lang="ts">
import UiParentCardLogo from '@/components/shared/UiParentCardLogo.vue';
<\/script>`,
};
}
};
</script>
+158
View File
@@ -0,0 +1,158 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- UiParentCard -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="UiParentCard — Elevated Card dengan Title">
<p class="text-body-1 mb-4">
<strong>UiParentCard</strong> adalah card dengan <code>elevation="10"</code> yang biasanya digunakan sebagai
<em>wrapper utama</em> pada halaman. Mendukung slot <code>default</code> (konten) dan slot
<code>action</code> (tombol/aksi di kanan atas header).
</p>
<!-- Props Table -->
<v-table density="compact" class="mb-6">
<thead>
<tr>
<th>Prop</th>
<th>Tipe</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul yang ditampilkan di header card</td></tr>
</tbody>
</v-table>
<!-- Slots Table -->
<v-table density="compact" class="mb-6">
<thead>
<tr>
<th>Slot</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Konten utama di dalam card</td></tr>
<tr><td><code>action</code></td><td>Konten aksi di kanan atas (misal: tombol)</td></tr>
</tbody>
</v-table>
<!-- Code Example -->
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ uiParentCardCode }}</pre>
</UiChildCard>
<!-- Live Demo -->
<UiChildCard title="Demo">
<UiParentCard title="Judul Card Saya">
<template #action>
<v-btn color="primary" size="small">Aksi</v-btn>
</template>
<p class="text-body-2">Ini adalah konten di dalam UiParentCard. Gunakan slot <strong>default</strong> untuk mengisi konten.</p>
</UiParentCard>
</UiChildCard>
</UiParentCard>
</v-col>
<!-- ============================================================= -->
<!-- UiChildCard -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="UiChildCard — Outlined Card dengan Title">
<p class="text-body-1 mb-4">
<strong>UiChildCard</strong> adalah card ber-outline (tanpa elevation) yang biasanya digunakan
<em>di dalam</em> UiParentCard untuk memisahkan bagian-bagian konten.
</p>
<!-- Props Table -->
<v-table density="compact" class="mb-6">
<thead>
<tr>
<th>Prop</th>
<th>Tipe</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul section di dalam card</td></tr>
</tbody>
</v-table>
<!-- Code Example -->
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ uiChildCardCode }}</pre>
</UiChildCard>
<!-- Live Demo -->
<UiChildCard title="Demo">
<v-row>
<v-col cols="12" md="6">
<UiChildCard title="Section A">
<p class="text-body-2">Konten section A.</p>
</UiChildCard>
</v-col>
<v-col cols="12" md="6">
<UiChildCard title="Section B">
<p class="text-body-2">Konten section B.</p>
</UiChildCard>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
uiParentCardCode: `<template>
<UiParentCard title="Judul Card Saya">
<!-- Slot action: tombol/aksi di kanan atas -->
<template #action>
<v-btn color="primary" size="small">Aksi</v-btn>
</template>
<!-- Slot default: konten utama -->
<p>Konten di sini...</p>
</UiParentCard>
</template>
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
<\/script>`,
uiChildCardCode: `<template>
<!-- Biasanya dipakai di dalam UiParentCard -->
<UiParentCard title="Halaman Saya">
<v-row>
<v-col cols="12" md="6">
<UiChildCard title="Section A">
<p>Konten section A</p>
</UiChildCard>
</v-col>
<v-col cols="12" md="6">
<UiChildCard title="Section B">
<p>Konten section B</p>
</UiChildCard>
</v-col>
</v-row>
</UiParentCard>
</template>
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
<\/script>`,
};
}
};
</script>
@@ -0,0 +1,170 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import UiTextfieldPrimary from '@/components/shared/UiTextfieldPrimary.vue';
const searchText = '';
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- UiTextfieldPrimary -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="UiTextfieldPrimary — Text Field Bercorak Primary">
<p class="text-body-1 mb-4">
<strong>UiTextfieldPrimary</strong> adalah wrapper tipis di atas <code>v-text-field</code>
Vuetify yang secara otomatis menerapkan <code>color="primary"</code>.
Gunakan <code>v-model</code> dan atribut lainnya langsung seperti pada
<code>v-text-field</code> biasa, dan manfaatkan slot default untuk label/ikon khusus.
</p>
<v-alert type="info" variant="tonal" class="mb-6">
Komponen ini menggunakan <strong>slot default</strong> (bukan prop <code>label</code>)
untuk menyisipkan konten. Untuk label teks biasa, cukup tulis teks di dalam tag.
Untuk atribut seperti <code>v-model</code>, <code>placeholder</code>,
<code>variant</code>, dan lainnya, gunakan langsung pada tag
<code>&lt;UiTextfieldPrimary&gt;</code>.
</v-alert>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop/Atribut</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr>
<td><code>v-model</code></td>
<td>Binding dua arah untuk nilai input</td>
</tr>
<tr>
<td><code>label</code>, <code>placeholder</code></td>
<td>Diteruskan langsung ke <code>v-text-field</code></td>
</tr>
<tr>
<td><code>variant</code></td>
<td>Gaya tampilan: <code>outlined</code>, <code>filled</code>, <code>underlined</code>, dll.</td>
</tr>
<tr>
<td><code>density</code></td>
<td><code>default</code>, <code>comfortable</code>, <code>compact</code></td>
</tr>
<tr>
<td><code>prepend-inner-icon</code>, <code>append-inner-icon</code></td>
<td>Ikon di dalam field</td>
</tr>
<tr>
<td>Slot <code>default</code></td>
<td>Konten/label tambahan yang disisipkan ke dalam field</td>
</tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ textfieldCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo — Berbagai Varian" class="mb-6">
<v-row>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2">Outlined (default Vuetify)</p>
<UiTextfieldPrimary
label="Nama Lengkap"
variant="outlined"
placeholder="Masukkan nama..."
/>
</v-col>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2">Filled</p>
<UiTextfieldPrimary
label="Email"
variant="filled"
placeholder="contoh@email.com"
/>
</v-col>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2">Underlined</p>
<UiTextfieldPrimary
label="Kata Sandi"
type="password"
variant="underlined"
/>
</v-col>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2">Dengan Ikon & Density Compact</p>
<UiTextfieldPrimary
label="Cari..."
variant="outlined"
density="compact"
prepend-inner-icon="mdi-magnify"
/>
</v-col>
</v-row>
</UiChildCard>
<UiChildCard title="Perbandingan: UiTextfieldPrimary vs v-text-field biasa" class="mb-6">
<v-row>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2 text-primary">UiTextfieldPrimary (warna primary otomatis)</p>
<UiTextfieldPrimary
label="Field Primary"
variant="outlined"
/>
</v-col>
<v-col cols="12" md="6">
<p class="text-subtitle-2 mb-2">v-text-field biasa (tanpa warna)</p>
<v-text-field
label="Field Biasa"
variant="outlined"
/>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
textfieldCode: `<template>
<!-- Penggunaan dasar -->
<UiTextfieldPrimary
v-model="email"
label="Email"
variant="outlined"
placeholder="contoh@email.com"
/>
<!-- Dengan ikon -->
<UiTextfieldPrimary
v-model="search"
label="Cari..."
variant="outlined"
density="compact"
prepend-inner-icon="mdi-magnify"
/>
<!-- Dengan type password -->
<UiTextfieldPrimary
v-model="password"
label="Kata Sandi"
type="password"
variant="outlined"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import UiTextfieldPrimary from '@/components/shared/UiTextfieldPrimary.vue';
const email = ref('');
const search = ref('');
const password = ref('');
<\/script>`,
};
}
};
</script>
+187
View File
@@ -0,0 +1,187 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import WidgetCard from '@/components/shared/WidgetCard.vue';
import WidgetCardv2 from '@/components/shared/WidgetCardv2.vue';
</script>
<template>
<v-row>
<!-- ============================================================= -->
<!-- WidgetCard -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="WidgetCard — Card Widget Sederhana">
<p class="text-body-1 mb-4">
<strong>WidgetCard</strong> adalah card outlined tanpa elevation yang dirancang untuk widget/statistik.
Memiliki margin bawah otomatis (<code>mb-6</code>) dan slot <code>default</code> untuk kontennya.
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop</th><th>Tipe</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul widget card</td></tr>
</tbody>
</v-table>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Slot</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Konten utama widget</td></tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ widgetCardCode }}</pre>
</UiChildCard>
<UiChildCard title="Demo">
<v-row>
<v-col cols="12" md="4">
<WidgetCard title="Total Pengguna">
<div class="d-flex align-center ga-2">
<v-icon color="primary" size="36">mdi-account-group</v-icon>
<span class="text-h4 font-weight-bold">1,250</span>
</div>
<p class="text-caption text-medium-emphasis mt-1">+12% dari bulan lalu</p>
</WidgetCard>
</v-col>
<v-col cols="12" md="4">
<WidgetCard title="Total Penjualan">
<div class="d-flex align-center ga-2">
<v-icon color="success" size="36">mdi-cash</v-icon>
<span class="text-h4 font-weight-bold">Rp 45jt</span>
</div>
<p class="text-caption text-medium-emphasis mt-1">+8% dari bulan lalu</p>
</WidgetCard>
</v-col>
<v-col cols="12" md="4">
<WidgetCard title="Total Order">
<div class="d-flex align-center ga-2">
<v-icon color="warning" size="36">mdi-cart</v-icon>
<span class="text-h4 font-weight-bold">320</span>
</div>
<p class="text-caption text-medium-emphasis mt-1">+5% dari bulan lalu</p>
</WidgetCard>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
<!-- ============================================================= -->
<!-- WidgetCardv2 -->
<!-- ============================================================= -->
<v-col cols="12">
<UiParentCard title="WidgetCardv2 — Card Widget dengan Footer">
<p class="text-body-1 mb-4">
<strong>WidgetCardv2</strong> adalah versi lanjutan dari WidgetCard dengan dukungan slot
<code>footer</code> untuk menambahkan aksi/info di bawah konten. Footer dapat
disembunyikan menggunakan prop <code>hideaction</code>.
</p>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Prop</th><th>Tipe</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>title</code></td><td>String</td><td>Judul widget</td></tr>
<tr><td><code>hideaction</code></td><td>Boolean</td><td>Jika <code>true</code>, footer disembunyikan</td></tr>
</tbody>
</v-table>
<v-table density="compact" class="mb-6">
<thead>
<tr><th>Slot</th><th>Keterangan</th></tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Konten utama</td></tr>
<tr><td><code>footer</code></td><td>Konten footer (aksi/info tambahan)</td></tr>
</tbody>
</v-table>
<UiChildCard title="Contoh Penggunaan" class="mb-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ widgetCardv2Code }}</pre>
</UiChildCard>
<UiChildCard title="Demo — Dengan Footer">
<v-row>
<v-col cols="12" md="6">
<WidgetCardv2 title="Laporan Bulan Ini">
<div class="d-flex align-center ga-2 mb-2">
<v-icon color="info" size="32">mdi-chart-bar</v-icon>
<span class="text-h5 font-weight-bold">75%</span>
<span class="text-body-2 text-medium-emphasis">target tercapai</span>
</div>
<v-progress-linear model-value="75" color="info" rounded height="8" />
<template #footer>
<v-btn variant="text" color="primary" size="small">Lihat Detail</v-btn>
<v-spacer />
<span class="text-caption text-medium-emphasis">Update: hari ini</span>
</template>
</WidgetCardv2>
</v-col>
<v-col cols="12" md="6">
<WidgetCardv2 title="Tanpa Footer" :hideaction="true">
<p class="text-body-2">
Prop <code>hideaction="true"</code> menyembunyikan area footer
meskipun slot footer telah diisi.
</p>
<template #footer>
<span>Footer ini tidak akan terlihat</span>
</template>
</WidgetCardv2>
</v-col>
</v-row>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
widgetCardCode: `<template>
<WidgetCard title="Total Pengguna">
<div class="d-flex align-center ga-2">
<v-icon color="primary" size="36">mdi-account-group</v-icon>
<span class="text-h4 font-weight-bold">1,250</span>
</div>
<p class="text-caption mt-1">+12% dari bulan lalu</p>
</WidgetCard>
</template>
<script setup lang="ts">
import WidgetCard from '@/components/shared/WidgetCard.vue';
<\/script>`,
widgetCardv2Code: `<template>
<WidgetCardv2 title="Laporan Bulan Ini">
<!-- Slot default: konten utama -->
<div>Konten widget di sini...</div>
<!-- Slot footer: aksi/info di bawah -->
<template #footer>
<v-btn variant="text" color="primary" size="small">Lihat Detail</v-btn>
</template>
</WidgetCardv2>
<!-- Sembunyikan footer dengan prop hideaction -->
<WidgetCardv2 title="Tanpa Footer" :hideaction="true">
<div>Footer tidak tampil.</div>
</WidgetCardv2>
</template>
<script setup lang="ts">
import WidgetCardv2 from '@/components/shared/WidgetCardv2.vue';
<\/script>`,
};
}
};
</script>
+282
View File
@@ -0,0 +1,282 @@
<script setup lang="ts">
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
const components = [
{
name: 'UiParentCard',
file: 'UiParentCard.vue',
category: 'Card',
description: 'Card elevated (elevation=10) sebagai wrapper utama halaman. Mendukung slot action di header.',
props: ['title'],
slots: ['default', 'action'],
link: '/shared-components/UiParentCard',
},
{
name: 'UiChildCard',
file: 'UiChildCard.vue',
category: 'Card',
description: 'Card outlined untuk memisahkan bagian-bagian konten di dalam UiParentCard.',
props: ['title'],
slots: ['default'],
link: '/shared-components/UiParentCard',
},
{
name: 'WidgetCard',
file: 'WidgetCard.vue',
category: 'Widget',
description: 'Card outlined ringan untuk widget/statistik. Margin bawah otomatis (mb-6).',
props: ['title'],
slots: ['default'],
link: '/shared-components/WidgetCards',
},
{
name: 'WidgetCardv2',
file: 'WidgetCardv2.vue',
category: 'Widget',
description: 'Widget card dengan footer yang bisa disembunyikan menggunakan prop hideaction.',
props: ['title', 'hideaction'],
slots: ['default', 'footer'],
link: '/shared-components/WidgetCards',
},
{
name: 'CardHeaderFooter',
file: 'CardHeaderFooter.vue',
category: 'Card',
description: 'Card dengan header, konten, dan footer yang dipisahkan garis pembatas.',
props: ['title'],
slots: ['default', 'footer'],
link: '/shared-components/CardComponents',
},
{
name: 'UiTableCard',
file: 'UiTableCard.vue',
category: 'Card',
description: 'Card wrapper untuk tabel data. Slot default langsung menerima v-data-table.',
props: ['title'],
slots: ['default'],
link: '/shared-components/CardComponents',
},
{
name: 'UiParentCardLogo',
file: 'UiParentCardLogo.vue',
category: 'Card',
description: 'Card elevated dengan Logo aplikasi di header. Ideal untuk halaman auth.',
props: [],
slots: ['default', 'action'],
link: '/shared-components/CardComponents',
},
{
name: 'BaseBreadcrumb',
file: 'BaseBreadcrumb.vue',
category: 'Navigation',
description: 'Header halaman dengan judul di kiri dan breadcrumb chip-style di kanan.',
props: ['title', 'breadcrumbs'],
slots: [],
link: '/shared-components/BaseBreadcrumb',
},
{
name: 'UiTextfieldPrimary',
file: 'UiTextfieldPrimary.vue',
category: 'Form',
description: 'Wrapper v-text-field dengan color="primary" otomatis.',
props: ['v-model', '...all v-text-field props'],
slots: ['default'],
link: '/shared-components/UiTextfieldPrimary',
},
{
name: 'AppBaseCard',
file: 'AppBaseCard.vue',
category: 'Layout',
description: 'Layout dua panel (kiri/kanan) responsif. Panel kiri menjadi drawer di mobile.',
props: [],
slots: ['leftpart', 'rightpart', 'mobileLeftContent'],
link: '/shared-components/AppBaseCard',
},
{
name: 'AppEmailCard',
file: 'AppEmailCard.vue',
category: 'Layout',
description: 'Layout tiga panel (compose/list/detail) untuk antarmuka email.',
props: [],
slots: ['mailCompose', 'mailList', 'mailDetail'],
link: '/shared-components/AppBaseCard',
},
];
const categoryColors: Record<string, string> = {
Card: 'primary',
Widget: 'success',
Navigation: 'info',
Form: 'warning',
Layout: 'secondary',
};
const categories = [...new Set(components.map(c => c.category))];
</script>
<template>
<v-row>
<v-col cols="12">
<UiParentCard title="Shared Components — Panduan Penggunaan">
<p class="text-body-1 mb-6">
Direktori <code>components/shared/</code> berisi komponen-komponen yang dapat digunakan
kembali di seluruh aplikasi. Gunakan komponen ini secara konsisten untuk menjaga
tampilan yang seragam.
</p>
<!-- Summary Stats -->
<v-row class="mb-6">
<v-col v-for="category in categories" :key="category" cols="6" md="4" lg="2">
<v-card variant="tonal" :color="categoryColors[category]" class="text-center pa-3">
<div class="text-h5 font-weight-bold">
{{ components.filter(c => c.category === category).length }}
</div>
<div class="text-caption">{{ category }}</div>
</v-card>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-card variant="tonal" color="error" class="text-center pa-3">
<div class="text-h5 font-weight-bold">{{ components.length }}</div>
<div class="text-caption">Total</div>
</v-card>
</v-col>
</v-row>
<!-- Component Cards -->
<v-row>
<v-col
v-for="comp in components"
:key="comp.name"
cols="12"
md="6"
lg="4"
>
<v-card
variant="outlined"
height="100%"
class="d-flex flex-column"
>
<v-card-item>
<template #prepend>
<v-chip
:color="categoryColors[comp.category]"
size="small"
variant="tonal"
class="mr-2"
>
{{ comp.category }}
</v-chip>
</template>
<v-card-title class="text-body-1 font-weight-bold">
{{ comp.name }}
</v-card-title>
<v-card-subtitle class="text-caption">
{{ comp.file }}
</v-card-subtitle>
</v-card-item>
<v-divider />
<v-card-text class="flex-grow-1">
<p class="text-body-2 mb-3">{{ comp.description }}</p>
<!-- Props -->
<div v-if="comp.props.length" class="mb-2">
<span class="text-caption font-weight-bold text-medium-emphasis">PROPS: </span>
<v-chip
v-for="prop in comp.props"
:key="prop"
size="x-small"
color="primary"
variant="tonal"
class="mr-1"
>
{{ prop }}
</v-chip>
</div>
<!-- Slots -->
<div v-if="comp.slots.length">
<span class="text-caption font-weight-bold text-medium-emphasis">SLOTS: </span>
<v-chip
v-for="slot in comp.slots"
:key="slot"
size="x-small"
color="secondary"
variant="tonal"
class="mr-1"
>
#{{ slot }}
</v-chip>
</div>
</v-card-text>
<v-divider />
<v-card-actions>
<v-btn
:to="comp.link"
color="primary"
variant="text"
size="small"
append-icon="mdi-arrow-right"
>
Lihat Tutorial
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<!-- Quick Start -->
<UiChildCard title="Quick Start — Import Komponen" class="mt-6">
<pre class="bg-grey-lighten-4 pa-4 rounded text-body-2 overflow-auto">{{ quickStartCode }}</pre>
</UiChildCard>
<!-- Best Practice -->
<UiChildCard title="Best Practice" class="mt-4">
<v-list lines="two">
<v-list-item
prepend-icon="mdi-check-circle"
title="Gunakan UiParentCard sebagai wrapper halaman"
subtitle="Setiap halaman sebaiknya dibungkus dengan UiParentCard untuk konsistensi elevation dan border."
/>
<v-list-item
prepend-icon="mdi-check-circle"
title="Gunakan UiChildCard untuk sub-section"
subtitle="Pisahkan setiap bagian konten dalam UiChildCard agar lebih terstruktur."
/>
<v-list-item
prepend-icon="mdi-check-circle"
title="Gunakan BaseBreadcrumb di awal halaman"
subtitle="Tempatkan BaseBreadcrumb sebagai elemen pertama untuk navigasi yang konsisten."
/>
<v-list-item
prepend-icon="mdi-check-circle"
title="Gunakan UiTextfieldPrimary alih-alih v-text-field"
subtitle="Untuk konsistensi warna primary di seluruh form aplikasi."
/>
</v-list>
</UiChildCard>
</UiParentCard>
</v-col>
</v-row>
</template>
<script lang="ts">
export default {
data() {
return {
quickStartCode: `// Import individual sesuai kebutuhan:
import UiParentCard from '@/components/shared/UiParentCard.vue';
import UiChildCard from '@/components/shared/UiChildCard.vue';
import WidgetCard from '@/components/shared/WidgetCard.vue';
import WidgetCardv2 from '@/components/shared/WidgetCardv2.vue';
import CardHeaderFooter from '@/components/shared/CardHeaderFooter.vue';
import UiTableCard from '@/components/shared/UiTableCard.vue';
import UiParentCardLogo from '@/components/shared/UiParentCardLogo.vue';
import BaseBreadcrumb from '@/components/shared/BaseBreadcrumb.vue';
import UiTextfieldPrimary from '@/components/shared/UiTextfieldPrimary.vue';
import AppBaseCard from '@/components/shared/AppBaseCard.vue';
import AppEmailCard from '@/components/shared/AppEmailCard.vue';`,
};
}
};
</script>