feat(ProductSerialPage, SerialItemCard, ProductListPage, ProductCard): slicing UI Product Serial Sage, update UI Product List Page
This commit is contained in:
BIN
src/assets/images/calendar.png
Normal file
BIN
src/assets/images/calendar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
BIN
src/assets/images/tools.png
Normal file
BIN
src/assets/images/tools.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -4,4 +4,18 @@
|
||||
|
||||
.bg-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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<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">
|
||||
<div class="h-full w-full flex flex-col justify-between items-center absolute p-4">
|
||||
<div class="flex-1 flex items-center">
|
||||
@@ -24,4 +24,8 @@ import { IonCard } from '@ionic/vue'
|
||||
defineProps({
|
||||
product: Object
|
||||
})
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
</script>
|
||||
88
src/components/product/SerialItemCard.vue
Normal file
88
src/components/product/SerialItemCard.vue
Normal 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>
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<ion-split-pane content-id="main-content" when="md">
|
||||
<!-- Sidebar -->
|
||||
<AppSidebar />
|
||||
<!-- <AppSidebar /> -->
|
||||
|
||||
<!-- Main Area -->
|
||||
<ion-page id="main-content">
|
||||
<AppHeader />
|
||||
<!-- <AppHeader /> -->
|
||||
<ion-content>
|
||||
<ion-router-outlet class="overflow-auto" />
|
||||
</ion-content>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import HomePage from '@/views/home/HomePage.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'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
@@ -26,8 +26,8 @@ const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
{
|
||||
path: 'products/:id',
|
||||
name: 'Products Details',
|
||||
component: ProductDetailPage
|
||||
name: 'Products Serials',
|
||||
component: ProductSerialPage
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<ion-page class="overflow-auto h-max min-h-full">
|
||||
<ion-content>
|
||||
<div class="relative bg-sky-50 bg-opacity-50 h-max min-h-full">
|
||||
<!-- Background -->
|
||||
<div class="bg-neutral-900 shallow-bow">
|
||||
</div>
|
||||
<ion-page class="shallow-bow-container">
|
||||
<!-- Background -->
|
||||
<div class="bg-neutral-900 shallow-bow">
|
||||
</div>
|
||||
<ion-content class="bg-transparent">
|
||||
<div class="h-max min-h-full">
|
||||
<div class="p-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%]">
|
||||
@@ -28,15 +28,16 @@
|
||||
</ion-card>
|
||||
</div>
|
||||
</div>
|
||||
<ion-card button color="danger" class="bg-red-500 p-2 text-sm rounded-full">
|
||||
<div class="flex items-center">
|
||||
<div class="w-14 aspect-square rounded-full bg-white flex items-center justify-center p-2">
|
||||
<ion-card button color="danger" class="bg-red-500 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">LOGIN DASHBOARD</div>
|
||||
<div class="flex-1 text-xl text-center font-semibold p-3">LOGIN DASHBOARD</div>
|
||||
</div>
|
||||
</ion-card>
|
||||
<AppFooter />
|
||||
<AppFooter color="white" />
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
@@ -55,17 +56,4 @@ const products = [
|
||||
{ id: 5, name: 'PHOTO BOX', stock: 0 },
|
||||
{ id: 6, name: 'MESIN ABSENSI', stock: 2 }
|
||||
]
|
||||
</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>
|
||||
</script>
|
||||
104
src/views/product/ProductSerialPage.vue
Normal file
104
src/views/product/ProductSerialPage.vue
Normal 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>
|
||||
Reference in New Issue
Block a user