242 lines
7.4 KiB
Vue
242 lines
7.4 KiB
Vue
<script setup>
|
|
import { computed, nextTick, onMounted, ref } from "vue";
|
|
import { useI18n } from "vue-i18n";
|
|
import CustomerSearchSelect from "@/components/search/economic/CustomerSearchSelect.vue";
|
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
|
|
|
const emit = defineEmits(["close", "created"]);
|
|
|
|
const { t } = useI18n();
|
|
|
|
const selectedCustomer = ref(null);
|
|
const registrationNumber = ref("");
|
|
const vehicleType = ref("");
|
|
const washSubscription = ref(false);
|
|
const reference = ref("");
|
|
const vehicleTypeOptions = ref([]);
|
|
const isLoadingVehicleTypes = ref(false);
|
|
const isSubmitting = ref(false);
|
|
const errorMessage = ref("");
|
|
const vehicleTypeError = ref("");
|
|
|
|
const getCustomerNumber = (customer) => {
|
|
const parsedValue = Number.parseInt(
|
|
String(customer?.customerNumber ?? customer?.customer_number ?? customer?.customer_id ?? customer?.id ?? ""),
|
|
10
|
|
);
|
|
|
|
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
|
|
};
|
|
|
|
const selectedCustomerNumber = computed(() => getCustomerNumber(selectedCustomer.value));
|
|
|
|
const normalizedVehicleType = computed(() => {
|
|
if (vehicleType.value === "") {
|
|
return null;
|
|
}
|
|
|
|
const parsedValue = Number.parseInt(String(vehicleType.value), 10);
|
|
return Number.isInteger(parsedValue) && parsedValue >= 0 ? parsedValue : null;
|
|
});
|
|
|
|
const normalizedRegistrationNumber = computed(() => {
|
|
return registrationNumber.value.trim().toUpperCase();
|
|
});
|
|
|
|
const canSubmit = computed(() => {
|
|
return (
|
|
!isSubmitting.value &&
|
|
!isLoadingVehicleTypes.value &&
|
|
selectedCustomerNumber.value !== null &&
|
|
normalizedRegistrationNumber.value.length > 0 &&
|
|
normalizedVehicleType.value !== null
|
|
);
|
|
});
|
|
|
|
const parseErrorMessage = (error) => {
|
|
return SessionUser.functions.parseErrorMessage(error) || t("vehicles.add_modal.error");
|
|
};
|
|
|
|
const loadVehicleTypes = async () => {
|
|
isLoadingVehicleTypes.value = true;
|
|
vehicleTypeError.value = "";
|
|
|
|
try {
|
|
vehicleTypeOptions.value = await SessionUser.objects.vehicles.columns.type.options();
|
|
} catch (error) {
|
|
vehicleTypeError.value = parseErrorMessage(error) || t("vehicles.add_modal.type_load_error");
|
|
} finally {
|
|
isLoadingVehicleTypes.value = false;
|
|
}
|
|
};
|
|
|
|
const closeModal = () => {
|
|
if (!isSubmitting.value) {
|
|
emit("close");
|
|
}
|
|
};
|
|
|
|
const submitVehicle = async () => {
|
|
if (!canSubmit.value) {
|
|
errorMessage.value = t("vehicles.add_modal.validation_error");
|
|
return;
|
|
}
|
|
|
|
isSubmitting.value = true;
|
|
errorMessage.value = "";
|
|
|
|
try {
|
|
const response = await SessionUser.objects.vehicles.add(
|
|
normalizedVehicleType.value,
|
|
normalizedRegistrationNumber.value,
|
|
washSubscription.value,
|
|
selectedCustomerNumber.value,
|
|
reference.value.trim() || null
|
|
);
|
|
|
|
emit("created", response);
|
|
} catch (error) {
|
|
errorMessage.value = parseErrorMessage(error);
|
|
} finally {
|
|
isSubmitting.value = false;
|
|
}
|
|
};
|
|
|
|
onMounted(async () => {
|
|
await loadVehicleTypes();
|
|
await nextTick();
|
|
document.getElementById("superuser-add-vehicle-customer-search")?.focus();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="modal is-active" data-testid="superuser-add-vehicle-modal">
|
|
<div class="modal-background" @click="closeModal"></div>
|
|
<div class="modal-card superuser-add-vehicle-modal">
|
|
<header class="modal-card-head">
|
|
<p class="modal-card-title">{{ t("vehicles.add_modal.title") }}</p>
|
|
<button
|
|
class="delete"
|
|
type="button"
|
|
:aria-label="t('common.close')"
|
|
data-testid="superuser-add-vehicle-close"
|
|
@click="closeModal"
|
|
></button>
|
|
</header>
|
|
|
|
<section class="modal-card-body">
|
|
<div v-if="errorMessage" class="notification is-danger is-light" data-testid="superuser-add-vehicle-error">
|
|
{{ errorMessage }}
|
|
</div>
|
|
|
|
<CustomerSearchSelect
|
|
v-model="selectedCustomer"
|
|
input-id="superuser-add-vehicle-customer-search"
|
|
test-id-prefix="superuser-add-vehicle-customer"
|
|
:disabled="isSubmitting"
|
|
:placeholder="t('vehicles.add_modal.customer_placeholder')"
|
|
/>
|
|
|
|
<div class="field">
|
|
<label class="label" for="superuser-add-vehicle-registration">{{ t("vehicles.form.license_plate") }}</label>
|
|
<div class="control">
|
|
<input
|
|
id="superuser-add-vehicle-registration"
|
|
v-model="registrationNumber"
|
|
class="input"
|
|
type="text"
|
|
autocomplete="off"
|
|
:placeholder="t('vehicles.add_modal.registration_placeholder')"
|
|
:disabled="isSubmitting"
|
|
data-testid="superuser-add-vehicle-registration"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label" for="superuser-add-vehicle-type">{{ t("vehicles.form.type") }}</label>
|
|
<div class="control" :class="{ 'is-loading': isLoadingVehicleTypes }">
|
|
<div class="select is-fullwidth">
|
|
<select
|
|
id="superuser-add-vehicle-type"
|
|
v-model="vehicleType"
|
|
:disabled="isSubmitting || isLoadingVehicleTypes || vehicleTypeOptions.length === 0"
|
|
data-testid="superuser-add-vehicle-type"
|
|
>
|
|
<option disabled value="">{{ t("vehicles.add_modal.type_placeholder") }}</option>
|
|
<option v-for="option in vehicleTypeOptions" :key="option.id" :value="option.id">
|
|
{{ option.name }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<p v-if="vehicleTypeError" class="help is-danger" data-testid="superuser-add-vehicle-type-error">
|
|
{{ vehicleTypeError }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="checkbox">
|
|
<input
|
|
v-model="washSubscription"
|
|
type="checkbox"
|
|
:disabled="isSubmitting"
|
|
data-testid="superuser-add-vehicle-wash-subscription"
|
|
/>
|
|
{{ t("objects.vehicles.columns.wash_subscription") }}
|
|
</label>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label" for="superuser-add-vehicle-reference">{{ t("common.reference") }}</label>
|
|
<div class="control">
|
|
<input
|
|
id="superuser-add-vehicle-reference"
|
|
v-model="reference"
|
|
class="input"
|
|
type="text"
|
|
autocomplete="off"
|
|
:placeholder="t('vehicles.add_modal.reference_placeholder')"
|
|
:disabled="isSubmitting"
|
|
data-testid="superuser-add-vehicle-reference"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<footer class="modal-card-foot is-justify-content-flex-end">
|
|
<button
|
|
class="button"
|
|
type="button"
|
|
:disabled="isSubmitting"
|
|
data-testid="superuser-add-vehicle-cancel"
|
|
@click="closeModal"
|
|
>
|
|
{{ t("common.cancel") }}
|
|
</button>
|
|
<button
|
|
class="button is-link"
|
|
type="button"
|
|
:class="{ 'is-loading': isSubmitting }"
|
|
:disabled="!canSubmit"
|
|
data-testid="superuser-add-vehicle-submit"
|
|
@click="submitVehicle"
|
|
>
|
|
{{ t("vehicles.add_modal.submit") }}
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.superuser-add-vehicle-modal {
|
|
max-width: min(44rem, calc(100vw - 2rem));
|
|
width: 100%;
|
|
}
|
|
|
|
.modal-card-body {
|
|
overflow: visible;
|
|
}
|
|
</style>
|