diff --git a/src/frontend/src/components/layout/edit/MaterialEdit.vue b/src/frontend/src/components/layout/edit/MaterialEdit.vue index 353cb0b..da7320d 100644 --- a/src/frontend/src/components/layout/edit/MaterialEdit.vue +++ b/src/frontend/src/components/layout/edit/MaterialEdit.vue @@ -1,64 +1,37 @@ @@ -76,7 +49,6 @@ import {mapStores} from "pinia"; import {parseNumberFromString} from "@/common.js"; import Modal from "@/components/UI/Modal.vue"; import SelectMaterial from "@/components/layout/material/SelectMaterial.vue"; -import {useCustomsStore} from "@/store/customs.js"; import Checkbox from "@/components/UI/Checkbox.vue"; export default { @@ -90,24 +62,24 @@ export default { emits: ["update:tariffRate", "updateMaterial", "update:partNumber", "update:hsCode", "save", "close"], props: { description: { - type: [String, null], - required: true, + type: String, + default: null, }, hsCode: { - required: true, - validator: (value) => value === null || typeof value === 'string' + type: String, + default: null, }, countryId: { - required: true, - validator: (value) => value === null || typeof value === 'number' + type: Number, + default: null, }, tariffUnlocked: { type: Boolean, required: true, }, tariffRate: { - required: true, - validator: (value) => value === null || typeof value === 'number' + type: Number, + default: null, }, partNumber: { type: String, @@ -124,9 +96,54 @@ export default { }, computed: { ...mapStores(useMaterialStore), + tariffRatePercent() { - return ((this.tariffRate ?? null) !== null) ? (this.tariffRate * 100).toFixed(2) : ''; + return this.tariffRate !== null + ? (this.tariffRate * 100).toFixed(2) + : ''; }, + + displayFields() { + const fields = []; + + if (!this.hideDescription) { + fields.push( + { + name: 'partNumber', + label: 'Part number', + value: this.partNumber, + editable: false + }, + { + name: 'description', + label: 'Description', + value: this.description, + editable: false + } + ); + } + + fields.push( + { + name: 'hsCode', + label: 'HS code', + value: this.hsCode, + editable: this.tariffUnlocked, + ref: 'hsCodeInput', + onBlur: this.hsCodeChanged + }, + { + name: 'tariffRate', + label: 'Tariff rate [%]', + value: this.tariffRatePercent, + editable: this.tariffUnlocked, + ref: 'tariffRateInput', + onBlur: this.validateTariffRateEvent + } + ); + + return fields; + } }, created() { }, @@ -136,37 +153,41 @@ export default { this.$emit('save', 'material'); } }, - updateInputValue(inputRef, formattedValue) { - this.$nextTick(() => { - if (this.$refs[inputRef] && this.$refs[inputRef].value !== formattedValue) { - this.$refs[inputRef].value = formattedValue; - } - }); - }, + validateTariffRateEvent(event) { event.target.value = this.validateTariffRate(event.target.value); }, + validateTariffRate(value) { const percentValue = parseNumberFromString(value, 2, true); - - const validatedPercent = (percentValue === null) ? null : Math.max(0, Math.min(999.99, percentValue)); - const validatedDecimal = (validatedPercent === null) ? null : validatedPercent / 100; + const validatedPercent = this.clampValue(percentValue, 0, 999.99); + const validatedDecimal = validatedPercent !== null ? validatedPercent / 100 : null; if (validatedDecimal !== this.tariffRate) { this.$emit('update:tariffRate', this.roundNumber(validatedDecimal, 4)); } - return validatedPercent === null ? null : validatedPercent.toFixed(2); - + return validatedPercent?.toFixed(2) ?? null; }, + + clampValue(value, min, max) { + return value === null ? null : Math.max(min, Math.min(max, value)); + }, + roundNumber(number, digits) { - const multiple = Math.pow(10, digits); + if (number === null) return null; + const multiple = 10 ** digits; return Math.round(number * multiple) / multiple; }, + hsCodeChanged(event) { - const currentValue = this.$refs.hsCodeSearchbar.searchQuery; - this.$emit("update:hsCode", event.target.value); - }, + const sanitized = event.target.value + .replace(/\D/g, '') + .substring(0, 10); + + this.$emit("update:hsCode", sanitized); + event.target.value = sanitized; + } } } @@ -182,15 +203,6 @@ export default { } - -.warning-icon { - color: #002F54; -} - -.text-container:hover .warning-icon { - transform: scale(1.02); -} - .hs-code-container { display: flex; align-items: center; @@ -207,27 +219,22 @@ export default { flex: 1 1 auto; } -/* Responsive Layout für Breiten unter 1500px - nur wenn responsive aktiviert ist */ @media (max-width: 1500px) { .container.responsive { grid-template-columns: - minmax(auto, max-content) /* Spalte 1: Label */ - minmax(120px, 1fr) /* Spalte 2: Input */ - minmax(auto, max-content) /* Spalte 3: Label */ - minmax(120px, 1fr) /* Spalte 4: Input/Dropdown */ - minmax(auto, max-content) /* Spalte 5: Label */ - minmax(120px, 1fr) /* Spalte 6: Input */ - minmax(auto, max-content) /* Spalte 7: Label */ - minmax(120px, 1fr); /* Spalte 8: Input/Dropdown */ + minmax(auto, max-content) + minmax(120px, 1fr) + minmax(auto, max-content) + minmax(120px, 1fr) + minmax(auto, max-content) + minmax(120px, 1fr) + minmax(auto, max-content) + minmax(120px, 1fr); grid-template-rows: auto; gap: 1.2rem 1rem; align-items: center; } - .container.responsive .field-group { - display: contents; - } - .container.responsive .caption-column { justify-self: start; } @@ -236,32 +243,12 @@ export default { min-width: 0; } - .container.responsive .field-group { - display: contents; - } - - .container.responsive .caption-column { - justify-self: start; - } - - .container.responsive .input-column { - min-width: 0; - } -} - -/* Optimierung für kleinere Bildschirme unter 1500px - nur wenn responsive aktiviert ist */ -@media (max-width: 1500px) { .container.responsive .input-field-tariffrate { min-width: auto; flex: 1 1 auto; } } - -.field-group { - display: contents; -} - .input-column { display: flex; justify-content: space-between; @@ -318,53 +305,6 @@ export default { position: relative; } -.tariff-rate-info { - position: absolute; - top: 3rem; - left: -3rem; - display: flex; - flex-direction: column; - justify-content: center; - gap: 1.2rem; - z-index: 5000; - background-color: #fff; - border-radius: 0.8rem; - border: 0.1rem solid #E3EDFF; - visibility: hidden; - opacity: 0; - transform: translateY(-20px); - padding: 1.6rem 2.4rem; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - min-width: 40rem; -} - -.input-field-tariffrate:hover .tariff-rate-info { - visibility: visible; - opacity: 1; - transform: translateY(0); - transition: opacity 0.5s ease, transform 0.5s ease; -} - -.tariff-rate-info-cell-measure { - font-size: 1.2rem; - font-weight: 500; - color: #002F54; - justify-self: flex-end; -} - -.tariff-rate-info-cell-tariffrate { - justify-self: flex-end; -} - -.tariff-rate-info-container { - margin-top: 2.4rem; - display: grid; - grid-template-columns: auto 1fr 1fr; - gap: 0.8rem; - font-size: 1.2rem; - font-weight: 400; -} - .tariff-rate-info-headerbox { color: #002F54; font-size: 1.2rem; diff --git a/src/frontend/src/store/premiseSingleEdit.js b/src/frontend/src/store/premiseSingleEdit.js index 3e0ad04..a8fdc37 100644 --- a/src/frontend/src/store/premiseSingleEdit.js +++ b/src/frontend/src/store/premiseSingleEdit.js @@ -67,7 +67,7 @@ export const usePremiseSingleEditStore = defineStore('premiseSingleEdit', { async startCalculation() { this.calculating = true; - const body = this.premise?.id; + const body = [this.premise?.id]; const url = `${config.backendUrl}/calculation/start/`; let error = null;