refaktoryzacja komponentów: dodanie interakcji i poprawa wyświetlania specyfikacji produktów
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<div class="my-order-card">
|
||||
<div class="mark"></div>
|
||||
<div class="card-head">
|
||||
<div class="order-info">
|
||||
<div class="order-info" @click="emit('showProductSpec', { product })">
|
||||
<span class="order-id">{{ product.orderId }}</span>
|
||||
<h3 class="model">{{ product.model }}</h3>
|
||||
<p class="client">{{ product.client }}</p>
|
||||
@@ -41,7 +41,7 @@ defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['addProductionEvent'])
|
||||
const emit = defineEmits(['addProductionEvent', 'showProductSpec'])
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.my-order-card {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<header class="header">
|
||||
<div>
|
||||
<h3 class="header-title">Edit Instrument</h3>
|
||||
<p class="header-info">0029/2025/12</p>
|
||||
<p class="header-info">{{ spec?.orderId ?? props.productId ?? 'brak produktu' }}</p>
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
@@ -16,34 +16,31 @@
|
||||
/>
|
||||
</header>
|
||||
<section class="spec">
|
||||
<div class="spec-header">
|
||||
<q-icon name="label" class="spec-icon" />
|
||||
<h4 class="spec-title">tadada</h4>
|
||||
<q-inner-loading :showing="isLoading" />
|
||||
|
||||
<q-banner v-if="error" rounded class="spec-state spec-state--error">
|
||||
Nie udalo sie wczytac specyfikacji produktu.
|
||||
</q-banner>
|
||||
|
||||
<div v-else-if="!sections.length && !isLoading" class="spec-state">
|
||||
Brak specyfikacji dla tego produktu.
|
||||
</div>
|
||||
<div class="spec-details">
|
||||
<div class="spec-record">
|
||||
<span class="spec-label">Radius:</span>
|
||||
<span class="spec-value">16"</span>
|
||||
|
||||
<template v-else>
|
||||
<div v-for="section in sections" :key="section.key" class="spec-section">
|
||||
<div class="spec-header">
|
||||
<q-icon name="label" class="spec-icon" />
|
||||
<h4 class="spec-title">{{ section.label }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="spec-details">
|
||||
<div v-for="field in section.fields" :key="field.key" class="spec-record">
|
||||
<span class="spec-label">{{ field.label }}:</span>
|
||||
<span class="spec-value">{{ formatValues(field.values) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<footer class="drawer-panel__footer">
|
||||
@@ -54,6 +51,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch } from 'vue'
|
||||
import { useProductSpecificationStore } from 'src/stores/productSpecificationStore'
|
||||
|
||||
const productSpecificationStore = useProductSpecificationStore()
|
||||
|
||||
const props = defineProps({
|
||||
productId: {
|
||||
type: [Number, String],
|
||||
@@ -65,8 +67,42 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const spec = computed(() => {
|
||||
if (!props.productId) {
|
||||
return null
|
||||
}
|
||||
|
||||
return productSpecificationStore.getSpecification(props.productId)
|
||||
})
|
||||
|
||||
const sections = computed(() => spec.value?.sections ?? [])
|
||||
const isLoading = computed(() => spec.value?.isLoading ?? false)
|
||||
const error = computed(() => spec.value?.error ?? null)
|
||||
|
||||
const emit = defineEmits(['cancel', 'close', 'saved'])
|
||||
|
||||
watch(
|
||||
() => props.productId,
|
||||
(productId) => {
|
||||
if (productId) {
|
||||
void loadSpecification(productId)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
async function loadSpecification(productId) {
|
||||
try {
|
||||
await productSpecificationStore.fetchSpecification(productId)
|
||||
} catch {
|
||||
// Error state is stored in productSpecificationStore and rendered above.
|
||||
}
|
||||
}
|
||||
|
||||
function formatValues(values) {
|
||||
return values?.length ? values.join(', ') : '-'
|
||||
}
|
||||
|
||||
function saveSpecification() {
|
||||
emit('saved', {
|
||||
productId: props.productId,
|
||||
@@ -107,7 +143,22 @@ function saveSpecification() {
|
||||
}
|
||||
|
||||
.spec {
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
|
||||
.spec-section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.spec-state {
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
|
||||
.spec-state--error {
|
||||
color: var(--my-error);
|
||||
background: var(--my-error-container);
|
||||
}
|
||||
|
||||
.spec-header {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
@@ -167,7 +218,7 @@ function saveSpecification() {
|
||||
color: var(--my-on-surface-variant);
|
||||
}
|
||||
|
||||
.footer {
|
||||
.drawer-panel__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
<q-layout view="lHh Lpr lFf">
|
||||
<q-header>
|
||||
<q-toolbar class="topbar">
|
||||
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" />
|
||||
<!-- <q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" /> -->
|
||||
|
||||
<q-toolbar-title> Quasar App </q-toolbar-title>
|
||||
<q-toolbar-title> DuckProductionManager </q-toolbar-title>
|
||||
|
||||
<q-btn :color="theme ? 'accent' : 'dark'" @click="theme = !theme">
|
||||
{{ theme ? 'DARK' : 'LIGHT' }}
|
||||
</q-btn>
|
||||
<div>Quasar v{{ $q.version }}</div>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
|
||||
@@ -106,13 +105,14 @@ const rightDrawerOpen = computed({
|
||||
},
|
||||
})
|
||||
|
||||
function toggleLeftDrawer() {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
}
|
||||
// function toggleLeftDrawer() {
|
||||
// leftDrawerOpen.value = !leftDrawerOpen.value
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.topbar {
|
||||
background: var(--my-background);
|
||||
color: var(--my-on-background);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<q-page class="content">
|
||||
<section class="drawer-test-actions">
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="Open Advanced Search"
|
||||
unelevated
|
||||
@click="openAdvancedSearch"
|
||||
/>
|
||||
<q-btn color="primary" label="Open Advanced Search" unelevated @click="openAdvancedSearch" />
|
||||
<q-btn
|
||||
color="secondary"
|
||||
label="Open Product Specification"
|
||||
@@ -30,8 +25,11 @@
|
||||
type="text"
|
||||
@keyup.enter="applySearch"
|
||||
/>
|
||||
<div class="icon-wrap" @click="applySearch">
|
||||
<span class="material-symbols-outlined" data-icon="tune">tune</span>
|
||||
<div class="icon-wrap">
|
||||
<q-icon class="icon" name="close" @click="clearSearch" />
|
||||
<span class="v-line"></span>
|
||||
<q-icon class="icon" name="camera_alt" />
|
||||
<q-icon class="icon" name="search" @click="applySearch" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,6 +63,7 @@
|
||||
:key="product.id"
|
||||
:product="product"
|
||||
@add-production-event="openProductionStatuses"
|
||||
@show-product-spec="openProductSpecification"
|
||||
/>
|
||||
</div>
|
||||
</q-page>
|
||||
@@ -107,15 +106,15 @@ function openAdvancedSearch() {
|
||||
})
|
||||
}
|
||||
|
||||
function openProductSpecification() {
|
||||
const product = productsStore.items[0]
|
||||
|
||||
if (!product) {
|
||||
function openProductSpecification(product) {
|
||||
const selectedProduct = product ?? productsStore.items[0]
|
||||
console.log(selectedProduct)
|
||||
if (!selectedProduct) {
|
||||
return
|
||||
}
|
||||
|
||||
uiStore.openDrawer(UI_PANELS.PRODUCT_SPECIFICATION, {
|
||||
productId: product.id,
|
||||
productId: selectedProduct.id,
|
||||
mode: 'edit',
|
||||
})
|
||||
}
|
||||
@@ -134,6 +133,11 @@ function openProductionStatuses({ product, partType } = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
searchQuery.value = ''
|
||||
productsStore.applySearch(searchQuery.value)
|
||||
}
|
||||
|
||||
function applySearch() {
|
||||
productsStore.applySearch(searchQuery.value)
|
||||
}
|
||||
@@ -168,20 +172,28 @@ function selectMonth(month) {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.search-field {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
color: var(--my-on-surface);
|
||||
background: var(--my-surface-container-highest);
|
||||
border-bottom: 2px solid var(--my-outline-variant);
|
||||
transition: border-color var(--my-transition);
|
||||
|
||||
&:focus-within {
|
||||
border-bottom-color: var(--my-primary);
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 3rem 0.75rem 1rem;
|
||||
min-width: 0;
|
||||
padding: 0.75rem 0.5rem 0.75rem 1rem;
|
||||
color: var(--my-on-surface);
|
||||
background: var(--my-surface-container-highest);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 2px solid var(--my-outline-variant);
|
||||
transition: border-color var(--my-transition);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-bottom-color: var(--my-primary);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@@ -190,11 +202,23 @@ function selectMonth(month) {
|
||||
}
|
||||
|
||||
.icon-wrap {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0 1rem 0 0.5rem;
|
||||
color: var(--my-on-surface-variant);
|
||||
transform: translateY(-50%);
|
||||
|
||||
.v-line {
|
||||
// align-self: stretch; /* 🔥 kluczowe */
|
||||
height: 2rem;
|
||||
width: 1px;
|
||||
background: var(--my-on-surface-variant);
|
||||
}
|
||||
.icon {
|
||||
// color: var(--my-on-surface);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user