Add force enable/disable machine actions to ActionSettingsWheelButton

- Implemented new actions for force enabling and disabling machines in `ActionSettingsWheelButton.vue`.
- Display success message on login in `LoginForm.vue` and handle reload with delay.
- Adjusted form input handling and error/success message display across auth-related components and forms.
- Included form ID attributes and improved message visibility for user feedback.
This commit is contained in:
Jeppe Bundgaard
2026-02-23 19:44:29 +01:00
parent 159445467a
commit 7614ef482d
8 changed files with 131 additions and 108 deletions
@@ -339,6 +339,28 @@ const defaultActions = computed(() => {
return !!props.department_lane_id && SessionUser.canAccessSuperUser();
},
},
{
icon: 'fas fa-play',
label: t('superuser.department_lane.force_enable_machine'),
template: 'success',
clickAction: () => {
return SessionUser.objects.department_lanes.functions.forceEnableMachine(props.department_lane_id);
},
showFunction: () => {
return !!props.department_lane_id && SessionUser.canAccessSuperUser();
},
},
{
icon: 'fas fa-stop',
label: t('superuser.department_lane.force_disable_machine'),
template: 'danger',
clickAction: () => {
return SessionUser.objects.department_lanes.functions.forceDisableMachine(props.department_lane_id);
},
showFunction: () => {
return !!props.department_lane_id && SessionUser.canAccessSuperUser();
},
},
/** Orders actions */
{
label: SessionUser.functions.ucFirst(SessionUser.objects.orders.meta.labels.single),
@@ -728,6 +750,22 @@ const defaultActions = computed(() => {
icon="fas fa-external-link-alt"
:click-action="() => SessionUser.functions.redirectTo.superUser('/department/lanes/' + props.department_lane_id, true)"
/>
<!-- Force enable machine -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
:label="t('superuser.department_lane.force_enable_machine')"
icon="fas fa-play"
template="success"
:click-action="() => SessionUser.objects.department_lanes.functions.forceEnableMachine(props.department_lane_id)"
/>
<!-- Force disable machine -->
<action-settings-wheel-item
v-if="SessionUser.canAccessSuperUser()"
:label="t('superuser.department_lane.force_disable_machine')"
icon="fas fa-stop"
template="danger"
:click-action="() => SessionUser.objects.department_lanes.functions.forceDisableMachine(props.department_lane_id)"
/>
</template>
<!-- If there's a user_id, show the user actions -->
<template v-if="hasUser()">
@@ -1,10 +1,7 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useToast } from 'vue-toast-notification';
import { ref } from 'vue';
const { t } = useI18n();
const toast = useToast();
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import { usePaginatedListInstance } from "@/components/pagination/paginatedList.vue";
@@ -13,36 +10,6 @@ import EditableTableColumn from "@/components/displays/buttons/EditableTableColu
import { SessionUser } from "@/components/session/token/SessionUser.vue";
const forceLoadingLaneId = ref(null);
const forceEnableMachine = async (laneId) => {
forceLoadingLaneId.value = laneId;
try {
await SessionUser.objects.department_lanes.functions.forceEnableMachine(laneId);
toast.success(t('superuser.department_lane.force_enable_success'));
await loadList();
} catch (error) {
console.error("Failed to force enable machine:", error);
toast.error(t('superuser.department_lane.force_error'));
} finally {
forceLoadingLaneId.value = null;
}
};
const forceDisableMachine = async (laneId) => {
forceLoadingLaneId.value = laneId;
try {
await SessionUser.objects.department_lanes.functions.forceDisableMachine(laneId);
toast.success(t('superuser.department_lane.force_disable_success'));
await loadList();
} catch (error) {
console.error("Failed to force disable machine:", error);
toast.error(t('superuser.department_lane.force_error'));
} finally {
forceLoadingLaneId.value = null;
}
};
// Define the props
const props = defineProps({
objects: {
@@ -143,34 +110,6 @@ const redirect = (path) => {
<!-- Actions -->
<td class="has-text-right">
<div class="columns is-mobile is-vcentered is-gapless is-justify-content-flex-end">
<div class="column is-narrow">
<!-- Force Enable button -->
<button
class="button is-small is-success mr-1"
:class="{ 'is-loading': forceLoadingLaneId === object.id }"
:disabled="forceLoadingLaneId === object.id"
@click="forceEnableMachine(object.id)"
:title="$t('superuser.department_lane.force_enable_machine')"
>
<span class="icon is-small">
<i class="fas fa-play"></i>
</span>
</button>
</div>
<div class="column is-narrow">
<!-- Force Disable button -->
<button
class="button is-small is-danger mr-1"
:class="{ 'is-loading': forceLoadingLaneId === object.id }"
:disabled="forceLoadingLaneId === object.id"
@click="forceDisableMachine(object.id)"
:title="$t('superuser.department_lane.force_disable_machine')"
>
<span class="icon is-small">
<i class="fas fa-stop"></i>
</span>
</button>
</div>
<div class="column is-narrow">
<!-- Stop button -->
<button
@@ -90,12 +90,12 @@ SessionUser.auth.reCAPTCHA.preCheck.get().then((response) => {
<form @submit.prevent="doNothing" class="mx-auto">
<div class="form-field">
<label class="label">User ID</label>
<input type="text" v-model="user_id">
<input type="text" v-model="user_id" name="user_id">
</div>
<div class="form-field">
<label class="label">Adgangskode</label>
<input type="password" v-model="employee_key" />
<input type="password" v-model="employee_key" name="password">
</div>
<div class="field mt-6 mx-auto" v-if="reCAPTCHA_data.enabled">
@@ -104,11 +104,11 @@ SessionUser.auth.reCAPTCHA.preCheck.get().then((response) => {
</div>
</div>
<p class="help is-danger" v-if="getError('auth')">
<p class="help is-danger" v-if="getError('auth')" id="user_auth_alert_error">
{{ getError('auth') }}
</p>
<LoadButtonWhileAwait :disabled="!user_id || !employee_key" :loadFunction="login" class="submit">Login</LoadButtonWhileAwait>
<LoadButtonWhileAwait :disabled="!user_id || !employee_key" :loadFunction="login" class="submit" id="operator_login_button">Login</LoadButtonWhileAwait>
</form>
<div v-if="false">
<div class="columns is-multiline is-centered">
+18 -6
View File
@@ -11,6 +11,7 @@ import { SessionUser } from "@/components/session/token/SessionUser.vue";
const customer_number = ref();
const password = ref('');
const error = ref('');
const successMessage = ref('');
const store = useStore();
const router = useRouter();
@@ -24,8 +25,13 @@ const login = async () => {
});
// Save the token in the local storage
localStorage.setItem('token', response.data.data.token);
// Reload the router to update the session
window.location.href = '/user';
// Set the success message
successMessage.value = "Du er nu logget ind!"
// Wait 500ms before reloading the page to show the success message
setTimeout(() => {
// Reload the page to update the UI
router.go(0);
}, 500);
// window.location.reload();
} catch (e) {
parseError(e, 'auth');
@@ -69,11 +75,11 @@ SessionUser.auth.reCAPTCHA.preCheck.get().then((response) => {
<form @submit.prevent="doNothing">
<div class="form-field">
<label class="label">{{ $t('auth.customer_number') }}</label>
<input type="text" v-model="customer_number" />
<input type="text" v-model="customer_number" name="customer_number" />
</div>
<div class="form-field">
<label class="label">{{ $t('auth.password') }}</label>
<input type="password" v-model="password" />
<input type="password" v-model="password" name="password" />
</div>
<!-- reCaptcha, if enabled -->
<div class="field mt-6" v-if="reCAPTCHA_data.enabled">
@@ -83,15 +89,21 @@ SessionUser.auth.reCAPTCHA.preCheck.get().then((response) => {
</div>
<!-- Display the error -->
<div class="field" v-if="getError('auth')">
<p class="help is-danger">
<p class="help is-danger" id="user_auth_alert_error">
{{ getError('auth') }}
</p>
</div>
<div class="field" v-if="successMessage">
<!-- Success message for login -->
<p class="help is-success" id="user_auth_alert_success">
{{ successMessage }}
</p>
</div>
<div class="form-field">
<label><input type="checkbox" id="remember" name="remember">{{ $t('auth.remember_me') }}</label>
</div>
<router-link :to="{name: 'password-reset'}" class="">{{ $t('auth.forgot_password') }}</router-link>
<router-link :to="{name: 'password-reset'}" class="" id="forgot-password-button">{{ $t('auth.forgot_password') }}</router-link>
<LoadButtonWhileAwait :loadFunction="login" class="submit" id="login-button">{{ $t('auth.login') }}</LoadButtonWhileAwait>
<router-link :to="{name: 'loginqr'}" class="submit">{{ $t('auth.login_with_qr_code') }}</router-link>
</form>
@@ -50,12 +50,13 @@ const doNothing = () => {
<!-- Login method selector -->
<div class="form-field">
<label class="label">{{ t('auth.login_with') }}</label>
<div class="login-method-selector">
<div class="login-method-selector" id="subuser_login_method_selector">
<button
type="button"
class="method-btn"
:class="{ active: loginMethod === 'phone' }"
@click="loginMethod = 'phone'"
id="subuser_login_method_phone_button"
>
{{ t('auth.phone') }}
</button>
@@ -64,6 +65,7 @@ const doNothing = () => {
class="method-btn"
:class="{ active: loginMethod === 'username' }"
@click="loginMethod = 'username'"
id="subuser_login_method_username_button"
>
{{ t('auth.username') }}
</button>
@@ -76,13 +78,13 @@ const doNothing = () => {
<div class="column is-narrow">
<div class="form-field">
<label class="label">{{ t('auth.country_code') }}</label>
<input type="text" v-model="phone_country_code" placeholder="45" pattern="[1-9][0-9]*" inputmode="numeric" @focusout="phone_country_code = phone_country_code.replace(/\D/g, '')" class="input-country-code"/>
<input type="text" v-model="phone_country_code" placeholder="45" pattern="[1-9][0-9]*" inputmode="numeric" @focusout="phone_country_code = phone_country_code.replace(/\D/g, '')" class="input-country-code" name="phone_country_code"/>
</div>
</div>
<div class="column">
<div class="form-field">
<label class="label">{{ t('auth.phone_number') }}</label>
<input type="tel" v-model="phone" placeholder="12345678" class="input-phone-number"/>
<input type="tel" v-model="phone" placeholder="12345678" class="input-phone-number" name="phone" pattern="[0-9]*" inputmode="numeric" @focusout="phone = phone.replace(/\D/g, '')" />
</div>
</div>
</div>
@@ -92,19 +94,19 @@ const doNothing = () => {
<template v-else>
<div class="form-field">
<label class="label">{{ t('auth.username') }}</label>
<input type="text" v-model="username" :placeholder="t('auth.your_username')" />
<input type="text" v-model="username" :placeholder="t('auth.your_username')" name="username" />
</div>
</template>
<!-- Password field -->
<div class="form-field">
<label class="label">{{ t('auth.password') }}</label>
<input type="password" v-model="password" />
<input type="password" v-model="password" name="password" />
</div>
<!-- Display the error -->
<div class="field" v-if="getError('auth')">
<p class="help is-danger">
<p class="help is-danger" id="subuser_auth_alert_error">
{{ getError('auth') }}
</p>
</div>
+53 -21
View File
@@ -5,29 +5,36 @@ import axios from "axios";
import {API_URL} from "@/config.js";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
const driverFormData = reactive({
const customerFormData = reactive({
cvr: null,
contactEmail: null,
contactPhone: null
})
const driverFormData = reactive({
driverCvr: null,
driverPhone: null,
})
const deliverSuccess = ref(false)
let deliverTimeoutId;
const driverDeliverSuccess = ref(false)
const submitDriver = async () => {
let deliverTimeoutId;
let driverDeliverTimeoutId;
const submitCustomer = async () => {
removeError('authRegisterCvr')
try {
await axios.post(API_URL + '/auth/register/cvr', {
cvr: driverFormData.cvr,
invoiceEmail: driverFormData.contactEmail,
contactEmail: driverFormData.contactEmail,
contactPhone: driverFormData.contactPhone,
companyPhone: driverFormData.contactPhone
cvr: customerFormData.cvr,
invoiceEmail: customerFormData.contactEmail,
contactEmail: customerFormData.contactEmail,
contactPhone: customerFormData.contactPhone,
companyPhone: customerFormData.contactPhone
});
driverFormData.contactPhone = null;
driverFormData.contactEmail = null;
driverFormData.cvr = null;
customerFormData.contactPhone = null;
customerFormData.contactEmail = null;
customerFormData.cvr = null;
deliverSuccess.value = true
@@ -40,6 +47,26 @@ const submitDriver = async () => {
}
}
const submitDriver = async () => {
removeError('authRegisterDriverCvr');
try {
await axios.post(API_URL + '/subusers', {
cvr: driverFormData.driverCvr,
phone: driverFormData.driverPhone,
phone_country_code: '45',
});
driverDeliverSuccess.value = true
driverDeliverTimeoutId = setTimeout(() => {
driverDeliverSuccess.value = false
}, 3500)
} catch (error) {
parseError(error, 'authRegisterDriverCvr');
}
}
onBeforeUnmount(() => {
if (deliverTimeoutId) clearTimeout(deliverTimeoutId);
})
@@ -72,29 +99,29 @@ onBeforeUnmount(() => {
<form class="truckwash-form" @submit.prevent="() => {}" method="post">
<div class="form-field">
<label for="customer_cvr">{{ $t('customer_creation.customer.cvr_label') }}</label>
<input type="text" id="customer_cvr" v-model="driverFormData.cvr" placeholder="12345678">
<input type="text" id="customer_cvr" v-model="customerFormData.cvr" placeholder="12345678">
</div>
<div class="form-field">
<label for="email">{{ $t('customer_creation.customer.email_label') }}</label>
<input type="email" id="email" v-model="driverFormData.contactEmail" name="email">
<input type="email" id="email" v-model="customerFormData.contactEmail" name="email">
</div>
<div class="form-field">
<label for="customer_phone">{{ $t('customer_creation.customer.phone_label') }}</label>
<input type="text" id="customer_phone" v-model="driverFormData.contactPhone" name="phone" placeholder="12345678">
<input type="text" id="customer_phone" v-model="customerFormData.contactPhone" name="phone" placeholder="12345678">
</div>
<p class="help is-danger" v-if="getError('authRegisterCvr')">
<p class="help is-danger" v-if="getError('authRegisterCvr')" id="customer_creation_alert_error">
{{ getError('authRegisterCvr') }}
</p>
<p class="help is-success" v-if="deliverSuccess">
<p class="help is-success" v-if="deliverSuccess" id="customer_creation_alert_success">
{{ $t('customer_creation.customer.success_message') }}
</p>
<LoadButtonWhileAwait :disabled="!driverFormData.cvr || !driverFormData.contactPhone || !driverFormData.contactEmail" :loadFunction="submitDriver" class="truckwash-submit">{{ $t('customer_creation.customer.submit') }}</LoadButtonWhileAwait>
<LoadButtonWhileAwait :disabled="!customerFormData.cvr || !customerFormData.contactPhone || !customerFormData.contactEmail" :loadFunction="submitCustomer" class="truckwash-submit" id="customer-register-button">{{ $t('customer_creation.customer.submit') }}</LoadButtonWhileAwait>
</form>
</div>
@@ -112,18 +139,23 @@ onBeforeUnmount(() => {
<li><strong>{{ $t('customer_creation.driver.benefit_loyalty') }}</strong> {{ $t('customer_creation.driver.benefit_loyalty_desc') }}</li>
</ul>
<form class="truckwash-form" method="post">
<form class="truckwash-form" @submit.prevent="() => {}" method="post">
<div class="form-field">
<label for="driver_cvr">{{ $t('customer_creation.driver.cvr_label') }}</label>
<input type="text" id="driver_cvr" name="cvr" placeholder="12345678">
<input type="text" id="driver_cvr" name="cvr" placeholder="12345678" v-model="driverFormData.driverCvr">
</div>
<div class="form-field">
<label for="driver_phone">{{ $t('customer_creation.driver.phone_label') }}</label>
<input type="text" id="driver_phone" name="phone" placeholder="12345678">
<input type="text" id="driver_phone" name="phone" placeholder="12345678" v-model="driverFormData.driverPhone">
</div>
<LoadButtonWhileAwait :disabled="true" class="truckwash-submit">{{ $t('customer_creation.driver.submit') }}</LoadButtonWhileAwait>
<p class="help is-danger" v-if="getError('authRegisterDriverCvr')" id="driver_creation_alert_error">
{{ getError('authRegisterDriverCvr') }}
</p>
<p class="help is-success" v-if="driverDeliverSuccess" id="driver_creation_alert_success">{{ $t('customer_creation.driver.success_message') }}</p>
<LoadButtonWhileAwait :disabled="!driverFormData.driverCvr || !driverFormData.driverPhone" class="truckwash-submit" id="driver-register-button" :loadFunction="submitDriver">{{ $t('customer_creation.driver.submit') }}</LoadButtonWhileAwait>
</form>
</div>
@@ -84,7 +84,7 @@ onBeforeUnmount(() => {
<!-- If the password has been successfully reset, show a success message and a link to the login page -->
<p v-if="hasSetPassword" class="help is-success">Password reset successful. Please log in.</p>
<form v-if="isTokenValid === false">
<p class="help is-danger" v-if="getError('resetPasswordValidate')">
<p class="help is-danger" v-if="getError('resetPasswordValidate')" id="password_reset_alert_error">
{{ getError('resetPasswordValidate') }}
</p>
<router-link :to="{name: 'password-reset'}" class="submit">{{ $t('password_reset.restart') }}</router-link>
@@ -92,14 +92,14 @@ onBeforeUnmount(() => {
<form v-if="isTokenValid === true" @submit.prevent="() => {}">
<div class="form-field">
<label class="label">{{ $t('password_reset.new_password') }}</label>
<input type="text" v-model="newPassword" />
<input type="password" v-model="newPassword" />
</div>
<p class="help is-danger" v-if="getError('resetPasswordConfirm')">
<p class="help is-danger" v-if="getError('resetPasswordConfirm')" id="password_reset_confirm_alert_error">
{{ getError('resetPasswordConfirm') }}
</p>
<p class="help is-success" v-if="message">
<p class="help is-success" v-if="message" id="password_reset_confirm_alert_success">
{{ message }}
</p>
+6 -6
View File
@@ -18,10 +18,10 @@ const resetPassword = async () => {
customer_number: customer_number.value,
});
message.value = response.data.message;
message.value = response.data.data.message;
timeoutId.value = setTimeout(() => {
message.value = null;
}, 3000)
}, 5000);
} catch (error) {
parseError(error, 'resetPassword');
}
@@ -45,18 +45,18 @@ onBeforeUnmount(() => {
<form @submit.prevent="() => {}">
<div class="form-field">
<label class="label">{{ $t('password_reset.customer_number') }}</label>
<input type="text" v-model="customer_number" />
<input type="text" v-model="customer_number" name="customer_number" id="password_reset_customer_number_input" />
</div>
<p class="help is-danger" v-if="getError('resetPassword')">
<p class="help is-danger" v-if="getError('resetPassword')" id="password_reset_alert_error">
{{ getError('resetPassword') }}
</p>
<p class="help is-success" v-if="message">
<p class="help is-success" v-if="message" id="password_reset_alert_success">
{{ message }}
</p>
<LoadButtonWhileAwait :disabled="!customer_number" :loadFunction="resetPassword" class="submit">{{ $t('password_reset.submit') }}</LoadButtonWhileAwait>
<LoadButtonWhileAwait :disabled="!customer_number" :loadFunction="resetPassword" class="submit" id="password-reset-button">{{ $t('password_reset.submit') }}</LoadButtonWhileAwait>
<router-link :to="{name: 'login'}" class="submit">{{ $t('password_reset.back_to_login') }}</router-link>
</form>
</div>