initial: prepare folder structure
This commit is contained in:
21
src/components/common/AppHeader.vue
Normal file
21
src/components/common/AppHeader.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button />
|
||||
</ion-buttons>
|
||||
|
||||
<ion-title>APLIKASI PRODUK</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonButtons,
|
||||
IonMenuButton
|
||||
} from '@ionic/vue'
|
||||
</script>
|
||||
44
src/components/common/AppSidebar.vue
Normal file
44
src/components/common/AppSidebar.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<ion-menu content-id="main-content" type="overlay">
|
||||
<ion-header>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-title>KDM Dashboard</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-menu-toggle :auto-hide="true">
|
||||
|
||||
<ion-item router-link="/" router-direction="root">
|
||||
<ion-icon :icon="homeOutline" slot="start" />
|
||||
<ion-label>Dashboard</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item router-link="/products">
|
||||
<ion-icon :icon="cubeOutline" slot="start" />
|
||||
<ion-label>Products</ion-label>
|
||||
</ion-item>
|
||||
|
||||
</ion-menu-toggle>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IonMenu,
|
||||
IonHeader,
|
||||
IonToolbar,
|
||||
IonTitle,
|
||||
IonContent,
|
||||
IonList,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonIcon,
|
||||
IonMenuToggle
|
||||
} from '@ionic/vue'
|
||||
|
||||
import { homeOutline, cubeOutline } from 'ionicons/icons'
|
||||
</script>
|
||||
29
src/components/common/StatusBadge.vue
Normal file
29
src/components/common/StatusBadge.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<span
|
||||
class="text-xs font-medium px-3 py-1 rounded-full"
|
||||
:class="badgeClass"
|
||||
>
|
||||
{{ status }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
status: String
|
||||
})
|
||||
|
||||
const badgeClass = computed(() => {
|
||||
switch (props.status) {
|
||||
case 'Digunakan':
|
||||
return 'bg-green-100 text-green-600'
|
||||
case 'Maintenance':
|
||||
return 'bg-yellow-100 text-yellow-700'
|
||||
case 'Ready to use':
|
||||
return 'bg-blue-100 text-blue-600'
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-600'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
29
src/components/product/ProductCard.vue
Normal file
29
src/components/product/ProductCard.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<ion-card
|
||||
button
|
||||
@click="$router.push('/products')"
|
||||
class="rounded-2xl shadow-md p-3 text-center"
|
||||
>
|
||||
<div class="h-20 bg-gray-100 rounded-lg mb-3 flex items-center justify-center">
|
||||
<span class="text-gray-400 text-sm">PHOTO</span>
|
||||
</div>
|
||||
|
||||
<h3 class="font-semibold text-sm mb-2">
|
||||
{{ product.name }}
|
||||
</h3>
|
||||
|
||||
<div
|
||||
class="text-xs font-medium px-3 py-1 rounded-full inline-block"
|
||||
:class="product.stock > 0 ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'"
|
||||
>
|
||||
{{ product.stock > 0 ? `Ready ${product.stock} Unit` : 'No Stock' }}
|
||||
</div>
|
||||
</ion-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { IonCard } from '@ionic/vue'
|
||||
defineProps({
|
||||
product: Object
|
||||
})
|
||||
</script>
|
||||
0
src/components/product/SerialItem.vue
Normal file
0
src/components/product/SerialItem.vue
Normal file
0
src/composables/useProducts.ts
Normal file
0
src/composables/useProducts.ts
Normal file
28
src/layouts/MainLayout.vue
Normal file
28
src/layouts/MainLayout.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<ion-split-pane content-id="main-content" when="md">
|
||||
<!-- Sidebar -->
|
||||
<AppSidebar />
|
||||
|
||||
<!-- Main Area -->
|
||||
<ion-page id="main-content">
|
||||
<AppHeader />
|
||||
|
||||
<ion-content>
|
||||
<ion-router-outlet />
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</ion-split-pane>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
IonSplitPane,
|
||||
IonPage,
|
||||
IonContent
|
||||
} from '@ionic/vue'
|
||||
|
||||
import AppSidebar from '@/components/common/AppSidebar.vue'
|
||||
import AppHeader from '@/components/common/AppHeader.vue'
|
||||
import { IonRouterOutlet } from '@ionic/vue';
|
||||
|
||||
</script>
|
||||
@@ -1,16 +1,35 @@
|
||||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import HomePage from '../views/HomePage.vue'
|
||||
import HomePage from '@/views/home/HomePage.vue'
|
||||
import ProductListPage from '@/views/product/ProductListPage.vue';
|
||||
import ProductDetailPage from '@/views/product/ProductDetailPage.vue';
|
||||
import MainLayout from '@/layouts/MainLayout.vue'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home'
|
||||
component: MainLayout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: '/products'
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
path: 'home',
|
||||
name: 'Home',
|
||||
component: HomePage
|
||||
},
|
||||
{
|
||||
path: 'products',
|
||||
name: 'Products',
|
||||
component: ProductListPage
|
||||
},
|
||||
{
|
||||
path: 'products/:id',
|
||||
name: 'Products Details',
|
||||
component: ProductDetailPage
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
0
src/services/product.service.ts
Normal file
0
src/services/product.service.ts
Normal file
0
src/stores/product.store.ts
Normal file
0
src/stores/product.store.ts
Normal file
0
src/types/product.ts
Normal file
0
src/types/product.ts
Normal file
59
src/views/product/ProductDetailPage.vue
Normal file
59
src/views/product/ProductDetailPage.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-content class="ion-padding">
|
||||
|
||||
<ion-button fill="clear" @click="$router.back()">
|
||||
<ion-icon slot="start" :icon="returnUpBack"></ion-icon>
|
||||
Kembali
|
||||
</ion-button>
|
||||
|
||||
<h2 class="text-xl font-bold mb-4">APM CLOPPY</h2>
|
||||
|
||||
<ion-card v-for="item in serials" :key="item.serial" class="mb-4 p-3 rounded-xl shadow-sm">
|
||||
|
||||
<div class="flex justify-between mb-2">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500">No Seri</p>
|
||||
<p class="font-semibold">{{ item.serial }}</p>
|
||||
</div>
|
||||
|
||||
<StatusBadge :status="item.status" />
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-600 space-y-1">
|
||||
<p><strong>Waktu Produksi:</strong> {{ item.production }}</p>
|
||||
<p><strong>Lokasi:</strong> {{ item.location }}</p>
|
||||
<p><strong>Installation:</strong> {{ item.installation }}</p>
|
||||
<p><strong>History:</strong> {{ item.history }}</p>
|
||||
</div>
|
||||
|
||||
</ion-card>
|
||||
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { IonPage, IonContent, IonCard, IonButton } from '@ionic/vue'
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import { returnUpBack } from 'ionicons/icons'
|
||||
|
||||
const serials = [
|
||||
{
|
||||
serial: 'APM001 INT',
|
||||
production: '04-02-2026',
|
||||
status: 'Digunakan',
|
||||
location: 'Dusun Bambu',
|
||||
installation: '03-04-2026',
|
||||
history: '2x Maintenance'
|
||||
},
|
||||
{
|
||||
serial: 'APM004 INT',
|
||||
production: '12-03-2026',
|
||||
status: 'Maintenance',
|
||||
location: 'Park Zoo',
|
||||
installation: '03-04-2026',
|
||||
history: '1x Maintenance'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
34
src/views/product/ProductListPage.vue
Normal file
34
src/views/product/ProductListPage.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-content class="ion-padding">
|
||||
|
||||
<h2 class="text-2xl font-bold mb-4">DATA PRODUCT</h2>
|
||||
<p class="text-sm text-gray-500 mb-6">
|
||||
Karya Digital Manufacturing
|
||||
</p>
|
||||
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col size="6" size-md="3" v-for="product in products" :key="product.id">
|
||||
<ProductCard :product="product" />
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { IonPage, IonContent, IonGrid, IonRow, IonCol } from '@ionic/vue'
|
||||
import ProductCard from '@/components/product/ProductCard.vue'
|
||||
|
||||
const products = [
|
||||
{ id: 1, name: 'APM CLOPPY', stock: 2 },
|
||||
{ id: 2, name: 'SELKA POINT', stock: 0 },
|
||||
{ id: 3, name: 'BARRIER GATE', stock: 0 },
|
||||
{ id: 4, name: 'SMART PARKING', stock: 2 },
|
||||
{ id: 5, name: 'PHOTO BOX', stock: 0 },
|
||||
{ id: 6, name: 'MESIN ABSENSI', stock: 2 }
|
||||
]
|
||||
</script>
|
||||
Reference in New Issue
Block a user