feat(ProductSerialPage, SerialItemCard, ProductListPage, ProductCard): slicing UI Product Serial Sage, update UI Product List Page

This commit is contained in:
2026-02-24 14:58:40 +07:00
parent 47da12135d
commit 76d7f350b9
11 changed files with 229 additions and 90 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
src/assets/images/tools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -5,3 +5,17 @@
.bg-transparent { .bg-transparent {
--background: transparent; --background: transparent;
} }
.shallow-bow {
border-radius: 100% 100% 0 0;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 150%;
height: 60%;
}
.shallow-bow-container {
@apply h-max min-h-full relative bg-sky-50 bg-opacity-50 max-w-full overflow-hidden;
}

View File

@@ -1,5 +1,5 @@
<template> <template>
<ion-card button @click="$router.push('/products/'+product.id)" :class="product.stock ? 'bg-blue-50 border-blue-100' : 'bg-red-50 border-red-100'" <ion-card button @click="router.push('/products/'+product.id)" :class="product.stock ? 'bg-blue-50 border-blue-100' : 'bg-red-50 border-red-100'"
class="rounded-2xl border m-0 aspect-square relative"> class="rounded-2xl border m-0 aspect-square relative">
<div class="h-full w-full flex flex-col justify-between items-center absolute p-4"> <div class="h-full w-full flex flex-col justify-between items-center absolute p-4">
<div class="flex-1 flex items-center"> <div class="flex-1 flex items-center">
@@ -24,4 +24,8 @@ import { IonCard } from '@ionic/vue'
defineProps({ defineProps({
product: Object product: Object
}) })
import { useRouter } from 'vue-router';
const router = useRouter();
</script> </script>

View File

@@ -0,0 +1,88 @@
<template>
<ion-card
:class="item.status === 'used' ? 'bg-blue-50 border-blue-100' : item.status === 'ready' ? 'bg-lime-50 border-lime-100' : 'bg-red-50 border-red-100'"
class="rounded-2xl border m-0">
<div class="h-full w-full p-4 space-y-2">
<div class="flex justify-between gap-2">
<div class="flex flex-col items-center w-full">
<div>No Seri</div>
<div
class="h-fit font-medium p-1 rounded-full bg-white w-full max-w-52 flex items-center justify-center border text-black">
{{ item.serial }}
</div>
</div>
<div class="flex flex-col items-center w-full">
<div>Lokasi</div>
<div
class="h-fit font-medium p-1 rounded-full bg-white w-full max-w-52 flex items-center justify-center border text-black">
{{ item.location }}
</div>
</div>
</div>
<div class="flex justify-between gap-2">
<div class="flex flex-col items-center w-full">
<div>Waktu Produksi</div>
<div
class="h-fit font-medium p-1 rounded-full bg-white w-full max-w-52 flex items-center justify-center border text-black">
{{ item.production }}
</div>
</div>
<div class="flex flex-col items-center w-full">
<div>Status</div>
<div class="h-fit font-medium p-0 rounded-full bg-white w-full max-w-52 flex items-center border relative">
<div class="w-5 max-h-full aspect-square rounded-full absolute left-[6px]"
:class="item.status === 'ready' ? 'bg-green-500' : item.status === 'used' ? 'bg-blue-500' : 'bg-red-500'">
</div>
<div class="flex-1 py-1 px-5 text-black text-center">
{{ item.status === 'ready' ? 'Ready to use' : item.status === 'used' ? 'Digunakan' : 'Maintenance' }}
</div>
</div>
</div>
</div>
<div :class="item.status === 'ready' && 'invisible'" class="grid grid-cols-2 gap-2 w-full">
<div :class="item.status === 'maintenance' && 'invisible'" class="flex flex-col items-center">
<div class="grid grid-cols-7 items-center w-full max-w-52 px-2">
<img src="@/assets/images/tools.png" alt="Icon tools" class="w-max aspect-square">
<div class="col-span-6 h-max font-medium flex flex-col items-center">
<div>History</div>
<div class="text-black">
{{ item.history }}
</div>
</div>
</div>
</div>
<div class="flex flex-col items-center">
<div class="grid grid-cols-7 items-center w-full max-w-52 px-2">
<img src="@/assets/images/calendar.png" alt="Icon calendar" class="w-max aspect-square">
<div class="col-span-6 h-max font-medium flex flex-col items-center">
<div>Installation</div>
<div class="text-black">
{{ item.installation }}
</div>
</div>
</div>
</div>
</div>
</div>
</ion-card>
</template>
<script setup>
import { IonCard } from '@ionic/vue'
defineProps({
item: {
type: {
serial: String,
production: String,
status: String,
location: String,
installation: String,
history: String
}
}
})
</script>

View File

@@ -1,11 +1,11 @@
<template> <template>
<ion-split-pane content-id="main-content" when="md"> <ion-split-pane content-id="main-content" when="md">
<!-- Sidebar --> <!-- Sidebar -->
<AppSidebar /> <!-- <AppSidebar /> -->
<!-- Main Area --> <!-- Main Area -->
<ion-page id="main-content"> <ion-page id="main-content">
<AppHeader /> <!-- <AppHeader /> -->
<ion-content> <ion-content>
<ion-router-outlet class="overflow-auto" /> <ion-router-outlet class="overflow-auto" />
</ion-content> </ion-content>

View File

@@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from '@ionic/vue-router';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import HomePage from '@/views/home/HomePage.vue' import HomePage from '@/views/home/HomePage.vue'
import ProductListPage from '@/views/product/ProductListPage.vue'; import ProductListPage from '@/views/product/ProductListPage.vue';
import ProductDetailPage from '@/views/product/ProductDetailPage.vue'; import ProductSerialPage from '@/views/product/ProductSerialPage.vue';
import MainLayout from '@/layouts/MainLayout.vue' import MainLayout from '@/layouts/MainLayout.vue'
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
@@ -26,8 +26,8 @@ const routes: Array<RouteRecordRaw> = [
}, },
{ {
path: 'products/:id', path: 'products/:id',
name: 'Products Details', name: 'Products Serials',
component: ProductDetailPage component: ProductSerialPage
} }
] ]
} }

View File

@@ -1,59 +0,0 @@
<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>

View File

@@ -1,10 +1,10 @@
<template> <template>
<ion-page class="overflow-auto h-max min-h-full"> <ion-page class="shallow-bow-container">
<ion-content> <!-- Background -->
<div class="relative bg-sky-50 bg-opacity-50 h-max min-h-full"> <div class="bg-neutral-900 shallow-bow">
<!-- Background --> </div>
<div class="bg-neutral-900 shallow-bow"> <ion-content class="bg-transparent">
</div> <div class="h-max min-h-full">
<div class="p-4"> <div class="p-4">
<div class="flex flex-col items-center aspect-auto mt-4"> <div class="flex flex-col items-center aspect-auto mt-4">
<img src="@/assets/images/Logo-KDM-merah.png" alt="Logo KDM" class="w-[20%]"> <img src="@/assets/images/Logo-KDM-merah.png" alt="Logo KDM" class="w-[20%]">
@@ -28,15 +28,16 @@
</ion-card> </ion-card>
</div> </div>
</div> </div>
<ion-card button color="danger" class="bg-red-500 p-2 text-sm rounded-full"> <ion-card button color="danger" class="bg-red-500 p-2 m-4 text-sm rounded-full">
<div class="flex items-center"> <div class="flex items-center relative h-max">
<div class="w-14 aspect-square rounded-full bg-white flex items-center justify-center p-2"> <div
class="h-full aspect-square rounded-full bg-white flex items-center justify-center p-2 absolute left-0">
<img src="@/assets/images/KDM.png" alt="Logo KDM small" class="h-full"> <img src="@/assets/images/KDM.png" alt="Logo KDM small" class="h-full">
</div> </div>
<div class="flex-1 text-xl text-center font-semibold">LOGIN DASHBOARD</div> <div class="flex-1 text-xl text-center font-semibold p-3">LOGIN DASHBOARD</div>
</div> </div>
</ion-card> </ion-card>
<AppFooter /> <AppFooter color="white" />
</div> </div>
</ion-content> </ion-content>
</ion-page> </ion-page>
@@ -56,16 +57,3 @@ const products = [
{ id: 6, name: 'MESIN ABSENSI', stock: 2 } { id: 6, name: 'MESIN ABSENSI', stock: 2 }
] ]
</script> </script>
<style scoped lang="scss">
.shallow-bow {
border-radius: 100% 100% 0 0;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 150%;
height: 60%;
}
</style>

View File

@@ -0,0 +1,104 @@
<template>
<ion-page class="shallow-bow-container">
<!-- Background -->
<div class="bg-red-500 shallow-bow">
</div>
<ion-content class="bg-transparent">
<div class="h-max min-h-full">
<div class="p-4">
<div class="flex items-center justify-between w-full aspect-auto mb-6 mt-4">
<div class="flex items-center gap-4">
<ion-button size="medium" fill="clear" color="dark" @click="router.back()">
<div class="font-semibold flex items-center gap-2">
<ion-icon slot="icon-only" :icon="caretBackCircleSharp" class="text-2xl"></ion-icon>
<div>Kembali</div>
</div>
</ion-button>
</div>
<img src="@/assets/images/Logo-KDM-merah.png" alt="Logo KDM" class="w-[20%] cursor-pointer" button
@click="router.push('/')">
</div>
<div class="grid grid-cols-1 gap-4">
<ion-searchbar mode="ios" :animated="true" placeholder="No Seri Produk"
class="border-2 shadow-sm rounded-full bg-transparent my-6 bg-white"></ion-searchbar>
<div class="flex gap-2 items-end justify-between mb-2">
<h2 class="text-2xl font-semibold">APM CLOPPY</h2>
<div class="flex gap-2 items-center justify-between text-gray-500">
<div>Detail informasi</div>
<ion-button size="small" fill="clear" class="text-gray-500 rounded-md">
<ion-icon slot="icon-only" :icon="funnelOutline"></ion-icon>
</ion-button>
</div>
</div>
<ion-card class="rounded-2xl m-0 border-2 shadow-sm">
<ion-card-content class="ion-padding">
<div class="grid grid-cols-1 gap-4">
<div size="6" size-md="3" v-for="(item) in serialItems" :key="item.id">
<SerialItemCard :item="item" />
</div>
</div>
</ion-card-content>
</ion-card>
</div>
</div>
<ion-card button color="danger" class="bg-neutral-900 p-2 m-4 text-sm rounded-full">
<div class="flex items-center relative h-max">
<div
class="h-full aspect-square rounded-full bg-white flex items-center justify-center p-2 absolute left-0">
<img src="@/assets/images/KDM.png" alt="Logo KDM small" class="h-full">
</div>
<div class="flex-1 text-xl text-center font-semibold p-3">LOGIN DASHBOARD</div>
</div>
</ion-card>
<AppFooter color="white" />
</div>
</ion-content>
</ion-page>
</template>
<script setup>
import { IonPage, IonContent, IonSearchbar, IonCard, IonCardContent, IonIcon, IonButton } from '@ionic/vue'
import AppFooter from '@/components/common/AppFooter.vue'
import SerialItemCard from '@/components/product/SerialItemCard.vue'
import { funnelOutline, caretBackCircleSharp } from 'ionicons/icons'
const serialItems = [
{
serial: 'APM001 INT',
production: '04-02-2026',
status: 'used',
location: 'Dusun Bambu',
installation: '03-04-2026',
history: '2x Maintenance'
},
{
serial: 'APM002 INT',
production: '12-03-2026',
status: 'ready',
location: 'Standby WS',
installation: '09-02-2026',
history: '1x Maintenance'
},
{
serial: 'APM003 INT',
production: '04-02-2026',
status: 'used',
location: 'Ancol',
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'
},
]
import { useRouter } from 'vue-router';
const router = useRouter();
</script>