Update prenotazione form with waiver handling improvements

Updated frontend changes documentation and prenotazione user form
to improve waiver (liberatoria) handling functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-12 16:56:52 +01:00
parent e0816bab45
commit b0f2420137
4 changed files with 548 additions and 210 deletions

View File

@@ -7,11 +7,15 @@ import { required } from '@vuelidate/validators';
import StrutturaService from '@/entities/struttura/struttura.service';
import UtenteAppService from '@/entities/utente-app/utente-app.service';
import LiberatoriaService from '@/entities/liberatoria/liberatoria.service';
import ModelloLiberatoriaService from '@/entities/modello-liberatoria/modello-liberatoria.service';
import { useAlertService } from '@/shared/alert/alert.service';
import { useDateFormat } from '@/shared/composables';
import { type IPrenotazione, Prenotazione } from '@/shared/model/prenotazione.model';
import { type IStruttura } from '@/shared/model/struttura.model';
import { type IUtenteApp } from '@/shared/model/utente-app.model';
import { type ILiberatoria, Liberatoria } from '@/shared/model/liberatoria.model';
import { type IModelloLiberatoria } from '@/shared/model/modello-liberatoria.model';
import { StatoPrenotazione } from '@/shared/model/enumerations/stato-prenotazione.model';
import PrenotazioneService from './prenotazione.service';
@@ -33,6 +37,15 @@ export default defineComponent({
const strutturaService = inject('strutturaService', () => new StrutturaService());
const strutturas: Ref<IStruttura[]> = ref([]);
const liberatoriaService = inject('liberatoriaService', () => new LiberatoriaService());
const modelloLiberatoriaService = inject('modelloLiberatoriaService', () => new ModelloLiberatoriaService());
// Liberatorie state
const modelloLiberatorias: Ref<IModelloLiberatoria[]> = ref([]);
const userLiberatorias: Ref<ILiberatoria[]> = ref([]);
const isLoadingLiberatorie = ref(false);
const selectedModello: Ref<IModelloLiberatoria | null> = ref(null);
const isSaving = ref(false);
const isLoading = ref(true);
const currentLanguage = inject('currentLanguage', () => computed(() => navigator.language ?? 'it'), true);
@@ -55,6 +68,50 @@ export default defineComponent({
};
const v$ = useVuelidate(validationRules, prenotazione as any);
// Computed properties for liberatorie
const liberatorieStatus = computed(() => {
if (!prenotazione.value.struttura?.id) return [];
// Filter ModelloLiberatoria for selected struttura
const strutturaModelli = modelloLiberatorias.value.filter(m => m.struttura?.id === prenotazione.value.struttura.id);
// Map to include acceptance status
return strutturaModelli.map(modello => {
const accepted = userLiberatorias.value.find(lib => lib.modelloLiberatoria?.id === modello.id);
return {
modello,
liberatoria: accepted || null,
isAccepted: !!accepted,
};
});
});
const allLiberatorieAccepted = computed(() => {
const status = liberatorieStatus.value;
return status.length === 0 || status.every(s => s.isAccepted);
});
const loadLiberatorieData = async () => {
if (!currentUser.value?.id) return;
try {
isLoadingLiberatorie.value = true;
// Fetch all ModelloLiberatoria (filter client-side)
const modelliRes = await modelloLiberatoriaService().retrieve();
modelloLiberatorias.value = modelliRes.data;
// Fetch all Liberatoria and filter by current user
const liberatorieRes = await liberatoriaService().retrieve();
userLiberatorias.value = liberatorieRes.data.filter((lib: ILiberatoria) => lib.utente?.id === currentUser.value.id);
isLoadingLiberatorie.value = false;
} catch (error) {
isLoadingLiberatorie.value = false;
alertService.showHttpError(error.response);
}
};
const initData = async () => {
try {
isLoading.value = true;
@@ -68,6 +125,9 @@ export default defineComponent({
const res = await strutturaService().retrieve();
strutturas.value = res.data;
// Load liberatorie data
await loadLiberatorieData();
isLoading.value = false;
} catch (error) {
isLoading.value = false;
@@ -99,6 +159,15 @@ export default defineComponent({
currentLanguage,
v$,
resetForm,
// Liberatorie additions
liberatoriaService,
modelloLiberatoriaService,
modelloLiberatorias,
userLiberatorias,
isLoadingLiberatorie,
selectedModello,
liberatorieStatus,
allLiberatorieAccepted,
...useDateFormat({ entityRef: prenotazione }),
t$,
};
@@ -113,6 +182,13 @@ export default defineComponent({
return;
}
// Check if all required liberatorie are accepted
if (!this.allLiberatorieAccepted) {
this.isSaving = false;
this.alertService.showError(this.t$('smartbookingApp.prenotazione.userForm.liberatorieRequired').toString());
return;
}
this.prenotazioneService()
.create(this.prenotazione)
.then(param => {
@@ -125,5 +201,45 @@ export default defineComponent({
this.alertService.showHttpError(error.response);
});
},
showLiberatoriaModal(modello: IModelloLiberatoria): void {
this.selectedModello = modello;
(<any>this.$refs.liberatoriaModal).show();
},
closeLiberatoriaModal(): void {
this.selectedModello = null;
(<any>this.$refs.liberatoriaModal).hide();
},
async acceptLiberatoria(): Promise<void> {
if (!this.selectedModello || !this.currentUser) return;
try {
this.isSaving = true;
// Create new Liberatoria
const newLiberatoria = new Liberatoria();
newLiberatoria.accettata = new Date();
newLiberatoria.utente = this.currentUser;
newLiberatoria.modelloLiberatoria = this.selectedModello;
const result = await this.liberatoriaService().create(newLiberatoria);
// Add to local state
this.userLiberatorias.push(result);
// Close modal
this.closeLiberatoriaModal();
// Show success message
this.alertService.showSuccess(this.t$('smartbookingApp.liberatoria.created', { param: result.id }).toString());
this.isSaving = false;
} catch (error) {
this.isSaving = false;
this.alertService.showHttpError(error.response);
}
},
},
});

View File

@@ -220,13 +220,65 @@
</div>
</div>
<!-- Liberatorie Section -->
<div class="card mb-4" v-if="prenotazione.struttura">
<div class="card-header">
<h4>{{ t$('smartbookingApp.prenotazione.userForm.liberatorie') }}</h4>
</div>
<div class="card-body">
<div v-if="isLoadingLiberatorie" class="text-center">
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div v-else-if="liberatorieStatus.length === 0" class="alert alert-info">
{{ t$('smartbookingApp.prenotazione.userForm.noLiberatorieRequired') }}
</div>
<div v-else class="list-group">
<div
v-for="item in liberatorieStatus"
:key="item.modello.id"
class="list-group-item d-flex justify-content-between align-items-center"
>
<div>
<span v-if="item.isAccepted" class="badge bg-success me-2">
<font-awesome-icon icon="check"></font-awesome-icon>
</span>
<span v-else class="badge bg-warning me-2">
<font-awesome-icon icon="exclamation-triangle"></font-awesome-icon>
</span>
<span v-if="item.isAccepted">
{{ item.modello.nome }}
</span>
<a v-else href="javascript:void(0)" @click="showLiberatoriaModal(item.modello)">
{{ item.modello.nome }}
</a>
</div>
<span v-if="item.isAccepted" class="text-muted small">
{{ t$('smartbookingApp.liberatoria.accettata') }}:
{{ new Date(item.liberatoria.accettata).toLocaleDateString('it-IT') }}
</span>
</div>
</div>
<div v-if="!allLiberatorieAccepted" class="alert alert-warning mt-3 mb-0">
<font-awesome-icon icon="exclamation-triangle"></font-awesome-icon>
{{ t$('smartbookingApp.prenotazione.userForm.liberatorieWarning') }}
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-secondary" @click="resetForm()" :disabled="isSaving">
<font-awesome-icon icon="undo"></font-awesome-icon>&nbsp;
<span>{{ t$('smartbookingApp.prenotazione.userForm.reset') }}</span>
</button>
<button type="submit" class="btn btn-primary" :disabled="v$.$invalid || isSaving">
<button type="submit" class="btn btn-primary" :disabled="v$.$invalid || isSaving || !allLiberatorieAccepted">
<font-awesome-icon icon="save"></font-awesome-icon>&nbsp;
<span>{{ t$('smartbookingApp.prenotazione.userForm.submit') }}</span>
</button>
@@ -234,5 +286,54 @@
</form>
</div>
</div>
<!-- Liberatoria Acceptance Modal -->
<b-modal ref="liberatoriaModal" size="lg" @hidden="closeLiberatoriaModal">
<template #title>
<h4>{{ selectedModello?.nome }}</h4>
</template>
<div v-if="selectedModello">
<!-- Modello Text -->
<div v-if="selectedModello.testo" class="mb-3">
<h5>{{ t$('smartbookingApp.modelloLiberatoria.testo') }}</h5>
<div class="border p-3 bg-light" style="white-space: pre-wrap">
{{ selectedModello.testo }}
</div>
</div>
<!-- Modello Document -->
<div v-if="selectedModello.documento" class="mb-3">
<h5>{{ t$('smartbookingApp.modelloLiberatoria.documento') }}</h5>
<a
:href="'data:' + selectedModello.documentoContentType + ';base64,' + selectedModello.documento"
:download="selectedModello.nome + '.pdf'"
class="btn btn-sm btn-outline-primary"
>
<font-awesome-icon icon="download"></font-awesome-icon>
{{ t$('entity.action.download') }}
</a>
</div>
<!-- Acceptance Statement -->
<div class="alert alert-info mt-4">
<strong>{{ t$('smartbookingApp.prenotazione.userForm.acceptanceStatement') }}</strong>
<p class="mb-0 mt-2">
Il sottoscritto <strong>{{ currentUser?.nome }} {{ currentUser?.cognome }}</strong>
presa visione della documentazione proposta accetta le condizioni ivi indicate.
</p>
</div>
</div>
<template #footer>
<button type="button" class="btn btn-secondary" @click="closeLiberatoriaModal" :disabled="isSaving">
{{ t$('entity.action.cancel') }}
</button>
<button type="button" class="btn btn-primary" @click="acceptLiberatoria" :disabled="isSaving">
<font-awesome-icon icon="check"></font-awesome-icon>
{{ t$('smartbookingApp.prenotazione.userForm.accept') }}
</button>
</template>
</b-modal>
</template>
<script lang="ts" src="./prenotazione-form-user.component.ts"></script>

View File

@@ -37,7 +37,13 @@
"societa": "società/associazione",
"companyData": "Dati della società/associazione",
"reset": "Annulla",
"submit": "Invia richiesta"
"submit": "Invia richiesta",
"liberatorie": "Liberatorie",
"noLiberatorieRequired": "Nessuna liberatoria richiesta per questa struttura",
"liberatorieWarning": "È necessario accettare tutte le liberatorie richieste prima di inviare la prenotazione",
"liberatorieRequired": "Devi accettare tutte le liberatorie richieste",
"acceptanceStatement": "Dichiarazione di accettazione",
"accept": "Accetta"
},
"StatoPrenotazione": {
"RICHIESTA": ""