refaktoryzacja paneli i widokow
This commit is contained in:
@@ -3,60 +3,45 @@
|
|||||||
<div class="mark"></div>
|
<div class="mark"></div>
|
||||||
<div class="card-head">
|
<div class="card-head">
|
||||||
<div class="order-info">
|
<div class="order-info">
|
||||||
<span class="order-id">{{ order.orderId }}</span>
|
<span class="order-id">{{ product.orderId }}</span>
|
||||||
<h3 class="model">{{ order.model }}</h3>
|
<h3 class="model">{{ product.model }}</h3>
|
||||||
<p class="client">{{ order.client }}</p>
|
<p class="client">{{ product.client }}</p>
|
||||||
<div class="order-lists">
|
<div class="order-lists">
|
||||||
<span
|
<span
|
||||||
v-for="assignedOrder in order.assignedOrders"
|
v-for="productionList in product.productionLists"
|
||||||
:key="assignedOrder"
|
:key="productionList"
|
||||||
class="order-list"
|
class="order-list"
|
||||||
>
|
>
|
||||||
{{ assignedOrder }}
|
{{ productionList }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="finish">{{ order.finish }}</span>
|
<span class="finish">{{ product.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>
|
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import ProductionPreview from 'src/components/ProductionPreview.vue'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
order: {
|
product: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['addProductionEvent'])
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.my-order-card {
|
.my-order-card {
|
||||||
@@ -155,62 +140,5 @@ defineProps({
|
|||||||
border-radius: var(--my-radius-sm);
|
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>
|
</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-list>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-drawer
|
||||||
|
v-model="rightDrawerOpen"
|
||||||
|
side="right"
|
||||||
|
overlay
|
||||||
|
:width="$q.screen.width"
|
||||||
|
class="right-drawer"
|
||||||
|
>
|
||||||
|
<RightDrawerHost />
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<router-view />
|
<router-view />
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
@@ -30,7 +40,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import EssentialLink from 'components/EssentialLink.vue'
|
import EssentialLink from 'components/EssentialLink.vue'
|
||||||
|
import RightDrawerHost from 'src/components/right-drawer/RightDrawerHost.vue'
|
||||||
import { useQuasar } from 'quasar'
|
import { useQuasar } from 'quasar'
|
||||||
|
import { useUiStore } from 'src/stores/uiStore'
|
||||||
|
|
||||||
const linksList = [
|
const linksList = [
|
||||||
{
|
{
|
||||||
@@ -78,12 +90,22 @@ const linksList = [
|
|||||||
]
|
]
|
||||||
const $q = useQuasar()
|
const $q = useQuasar()
|
||||||
const leftDrawerOpen = ref(false)
|
const leftDrawerOpen = ref(false)
|
||||||
|
const uiStore = useUiStore()
|
||||||
|
|
||||||
const theme = computed({
|
const theme = computed({
|
||||||
get: () => $q.dark.isActive,
|
get: () => $q.dark.isActive,
|
||||||
set: (val) => $q.dark.set(val),
|
set: (val) => $q.dark.set(val),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const rightDrawerOpen = computed({
|
||||||
|
get: () => uiStore.isDrawerOpen,
|
||||||
|
set: (value) => {
|
||||||
|
if (!value) {
|
||||||
|
uiStore.closeDrawer()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
function toggleLeftDrawer() {
|
function toggleLeftDrawer() {
|
||||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-page class="content">
|
<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">
|
<section class="filters">
|
||||||
<div class="search-field">
|
<div class="search-field">
|
||||||
<input class="input" placeholder="Search orders or models..." type="text" />
|
<input
|
||||||
<div class="icon-wrap">
|
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>
|
<span class="material-symbols-outlined" data-icon="tune">tune</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="month-tabs no-scrollbar">
|
<div class="month-tabs no-scrollbar">
|
||||||
<button class="tab active">All</button>
|
<button
|
||||||
<button class="tab">Jan</button>
|
v-for="month in monthFilters"
|
||||||
<button class="tab">Feb</button>
|
:key="month.value"
|
||||||
<button class="tab">Mar</button>
|
class="tab"
|
||||||
<button class="tab">Apr</button>
|
:class="{ active: activeMonth === month.value }"
|
||||||
<button class="tab">May</button>
|
@click="selectMonth(month.value)"
|
||||||
<button class="tab">Jun</button>
|
>
|
||||||
|
{{ month.label }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<div class="stat-card primary">
|
<div class="stat-card primary">
|
||||||
<span class="label">In Progress</span>
|
<span class="label">In Progress</span>
|
||||||
<span class="value">14 Units</span>
|
<span class="value">{{ loadedProductsCount }} Units</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card tertiary">
|
<div class="stat-card tertiary">
|
||||||
<span class="label">Avg Lead Time</span>
|
<span class="label">Avg Lead Time</span>
|
||||||
@@ -31,49 +60,87 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="orders">
|
<div class="orders">
|
||||||
<order-card :order="order[0]" />
|
<order-card
|
||||||
<order-card :order="order[1]" />
|
v-for="product in productsStore.items"
|
||||||
<order-card :order="order[2]" />
|
:key="product.id"
|
||||||
|
:product="product"
|
||||||
|
@add-production-event="openProductionStatuses"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-page>
|
</q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import OrderCard from 'src/components/OrderCard.vue'
|
import OrderCard from 'src/components/OrderCard.vue'
|
||||||
const order = [
|
import { UI_PANELS, useUiStore } from 'src/stores/uiStore'
|
||||||
{
|
import { useProductsStore } from 'src/stores/productsStore'
|
||||||
orderId: '0112/2025/12',
|
|
||||||
model: 'Duvell 6',
|
const productsStore = useProductsStore()
|
||||||
client: 'Sleek Elite',
|
const uiStore = useUiStore()
|
||||||
assignedOrders: ['PAZ-25'],
|
const searchQuery = ref(productsStore.filters.search)
|
||||||
finish: 'GLOSS',
|
const activeMonth = ref('all')
|
||||||
steps: {
|
|
||||||
body: ['B', 'IZ', 'AK', 'LD', 'LD', 'LD', 'UV'],
|
const monthFilters = [
|
||||||
neck: ['B', 'IZ', 'AK'],
|
{ label: 'All', value: 'all' },
|
||||||
},
|
{ label: 'Jan', value: 1 },
|
||||||
},
|
{ label: 'Feb', value: 2 },
|
||||||
{
|
{ label: 'Mar', value: 3 },
|
||||||
orderId: '0029/2024/1',
|
{ label: 'Apr', value: 4 },
|
||||||
model: 'Legend 6',
|
{ label: 'May', value: 5 },
|
||||||
client: 'Heindeburs Indonesia',
|
{ label: 'Jun', value: 6 },
|
||||||
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'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
//
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -88,6 +155,13 @@ const order = [
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-test-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.filters {
|
.filters {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user