fix css e accessibilità, luogo required

This commit is contained in:
Flavio Bontà 2025-11-14 10:31:47 +01:00
parent 94eee4a484
commit ca10e6ebb4
5 changed files with 211 additions and 166 deletions

View File

@ -24,12 +24,12 @@ export interface StrutturePubblicheControllerFindManyStrutture$Params {
/**
* Luogo calcola distanza da
*/
luogo?: string | null;
luogo: string;
/**
* Tipo luogo calcola distanza da
*/
'luogo.tipo'?: string | null;
'luogo.tipo': string;
/**
* Tipo della struttura
@ -40,7 +40,7 @@ export interface StrutturePubblicheControllerFindManyStrutture$Params {
export function strutturePubblicheControllerFindManyStrutture(
http: HttpClient,
rootUrl: string,
params?: StrutturePubblicheControllerFindManyStrutture$Params,
params: StrutturePubblicheControllerFindManyStrutture$Params,
context?: HttpContext,
): Observable<StrictHttpResponse<Array<StrutturePubblicheResDto>>> {
const rb = new RequestBuilder(

View File

@ -39,7 +39,7 @@ export class StrutturePubblicheApiClient extends BaseService {
* This method doesn't expect any request body.
*/
strutturePubblicheControllerFindManyStrutture$Response(
params?: StrutturePubblicheControllerFindManyStrutture$Params,
params: StrutturePubblicheControllerFindManyStrutture$Params,
context?: HttpContext,
): Observable<StrictHttpResponse<Array<StrutturePubblicheResDto>>> {
return strutturePubblicheControllerFindManyStrutture(
@ -61,7 +61,7 @@ export class StrutturePubblicheApiClient extends BaseService {
* This method doesn't expect any request body.
*/
strutturePubblicheControllerFindManyStrutture(
params?: StrutturePubblicheControllerFindManyStrutture$Params,
params: StrutturePubblicheControllerFindManyStrutture$Params,
context?: HttpContext,
): Observable<Array<StrutturePubblicheResDto>> {
return this.strutturePubblicheControllerFindManyStrutture$Response(

View File

@ -3,26 +3,38 @@
<div class="flex flex-col md:flex-row justify-between items-center md:items-end gap-4 md:gap-6">
<div>
<img src="assets/images/MUSA/logo.png"
alt="poseidon-layout"
alt="logo-consorzio-musa"
class="block mx-auto"
style="height: 50px" />
</div>
<div class="flex-1 flex flex-col text-center md:text-left">
<div class="text-surface-900 dark:text-surface-0 text-2xl font-semibold leading-tight">
<h1 class="text-surface-900 dark:text-surface-0 text-2xl font-semibold leading-tight">
Consorzio Mu.Sa.
</div>
<div class="text-surface-500 dark:text-surface-200 text-xl font-normal leading-tight">
</h1>
<h2 class="text-surface-500 dark:text-surface-200 text-xl font-normal leading-tight">
Cerca strutture convenzionate
</div>
</h2>
</div>
</div>
<p-divider class="w-full my-0!"></p-divider>
<form [formGroup]="cercaStruttureForm"
(ngSubmit)="getStrutture()">
<div class="flex flex-wrap gap-4 items-center">
<div class="flex flex-wrap gap-8 md:gap-12 items-start">
<div
class="flex-1 bg-surface-0 dark:bg-surface-950 flex flex-col justify-start items-start gap-8 md:gap-12 w-full">
<div class="self-stretch flex flex-col justify-start items-start gap-6">
<div class="self-stretch flex flex-col justify-start items-start gap-6">
<div
class="self-stretch justify-start text-surface-900 dark:text-surface-0 text-lg md:text-xl font-semibold leading-tight">
Strutture Convenzionate</div>
<div class="self-stretch flex flex-col justify-start items-start gap-4">
<div class="self-stretch flex flex-col justify-start items-start gap-2">
<div id="tipoStruttura"
class="self-stretch justify-start text-surface-900 dark:text-surface-0 text-base font-normal leading-tight">
Tipologia struttura</div>
<p-dropdown [options]="vm.tipologieStrutture"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0 lg:w-72"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0"
name="tiplogiaStruttura"
appendTo="body"
formControlName="tipologiaStruttura"
@ -30,29 +42,45 @@
showClear="true"
optionLabel="descrizione"
optionValue="codice"
ariaLabelledBy="tipoStruttura"
[filter]="true"></p-dropdown>
<!-- {{vm.filteredLuoghiEsteso | json}}
{{cercaStruttureForm.controls['luogo'].value | json}} -->
<!-- <p-autocomplete [(ngModel)]="modelLuogo"
[ngModelOptions]="{ standalone: true }"
[suggestions]="vm.filteredLuoghiEsteso"
(completeMethod)="fetchLuogo($event, 1)"
optionLabel="tipo" /> -->
<p-autoComplete #acSt formControlName="luogo"
</div>
</div>
<div class="self-stretch flex flex-col md:flex-row justify-start items-start gap-4">
<div class="flex-1 flex flex-col justify-start items-start gap-2 w-full">
<div id="luogoAutocomplete"
class="self-stretch justify-start text-surface-900 dark:text-surface-0 text-base font-normal leading-tight">
Luogo<small class="text-red-600">*
@if (
cercaStruttureForm.controls.luogo.errors?.['required'] &&
vm.cercaStruttureFormSubmitted
) {
Luogo obbligatorio
}
</small></div>
<p-autoComplete #acSt
formControlName="luogo"
[suggestions]="vm.filteredLuoghiEsteso"
(completeMethod)="fetchLuogo($event, 1)"
(onSelect)="state.set({ filteredLuoghiEsteso: [] })"
(onHide)="checkLuogo()"
styleClass="w-full"
inputStyleClass="w-full"
placeholder="Cerca per città, provincia, stato o regione"
placeholder="Cerca per città, provincia o regione"
appendTo="body"
minLength="1"
[delay]="500"
emptyMessage="Nessun luogo trovato"
[showClear]="true"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0 lg:w-72"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0"
[ngClass]="{
'ng-invalid ng-dirty':
cercaStruttureForm.controls.luogo.errors?.['required'] &&
vm.cercaStruttureFormSubmitted
}"
dataKey="dataKey"
ariaLabelledBy="luogoAutocomplete"
optionLabel="luogo">
<ng-template let-luogo
#item>
@ -119,32 +147,42 @@
</div>
</ng-template>
</p-autoComplete>
</div>
<div class="flex-1 flex flex-col justify-start items-start gap-2 w-full">
<div id="indirizzo"
class="self-stretch justify-start text-surface-900 dark:text-surface-0 text-base font-normal leading-tight">
Indirizzo</div>
<input type="text"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0 lg:w-72"
class="flex-auto lg:flex-1 lg:mt-0 w-full mr-0 lg:mr-1 text-surface-900 dark:text-surface-0 "
formControlName="indirizzo"
pInputText
aria-labelledby="indirizzo"
placeholder="Inserisci l'indirizzo" />
</div>
</div>
</div>
<div class="self-stretch flex flex-col md:flex-row justify-end items-center gap-4">
<button pButton
severity="secondary"
class="w-full md:w-auto"
(click)="cercaStruttureForm.reset()"
[outlined]="true">
<span pButtonLabel>Annulla filtri</span>
</button>
<button pButton
type="submit"
[disabled]="vm.isSearching"
[loading]="vm.isSearching"
icon="pi pi-search"
[label]="vm.isSearching ? 'Ricerca in corso...' : 'Cerca'"
[label]="vm.isSearching ? 'Ricerca in corso...' : 'Cerca strutture'"
severity="primary"
class="shrink-0"></button>
</div>
@if (
cercaStruttureForm.errors?.['atLeastOneRequired'] &&
vm.cercaStruttureFormSubmitted
) {
<small class="text-red-600">Seleziona almeno un filtro.</small>
}
</form>
</div>
</div>
<div
class="bg-surface-50 dark:bg-surface-900 rounded-2xl border-2 border-dashed border-surface-200 dark:border-surface-700 h-[400px]">
class="flex-auto lg:flex-1 bg-surface-50 dark:bg-surface-900 rounded-2xl border-2 border-dashed border-surface-200 dark:border-surface-700 h-[350px] w-full lg:1/2">
<google-map #gmap
height="100%"
width="100%"
@ -156,8 +194,11 @@
<map-info-window></map-info-window>
</google-map>
</div>
</div>
</form>
<p-table #struttureTable
*ngIf="vm.strutture !== undefined"
[totalRecords]="vm.strutture.length"
dataKey="id"
[loading]="vm.struttureAreLoading"
@ -182,6 +223,7 @@
</p-inputicon>
<input pInputText
type="text"
aria-label="Filtra strutture"
(input)="applyFilterGlobal($event, 'contains')"
placeholder="Filtra strutture" />
</p-iconfield>
@ -218,7 +260,7 @@
<span class="font-light"> <b>{{ row.struttura.nome }}</b></span>
<div *ngIf="row.struttura.sitoWeb"
class="mt-4 ont-light">
<a class="cursor-pointer hover:underline text-primary-600"
<a class="cursor-pointer hover:underline text-primary-700"
(click)="externalLink(row.struttura.sitoWeb)">Sito Web</a>
</div>
</td>
@ -235,26 +277,26 @@
<div class="text-sm"
*ngIf="row.struttura.cupPubblico">
CUP Pubblico:
<a class="cursor-pointer hover:underline text-primary-600"
<a class="cursor-pointer hover:underline text-primary-700"
href="tel:{{ row.struttura.cupPubblico }}">{{ row.struttura.cupPubblico }}</a>
</div>
<div *ngIf="row.struttura.cupPrivato"
class="text-sm">
CUP Privato:
<a class="cursor-pointer hover:underline text-primary-600"
<a class="cursor-pointer hover:underline text-primary-700"
href="tel:{{ row.struttura.cupPrivato }}">{{ row.struttura.cupPrivato }}</a>
</div>
<ng-container *ngIf="!row.struttura.cupPubblico && !row.struttura.cupPrivato">
<div *ngIf="row.struttura.telefono1"
class="text-sm">
tel:
<a class="cursor-pointer hover:underline text-primary-600"
<a class="cursor-pointer hover:underline text-primary-700"
href="tel:{{ row.struttura.telefono1 }}">{{ row.struttura.telefono1 }}</a>
</div>
<div *ngIf="row.struttura.telefono2"
class="text-sm">
tel:
<a class="cursor-pointer hover:underline text-primary-600"
<a class="cursor-pointer hover:underline text-primary-700"
href="tel:{{ row.struttura.telefono2 }}">{{ row.struttura.telefono2 }}</a>
</div>
</ng-container>
@ -266,6 +308,7 @@
<p-button icon="pi pi-map-marker"
label="Apri in Google Maps"
[rounded]="true"
styleClass="hover:underline text-primary-700"
(onClick)="calculateMapsLink(row)"
[text]="true" />
</div>

View File

@ -5,6 +5,7 @@ import {
FormGroup,
FormsModule,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import {
GoogleMap,
@ -46,19 +47,17 @@ import {
tap,
} from 'rxjs';
import { StrutturePubblicheControllerFindManyStrutture$Params } from '../../../../api/fn/strutture-pubbliche/strutture-pubbliche-controller-find-many-strutture';
import { atLeastOneFilterRequired } from '../../main/strutture/strutture.component';
import { TableColumn } from '../../shared';
import { StrutturePubblicheService } from './strutture-pubbliche.service';
const cercaStruttureFormGroupFunc = () => {
return new FormGroup(
{
return new FormGroup({
tipologiaStruttura: new FormControl<string | null>(null),
luogo: new FormControl<LuogoRes | null>(null),
luogo: new FormControl<LuogoRes | null>(null, {
validators: [Validators.required],
}),
indirizzo: new FormControl<string | null>(null),
},
[atLeastOneFilterRequired],
);
});
};
export type cercaStruttureForm = ReturnType<typeof cercaStruttureFormGroupFunc>;
export type cercaStruttureFormValue = cercaStruttureForm['value'];
@ -151,7 +150,7 @@ export class StrutturePubblicheComponent {
) {
this.model$ = this.state.select();
this.state.set({
strutture: [],
strutture: undefined,
tipologieStrutture: [],
filteredLuoghiEsteso: [],
@ -222,28 +221,27 @@ export class StrutturePubblicheComponent {
const params: StrutturePubblicheControllerFindManyStrutture$Params = {
indirizzo: _form.indirizzo,
tipoStruttura: _form.tipologiaStruttura,
luogo: _form.luogo?.luogo,
'luogo.tipo': _form.luogo?.tipo,
luogo: _form.luogo!.luogo!,
'luogo.tipo': _form.luogo!.tipo!,
};
this.calcolaIndirizzoDa = _form.indirizzo;
const strutture$ = this.strutturePubblicheService.getStrutture(params).pipe(
catchError(() => {
catchError((err) => {
this.messageService.add({
severity: 'error',
life: 5000,
summary: 'Attenzione!',
detail:
'Impossibile recuperare la lista delle strutture in questo momento, riprova più tardi',
err.status === 400
? 'Il luogo è obbligatorio'
: 'Impossibile recuperare la lista delle strutture in questo momento, riprova più tardi',
});
return of([] as StrutturePubblicheResDto[]);
}),
tap((res) => {
this.cercaStruttureForm.enable();
if (!res) return;
this.state.set(() => ({
cercaStruttureFormSubmitted: false,
}));
this.gMapMarkers(res, false);
}),
map((res) => ({ strutture: res })),
@ -290,6 +288,9 @@ export class StrutturePubblicheComponent {
}
</p>
</div>
<div class="mt-2">
<a class="hover:underline text-primary-700" target="_blank" href="${this.calculateMapsLink(info, false)}"><i class="pi pi-map-marker"></i>Apri su Google Maps</a>
</div>
</div>
`;
this.infoWindow.infoWindow?.setContent(contentString);
@ -398,7 +399,7 @@ export class StrutturePubblicheComponent {
);
}
calculateMapsLink(struttura: StrutturePubblicheResDto): void {
calculateMapsLink(struttura: StrutturePubblicheResDto, open = true): string {
// https://www.google.com/maps/dir/?api=1&origin=45.476333943968626,9.17169124076061&destination=45.4692422,9.16562&travelmode=driving
// https://www.google.com/maps/dir/?api=1&destination=45.4692422,9.16562&travelmode=driving
@ -436,13 +437,14 @@ export class StrutturePubblicheComponent {
}
url = `${url}&travelmode=${travelMode}`;
window.open(url, '_blank');
if (open) window.open(url, '_blank');
return url;
}
checkLuogo() {
//Hack: rimosso dall'autocomplete forceSelection perchè non funzionava come previsto, selezionando ad esempio "Milano" (comune), selezionava "Milano" (comune).
const luogo = this.cercaStruttureForm.controls['luogo'].value;
console.log('luogo', luogo);
if (typeof luogo === 'string') {
this.cercaStruttureForm.controls['luogo'].setValue(null);
}