"Add 'Add Customer' functionality: integrate new popup component and action flow for customer creation in PosDepartmentStepMobile. Update popup handling logic, adjust button actions, and introduce dynamic form autofill for seamless user experience."

This commit is contained in:
Jeppe Bundgaard
2025-11-17 13:31:54 +01:00
parent 3ae84cac1c
commit 32bf34658d
4 changed files with 260 additions and 3 deletions
@@ -31,7 +31,10 @@ import PosDepartmentStepMobileAttachments
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobileAttachments.vue";
import PosDepartmentStep1MobileTransactionHistory
from "@/components/displays/department/pos/steps/mobile/views/PosDepartmentStep1MobileTransactionHistory.vue";
import {attachments} from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue";
import {
attachments,
popups
} from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue";
// Debug mode flag
const debug_mode = ref(false);
// Debug array to store request results
@@ -0,0 +1,228 @@
<script setup lang="ts">
import { computed, defineEmits, watch, ref } from "vue";
import { PosSearchResult } from "../objects/PosSearchResult.vue";
import CustomerSearchField from "@/components/search/economic/customerSearchField.vue";
import { searchCustomerResults, isSearching } from "@/components/search/economic/customerSearch.vue";
import ControlFieldInputSearchResults
from "@/components/viewport/elements/controls/fields/search/ControlFieldInputSearchResults.vue";
import { searchAndSelectCustomer } from "@/components/shop/POSDepartmentProcess.vue";
import { metadata, vehicles } from "../objects/PosDepartmentStepMobileFlow.vue";
import VehicleCustomerSuggestionsPos from "@/components/forms/department/pos/input/vehicleCustomerSuggestionsPos.vue";
import {
actionButtons,
popups
} from "@/components/displays/department/pos/steps/mobile/objects/PosDepartmentStepMobileFlow.vue";
import SessionUser from "@/components/session/token/SessionUser.vue";
const emit = defineEmits<{
(e: 'close'): void;
}>();
/**
* Variables
*/
const searchQuery = ref("");
const searchResult = ref(/**{
"vat": 41004355,
"status": "Normal",
"name": "Truckwash ApS",
"address": "Letland Alle 2",
"zipcode": 2630,
"city": "Taastrup",
"protected": true,
"phone": "21754690",
"website": null,
"email": "mikkel@truckwash.dk",
"fax": null,
"startdate": "2019-12-11",
"enddate": null,
"employees": 14,
"industrycode": 953190,
"industrydesc": "Reparation og vedligeholdelse af motorkøretøjer i.a.n.",
"companytype": "APS",
"companydesc": "Anpartsselskab",
"owners": [
"DELOITTE STATSAUTORISERET REVISIONSPARTNERSELSKAB",
"MBL Revision I/S",
"WASH GROUP A/S"
]
}*/null);
const lastSearchResult = ref(null); // This is used to change the autofilled fields, when the search result changes
const searching = ref(false);
const error = ref(popups.get().props.error); // Used to display an error message if the customer creation fails
const search = (query: string) => {
return SessionUser.request("/cvr/search", "GET", {
query: query,
}).then((response) => {
console.warn(response, 'response');
searchResult.value = response.data.data;
}).catch((error) => {
console.error(error);
searchResult.value = null;
}).finally(() => {
searching.value = false;
changeAutofilledFields();
});
};
/**
* Form fields
*/
const cvr = ref("");
const companyPhone = ref("");
const invoiceEmail = ref("");
const contactEmail = ref("");
const contactPhone = ref("");
/**
* Autofill fields, when search result changes
* If the operator has changed the values in the fields, we want to keep them, so we can autofill them again when the search result changes
*/
const changeAutofilledFields = () => {
if (lastSearchResult.value !== null) {
cvr.value = "";
if (lastSearchResult.value.phone === companyPhone.value) {
companyPhone.value = "";
}
if (lastSearchResult.value.phone === contactPhone.value) {
contactPhone.value = "";
}
if (lastSearchResult.value.email === contactEmail.value) {
contactEmail.value = "";
}
if (lastSearchResult.value.email === invoiceEmail.value) {
invoiceEmail.value = "";
}
}
// Fill the fields with the new search result
if (searchResult.value !== null) {
if (cvr.value === "") {
cvr.value = searchResult.value.vat.toString();
}
if (companyPhone.value === "") {
companyPhone.value = searchResult.value.phone;
}
if (contactPhone.value === "") {
contactPhone.value = searchResult.value.phone;
}
if (contactEmail.value === "") {
contactEmail.value = searchResult.value.email;
}
if (invoiceEmail.value === "") {
invoiceEmail.value = searchResult.value.email;
}
}
lastSearchResult.value = searchResult.value;
}
/**
* Watch the validity of the fields and emit an event to the parent component
* If the fields are valid, we can enable the "Add Customer" button
*/
watch([cvr, companyPhone, invoiceEmail, contactEmail, contactPhone], (newValues) => {
// Remove the "Add Customer" button if it exists
const index = popups.get().actionButtons.findIndex((button) => button.label === "Tilføj kunde");
if (index !== -1) {
popups.get().actionButtons.splice(index, 1);
}
// Add the "Add Customer" button if all fields are filled
if (newValues.every((value) => value !== "")) {
popups.get().actionButtons.push({
label: "Tilføj kunde",
onClick: () => {
popups.get().actionButtons = []; // Remove all action buttons to prevent multiple clicks
try {
SessionUser.request("/auth/register/cvr", "POST", {
cvr: cvr.value,
companyPhone: parseInt(companyPhone.value),
invoiceEmail: invoiceEmail.value,
contactEmail: contactEmail.value,
contactPhone: parseInt(contactPhone.value),
searchResult: searchResult.value,
}).then((response) => {
console.warn(response, 'response register cvr');
searchAndSelectCustomer(companyPhone.value);
metadata.setCustomerId(parseInt(companyPhone.value));
emit('close');
}).catch((error) => {
console.error(error);
const errorResponse = SessionUser.functions.parseErrorMessage(error);
if (errorResponse !== null) {
popups.get().props.error = `Der opstod en fejl under oprettelsen af kunden: ${errorResponse}`;
// Reset the buttons
popups.get().actionButtons = popups.get().actionButtons = [{...actionButtons.default.value.cancel}];
return;
}
popups.get().props.error = "Der opstod en fejl under oprettelsen af kunden. Prøv igen.";
});
} catch (error) {
console.error(error);
popups.get().props.error = "Der opstod en fejl under oprettelsen af kunden. Prøv igen.";
}
},
color: "primary",
});
}
})
</script>
<template>
<div style="overflow-y: auto; overflow-x: hidden; height: 100%; padding-bottom: 10px;">
<div class="mx-3">
<div class="field">
<label class="label"><small>CVR</small></label>
<div class="control has-icons-right" :class="{'is-loading': searching}">
<input
v-model="searchQuery"
@input="searching = true; search(searchQuery);"
class="input is-searched"
:class="{'is-danger': searchResult === null, 'is-success': searchResult !== null && searchResult.name !== 'Ikke fundet'}"
type="text"
placeholder="Søg CVR nummer..."
/>
<span class="icon is-right" :class="{'has-text-danger': (searchResult === null || searchResult.name === 'Ikke fundet') && !searching, 'has-text-success': (searchResult !== null && searchResult.name !== 'Ikke fundet') && !searching}">
<i class="fas fa-check" v-if="(searchResult !== null && searchResult.name !== 'Ikke fundet') && !searching"></i>
<i class="fas fa-exclamation-triangle" v-else-if="(searchResult === null || searchResult.name === 'Ikke fundet') && !searching"></i>
<i class="fas fa-search" v-else-if="!searching"></i>
</span>
</div>
<p class="help" v-if="searchResult !== null && searchResult.name !== 'Ikke fundet'">{{ searchResult.name }}</p>
</div>
<!-- Company phone -->
<div class="field">
<label class="label"><small>Firma telefon</small></label>
<div class="control">
<input v-model="companyPhone" class="input" type="text" placeholder="Firma telefon" :disabled="searchResult === null" :class="{'is-danger': searchResult !== null && companyPhone === ''}"/>
</div>
</div>
<!-- Invoice email -->
<div class="field">
<label class="label"><small>Faktura email</small></label>
<div class="control">
<input v-model="invoiceEmail" class="input" type="text" placeholder="Faktura email" :disabled="searchResult === null" :class="{'is-danger': searchResult !== null && invoiceEmail === ''}"/>
</div>
</div>
<!-- Contact email -->
<div class="field">
<label class="label"><small>Kontakt email</small></label>
<div class="control">
<input v-model="contactEmail" class="input" type="text" placeholder="Kontakt email" :disabled="searchResult === null" :class="{'is-danger': searchResult !== null && contactEmail === ''}"/>
</div>
</div>
<!-- Contact phone -->
<div class="field">
<label class="label"><small>Kontakt telefon</small></label>
<div class="control">
<input v-model="contactPhone" class="input" type="text" placeholder="Kontakt telefon" :disabled="searchResult === null" :class="{'is-danger': searchResult !== null && contactPhone === ''}"/>
</div>
</div>
<!-- Error message -->
<div class="notification is-danger" v-if="popups.get().props.error !== null">
{{ popups.get().props.error}}
</div>
</div>
<!-- Search results -->
<!--{{ searchResult }}-->
</div>
</template>
<style scoped>
</style>
@@ -157,6 +157,16 @@ const defaultActionButtons = ref<{ [key: string]: PosActionButton }>({
},
color: 'primary'
},
addCustomer: {
label: 'Tilføj kunde',
description: 'Tilføj en ny kunde til systemet',
onClick: () => {
console.warn('Add Customer button clicked');
// Open the add customer popup
popups.select('add_customer', { props: { error: null, canCreate: true } });
},
color: 'primary',
},
editReference: {
label: 'Redigér reference',
description: 'Redigér reference for denne transaktion',
@@ -229,7 +239,7 @@ const addDefaultPopups = () => {
message: 'Bekræft venligst valget af kunde til denne transaktion',
component: 'select_customer',
style: {height: '50vh'},
actionButtons: [{...defaultActionButtons.value.cancel}],
actionButtons: [{...defaultActionButtons.value.cancel}, {...defaultActionButtons.value.addCustomer, label: 'Ny kunde'}],
});
// Completed
addPopup({
@@ -315,6 +325,18 @@ const addDefaultPopups = () => {
],
props: {base64String: null, filename: null}
});
// Add customer
addPopup({
id: 'add_customer',
title: 'Tilføj kunde',
message: 'Indtast venligst oplysninger for den nye kunde',
component: 'add_customer',
style: {maxHeight: '60vh'},
actionButtons: [
{...defaultActionButtons.value.cancel}
],
props: {cvr: null, contactEmail: null, contactPhone: null, companyPhone: null, invoiceEmail: null, searchResult: null, canCreate: false, error: null}
});
}
// Function to check if a popup is defined in the list
const isPopupDefined = (id: string): boolean => {
@@ -20,8 +20,10 @@ import PosDepartmentStepMobilePopupCustomerNotes
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobilePopupCustomerNotes.vue";
import PosDepartmentStepMobilePopupImage
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobilePopupImage.vue";
import PosDepartmentStepMobilePopupAddCustomer
from "@/components/displays/department/pos/steps/mobile/elements/PosDepartmentStepMobilePopupAddCustomer.vue";
export type PopupComponentKey = 'select_customer' | 'completed_transaction' | 'complete_booking' | 'error' | 'add_product_note' | 'select_vehicle' | 'change_reference' | 'customer_notes' | 'image_viewer';
export type PopupComponentKey = 'select_customer' | 'completed_transaction' | 'complete_booking' | 'error' | 'add_product_note' | 'select_vehicle' | 'change_reference' | 'customer_notes' | 'image_viewer' | 'add_customer';
export const PopupComponents = {
select_customer: PosDepartmentStepMobilePopupSelectCustomer,
@@ -33,6 +35,7 @@ export const PopupComponents = {
change_reference: PosDepartmentStepMobilePopupSetReference,
customer_notes: PosDepartmentStepMobilePopupCustomerNotes,
image_viewer: PosDepartmentStepMobilePopupImage,
add_customer: PosDepartmentStepMobilePopupAddCustomer
} as const;
export const popupComponentKeyToComponent = (key: PopupComponentKey) => PopupComponents[key];
@@ -64,6 +67,7 @@ export default defineComponent({
PosDepartmentStepMobilePopupSetReference,
PosDepartmentStepMobilePopupCustomerNotes,
PosDepartmentStepMobilePopupImage,
PosDepartmentStepMobilePopupAddCustomer
},
props: {
popup: {