refaktoryzacja paneli i widokow
This commit is contained in:
@@ -3,60 +3,45 @@
|
||||
<div class="mark"></div>
|
||||
<div class="card-head">
|
||||
<div class="order-info">
|
||||
<span class="order-id">{{ order.orderId }}</span>
|
||||
<h3 class="model">{{ order.model }}</h3>
|
||||
<p class="client">{{ order.client }}</p>
|
||||
<span class="order-id">{{ product.orderId }}</span>
|
||||
<h3 class="model">{{ product.model }}</h3>
|
||||
<p class="client">{{ product.client }}</p>
|
||||
<div class="order-lists">
|
||||
<span
|
||||
v-for="assignedOrder in order.assignedOrders"
|
||||
:key="assignedOrder"
|
||||
v-for="productionList in product.productionLists"
|
||||
:key="productionList"
|
||||
class="order-list"
|
||||
>
|
||||
{{ assignedOrder }}
|
||||
{{ productionList }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="finish">{{ order.finish }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow">
|
||||
<div class="flow-head">
|
||||
<span class="label">Deska</span>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="steps no-scrollbar">
|
||||
<template v-for="(step, index) in order.steps.body" :key="`${step}-${index}`">
|
||||
<div class="step">{{ step }}</div>
|
||||
<span class="arrow">→</span>
|
||||
</template>
|
||||
<button class="step add">
|
||||
<q-icon class="add-icon" name="add" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow" v-if="order.steps.neck">
|
||||
<div class="flow-head">
|
||||
<span class="label">Gryf</span>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="steps no-scrollbar">
|
||||
<template v-for="(step, index) in order.steps.neck" :key="`${step}-${index}`">
|
||||
<div class="step">{{ step }}</div>
|
||||
<span class="arrow">→</span>
|
||||
</template>
|
||||
<button class="step add">
|
||||
<q-icon class="add-icon" name="add" />
|
||||
</button>
|
||||
<span class="finish">{{ product.finish }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<production-preview
|
||||
label="Deska"
|
||||
:steps="product.timelinePreview.body"
|
||||
@add="emit('addProductionEvent', { product, partType: 'BODY' })"
|
||||
/>
|
||||
<production-preview
|
||||
v-if="product.timelinePreview.neck.length"
|
||||
label="Gryf"
|
||||
:steps="product.timelinePreview.neck"
|
||||
@add="emit('addProductionEvent', { product, partType: 'NECK' })"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import ProductionPreview from 'src/components/ProductionPreview.vue'
|
||||
|
||||
defineProps({
|
||||
order: {
|
||||
product: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['addProductionEvent'])
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.my-order-card {
|
||||
@@ -155,62 +140,5 @@ defineProps({
|
||||
border-radius: var(--my-radius-sm);
|
||||
}
|
||||
}
|
||||
|
||||
.flow {
|
||||
margin-top: 1rem;
|
||||
.flow-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
.label {
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: color-mix(in srgb, var(--my-outline-variant) 30%, transparent);
|
||||
}
|
||||
}
|
||||
.steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
overflow-x: auto;
|
||||
.arrow {
|
||||
color: var(--my-outline-variant);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.step {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--my-primary);
|
||||
background: color-mix(in srgb, var(--my-primary-dim) 20%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--my-primary) 30%, transparent);
|
||||
border-radius: var(--my-radius-sm);
|
||||
|
||||
&.add {
|
||||
background: var(--my-surface-container-lowest);
|
||||
border-style: dashed;
|
||||
border-color: var(--my-outline-variant);
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
91
frontend/src/components/ProductionPreview.vue
Normal file
91
frontend/src/components/ProductionPreview.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="flow">
|
||||
<div class="flow-head">
|
||||
<span class="label">{{ label }}</span>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div class="steps no-scrollbar">
|
||||
<template v-for="step in steps" :key="step.id">
|
||||
<div class="step">{{ step.code }}</div>
|
||||
<span class="arrow">→</span>
|
||||
</template>
|
||||
<button class="step add" type="button" @click="emit('add')">
|
||||
<q-icon class="add-icon" name="add" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
steps: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['add'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.flow {
|
||||
margin-top: 1rem;
|
||||
.flow-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
.label {
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: color-mix(in srgb, var(--my-outline-variant) 30%, transparent);
|
||||
}
|
||||
}
|
||||
.steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
overflow-x: auto;
|
||||
.arrow {
|
||||
color: var(--my-outline-variant);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.step {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--my-primary);
|
||||
background: color-mix(in srgb, var(--my-primary-dim) 20%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--my-primary) 30%, transparent);
|
||||
border-radius: var(--my-radius-sm);
|
||||
|
||||
&.add {
|
||||
background: var(--my-surface-container-lowest);
|
||||
border-style: dashed;
|
||||
border-color: var(--my-outline-variant);
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
48
frontend/src/components/right-drawer/RightDrawerHost.vue
Normal file
48
frontend/src/components/right-drawer/RightDrawerHost.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="right-drawer-host">
|
||||
<component
|
||||
:is="activePanelComponent"
|
||||
v-if="activePanelComponent"
|
||||
:key="uiStore.drawerInstanceKey"
|
||||
v-bind="uiStore.drawerPayload"
|
||||
@cancel="closeDrawer"
|
||||
@close="closeDrawer"
|
||||
@saved="closeDrawer"
|
||||
/>
|
||||
|
||||
<div v-else class="right-drawer-host__empty">
|
||||
Brak aktywnego panelu.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useUiStore } from 'src/stores/uiStore'
|
||||
import { rightDrawerPanels } from './panels'
|
||||
|
||||
const uiStore = useUiStore()
|
||||
|
||||
const activePanelComponent = computed(() => {
|
||||
if (!uiStore.activePanel) {
|
||||
return null
|
||||
}
|
||||
|
||||
return rightDrawerPanels[uiStore.activePanel] ?? null
|
||||
})
|
||||
|
||||
function closeDrawer() {
|
||||
uiStore.closeDrawer()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-drawer-host {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.right-drawer-host__empty {
|
||||
padding: 1.25rem;
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<section class="drawer-panel">
|
||||
<header class="drawer-panel__header">
|
||||
<div>
|
||||
<div class="drawer-panel__eyebrow">Search</div>
|
||||
<h2 class="drawer-panel__title">Advanced Search</h2>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="close"
|
||||
aria-label="Close advanced search panel"
|
||||
@click="emit('close')"
|
||||
/>
|
||||
</header>
|
||||
|
||||
<div class="drawer-panel__body">
|
||||
<p class="drawer-panel__description">
|
||||
Ten panel bedzie trzymal robocza konfiguracje filtrow i zapisze ja dopiero po
|
||||
zatwierdzeniu przez uzytkownika.
|
||||
</p>
|
||||
|
||||
<div class="drawer-panel__placeholder">
|
||||
Tu trafia kontrolki filtrow wybieranych bez wpisywania tekstu.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="drawer-panel__footer">
|
||||
<q-btn flat label="Cancel" @click="emit('cancel')" />
|
||||
<q-btn color="primary" label="Apply" @click="applyFilters" />
|
||||
</footer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['cancel', 'close', 'apply'])
|
||||
|
||||
function applyFilters() {
|
||||
emit('apply')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-panel {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
min-height: 100%;
|
||||
background: var(--my-surface);
|
||||
}
|
||||
|
||||
.drawer-panel__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem 1.25rem 1rem;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
}
|
||||
|
||||
.drawer-panel__eyebrow {
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.drawer-panel__title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.drawer-panel__body {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.drawer-panel__description {
|
||||
margin: 0;
|
||||
color: var(--my-on-surface-variant);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.drawer-panel__placeholder {
|
||||
padding: 1rem;
|
||||
background: var(--my-surface-container-low);
|
||||
border: 1px dashed var(--my-outline-variant);
|
||||
border-radius: var(--my-radius-md);
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
|
||||
.drawer-panel__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
border-top: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<section class="drawer-panel">
|
||||
<header class="header">
|
||||
<div>
|
||||
<h3 class="header-title">Edit Instrument</h3>
|
||||
<p class="header-info">0029/2025/12</p>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="close"
|
||||
aria-label="Close product specification panel"
|
||||
@click="emit('close')"
|
||||
/>
|
||||
</header>
|
||||
<section class="spec">
|
||||
<div class="spec-header">
|
||||
<q-icon name="label" class="spec-icon" />
|
||||
<h4 class="spec-title">tadada</h4>
|
||||
</div>
|
||||
<div class="spec-details">
|
||||
<div class="spec-record">
|
||||
<span class="spec-label">Radius:</span>
|
||||
<span class="spec-value">16"</span>
|
||||
</div>
|
||||
<div class="spec-record">
|
||||
<span class="spec-label">Drewno Szyjka:</span>
|
||||
<span class="spec-value">5ply Wenge/Klon with Carbon Rods</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spec-header">
|
||||
<q-icon name="label" class="spec-icon" />
|
||||
<h4 class="spec-title">tadada</h4>
|
||||
</div>
|
||||
<div class="spec-details">
|
||||
<div class="spec-record">
|
||||
<span class="spec-label">Radius:</span>
|
||||
<span class="spec-value">16"</span>
|
||||
</div>
|
||||
<div class="spec-record">
|
||||
<span class="spec-label">Drewno Szyjka:</span>
|
||||
<span class="spec-value">5ply Wenge/Klon with Carbon Rods</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="drawer-panel__footer">
|
||||
<q-btn flat label="Cancel" @click="emit('cancel')" />
|
||||
<q-btn color="primary" label="Save" @click="saveSpecification" />
|
||||
</footer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
productId: {
|
||||
type: [Number, String],
|
||||
default: null,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'view',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cancel', 'close', 'saved'])
|
||||
|
||||
function saveSpecification() {
|
||||
emit('saved', {
|
||||
productId: props.productId,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-panel {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
min-height: 100%;
|
||||
background: var(--my-surface-container);
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1.5rem;
|
||||
// border-bottom: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
|
||||
.header-title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
margin: 0;
|
||||
color: var(--my-primary);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
// letter-spacing: 0.08em;
|
||||
// letter-spacing: -0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.spec {
|
||||
padding: 1rem;
|
||||
.spec-header {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 16px;
|
||||
.spec-icon {
|
||||
color: var(--my-primary);
|
||||
}
|
||||
.spec-title {
|
||||
color: var(--my-on-surface-variant);
|
||||
margin: 0;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 900;
|
||||
line-height: 1rem;
|
||||
letter-spacing: 0.1rem;
|
||||
}
|
||||
}
|
||||
.spec-details {
|
||||
.spec-record {
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background-color: var(--my-surface-container-high);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
.spec-label {
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
.spec-value {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.body {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
color: var(--my-on-surface-variant);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
background: var(--my-surface-container-low);
|
||||
border: 1px dashed var(--my-outline-variant);
|
||||
border-radius: var(--my-radius-md);
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
border-top: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<section class="drawer-panel">
|
||||
<header class="drawer-panel__header">
|
||||
<div>
|
||||
<div class="drawer-panel__eyebrow">Production</div>
|
||||
<h2 class="drawer-panel__title">Production Statuses</h2>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="close"
|
||||
aria-label="Close production statuses panel"
|
||||
@click="emit('close')"
|
||||
/>
|
||||
</header>
|
||||
|
||||
<div class="drawer-panel__body">
|
||||
<p class="drawer-panel__description">
|
||||
Ten panel dostanie identyfikator kontekstu produkcyjnego i utworzy lokalny draft do
|
||||
przegladania oraz edycji statusow.
|
||||
</p>
|
||||
|
||||
<div class="drawer-panel__placeholder">
|
||||
<div><strong>orderId:</strong> {{ orderId ?? 'brak' }}</div>
|
||||
<div><strong>productId:</strong> {{ productId ?? 'brak' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="drawer-panel__footer">
|
||||
<q-btn flat label="Cancel" @click="emit('cancel')" />
|
||||
<q-btn color="primary" label="Save" @click="saveStatuses" />
|
||||
</footer>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
orderId: {
|
||||
type: [Number, String],
|
||||
default: null,
|
||||
},
|
||||
productId: {
|
||||
type: [Number, String],
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cancel', 'close', 'saved'])
|
||||
|
||||
function saveStatuses() {
|
||||
emit('saved', {
|
||||
orderId: props.orderId,
|
||||
productId: props.productId,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drawer-panel {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
min-height: 100%;
|
||||
background: var(--my-surface);
|
||||
}
|
||||
|
||||
.drawer-panel__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem 1.25rem 1rem;
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
}
|
||||
|
||||
.drawer-panel__eyebrow {
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--my-on-surface-variant);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.drawer-panel__title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.drawer-panel__body {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.drawer-panel__description {
|
||||
margin: 0;
|
||||
color: var(--my-on-surface-variant);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.drawer-panel__placeholder {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
background: var(--my-surface-container-low);
|
||||
border: 1px dashed var(--my-outline-variant);
|
||||
border-radius: var(--my-radius-md);
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
|
||||
.drawer-panel__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
border-top: 1px solid color-mix(in srgb, var(--my-outline-variant) 60%, transparent);
|
||||
}
|
||||
</style>
|
||||
@@ -21,6 +21,16 @@
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
|
||||
<q-drawer
|
||||
v-model="rightDrawerOpen"
|
||||
side="right"
|
||||
overlay
|
||||
:width="$q.screen.width"
|
||||
class="right-drawer"
|
||||
>
|
||||
<RightDrawerHost />
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container>
|
||||
<router-view />
|
||||
</q-page-container>
|
||||
@@ -30,7 +40,9 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import EssentialLink from 'components/EssentialLink.vue'
|
||||
import RightDrawerHost from 'src/components/right-drawer/RightDrawerHost.vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useUiStore } from 'src/stores/uiStore'
|
||||
|
||||
const linksList = [
|
||||
{
|
||||
@@ -78,12 +90,22 @@ const linksList = [
|
||||
]
|
||||
const $q = useQuasar()
|
||||
const leftDrawerOpen = ref(false)
|
||||
const uiStore = useUiStore()
|
||||
|
||||
const theme = computed({
|
||||
get: () => $q.dark.isActive,
|
||||
set: (val) => $q.dark.set(val),
|
||||
})
|
||||
|
||||
const rightDrawerOpen = computed({
|
||||
get: () => uiStore.isDrawerOpen,
|
||||
set: (value) => {
|
||||
if (!value) {
|
||||
uiStore.closeDrawer()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function toggleLeftDrawer() {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
}
|
||||
|
||||
@@ -1,28 +1,57 @@
|
||||
<template>
|
||||
<q-page class="content">
|
||||
<section class="drawer-test-actions">
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="Open Advanced Search"
|
||||
unelevated
|
||||
@click="openAdvancedSearch"
|
||||
/>
|
||||
<q-btn
|
||||
color="secondary"
|
||||
label="Open Product Specification"
|
||||
unelevated
|
||||
@click="openProductSpecification"
|
||||
/>
|
||||
<q-btn
|
||||
color="accent"
|
||||
label="Open Production Statuses"
|
||||
unelevated
|
||||
@click="openProductionStatuses"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section class="filters">
|
||||
<div class="search-field">
|
||||
<input class="input" placeholder="Search orders or models..." type="text" />
|
||||
<div class="icon-wrap">
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
class="input"
|
||||
placeholder="Search orders or models..."
|
||||
type="text"
|
||||
@keyup.enter="applySearch"
|
||||
/>
|
||||
<div class="icon-wrap" @click="applySearch">
|
||||
<span class="material-symbols-outlined" data-icon="tune">tune</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="month-tabs no-scrollbar">
|
||||
<button class="tab active">All</button>
|
||||
<button class="tab">Jan</button>
|
||||
<button class="tab">Feb</button>
|
||||
<button class="tab">Mar</button>
|
||||
<button class="tab">Apr</button>
|
||||
<button class="tab">May</button>
|
||||
<button class="tab">Jun</button>
|
||||
<button
|
||||
v-for="month in monthFilters"
|
||||
:key="month.value"
|
||||
class="tab"
|
||||
:class="{ active: activeMonth === month.value }"
|
||||
@click="selectMonth(month.value)"
|
||||
>
|
||||
{{ month.label }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-card primary">
|
||||
<span class="label">In Progress</span>
|
||||
<span class="value">14 Units</span>
|
||||
<span class="value">{{ loadedProductsCount }} Units</span>
|
||||
</div>
|
||||
<div class="stat-card tertiary">
|
||||
<span class="label">Avg Lead Time</span>
|
||||
@@ -31,49 +60,87 @@
|
||||
</div>
|
||||
|
||||
<div class="orders">
|
||||
<order-card :order="order[0]" />
|
||||
<order-card :order="order[1]" />
|
||||
<order-card :order="order[2]" />
|
||||
<order-card
|
||||
v-for="product in productsStore.items"
|
||||
:key="product.id"
|
||||
:product="product"
|
||||
@add-production-event="openProductionStatuses"
|
||||
/>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import OrderCard from 'src/components/OrderCard.vue'
|
||||
const order = [
|
||||
{
|
||||
orderId: '0112/2025/12',
|
||||
model: 'Duvell 6',
|
||||
client: 'Sleek Elite',
|
||||
assignedOrders: ['PAZ-25'],
|
||||
finish: 'GLOSS',
|
||||
steps: {
|
||||
body: ['B', 'IZ', 'AK', 'LD', 'LD', 'LD', 'UV'],
|
||||
neck: ['B', 'IZ', 'AK'],
|
||||
},
|
||||
},
|
||||
{
|
||||
orderId: '0029/2024/1',
|
||||
model: 'Legend 6',
|
||||
client: 'Heindeburs Indonesia',
|
||||
assignedOrders: ['STY-25'],
|
||||
finish: 'MAT',
|
||||
steps: {
|
||||
body: ['B', 'M'],
|
||||
},
|
||||
},
|
||||
{
|
||||
orderId: '0001/2025/20',
|
||||
model: 'Regius 6 Core',
|
||||
client: 'Mayo Stock',
|
||||
assignedOrders: ['KWI-25', 'SUMMIT'],
|
||||
finish: 'MIX',
|
||||
steps: {
|
||||
body: ['B', 'IZ', 'AK', 'LD'],
|
||||
},
|
||||
},
|
||||
import { UI_PANELS, useUiStore } from 'src/stores/uiStore'
|
||||
import { useProductsStore } from 'src/stores/productsStore'
|
||||
|
||||
const productsStore = useProductsStore()
|
||||
const uiStore = useUiStore()
|
||||
const searchQuery = ref(productsStore.filters.search)
|
||||
const activeMonth = ref('all')
|
||||
|
||||
const monthFilters = [
|
||||
{ label: 'All', value: 'all' },
|
||||
{ label: 'Jan', value: 1 },
|
||||
{ label: 'Feb', value: 2 },
|
||||
{ label: 'Mar', value: 3 },
|
||||
{ label: 'Apr', value: 4 },
|
||||
{ label: 'May', value: 5 },
|
||||
{ label: 'Jun', value: 6 },
|
||||
]
|
||||
//
|
||||
|
||||
const loadedProductsCount = computed(() => productsStore.count)
|
||||
|
||||
onMounted(() => {
|
||||
productsStore.fetchFirstPage()
|
||||
})
|
||||
|
||||
watch(searchQuery, (value) => {
|
||||
productsStore.setFilters({ search: value })
|
||||
})
|
||||
|
||||
function openAdvancedSearch() {
|
||||
uiStore.openDrawer(UI_PANELS.ADVANCED_SEARCH, {
|
||||
source: 'index-page',
|
||||
})
|
||||
}
|
||||
|
||||
function openProductSpecification() {
|
||||
const product = productsStore.items[0]
|
||||
|
||||
if (!product) {
|
||||
return
|
||||
}
|
||||
|
||||
uiStore.openDrawer(UI_PANELS.PRODUCT_SPECIFICATION, {
|
||||
productId: product.id,
|
||||
mode: 'edit',
|
||||
})
|
||||
}
|
||||
|
||||
function openProductionStatuses({ product, partType } = {}) {
|
||||
const selectedProduct = product ?? productsStore.items[0]
|
||||
|
||||
if (!selectedProduct) {
|
||||
return
|
||||
}
|
||||
|
||||
uiStore.openDrawer(UI_PANELS.PRODUCTION_STATUSES, {
|
||||
orderId: selectedProduct.orderId,
|
||||
productId: selectedProduct.id,
|
||||
partType,
|
||||
})
|
||||
}
|
||||
|
||||
function applySearch() {
|
||||
productsStore.applyFilters({ search: searchQuery.value })
|
||||
}
|
||||
|
||||
function selectMonth(month) {
|
||||
activeMonth.value = month
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -88,6 +155,13 @@ const order = [
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.drawer-test-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
|
||||
Reference in New Issue
Block a user