changes + saved search

This commit is contained in:
Arnaud Delcasse
2025-10-08 10:39:13 +02:00
parent 27ce05a91a
commit 2fee2a4ce8
16 changed files with 670 additions and 165 deletions

View File

@@ -24,6 +24,10 @@
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Distance du covoiturage
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Profil validé
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
@@ -38,8 +42,15 @@
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .DriverDepartureAddress }}</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .DriverArrivalAddress }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Distance }} km</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if carpoolDriverValidatedProfile $driver (carpoolDocuments $driver.ID) }}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue hover:text-co-blue"
<a class="text-co-blue hover:text-co-blue"
href="/app/organized-carpool/drivers/{{$driver.ID}}/journeys/{{.Id}}">
Organiser
</a>

View File

@@ -0,0 +1,110 @@
{{define "saved_searches"}}
<div class="bg-white shadow sm:rounded-2xl sm:overflow-hidden">
<h2 class="text-lg font-medium text-gray-900 p-4 sm:px-6">Recherches sauvegardées</h2>
<div class="border-t border-gray-200">
{{if .ViewState.saved_searches}}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="w-1/6 py-2 px-3 text-left text-sm font-semibold text-gray-900">
Bénéficiaire
</th>
<th scope="col" class="w-1/4 px-3 py-2 text-left text-sm font-semibold text-gray-900">
Départ
</th>
<th scope="col" class="w-1/4 px-3 py-2 text-left text-sm font-semibold text-gray-900">
Destination
</th>
<th scope="col" class="w-1/6 px-3 py-2 text-left text-sm font-semibold text-gray-900">
Date/Heure
</th>
<th scope="col" class="w-1/6 relative py-2 px-3 text-right text-sm font-semibold text-gray-900">
<span class="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.saved_searches}}
<tr>
<td class="py-3 px-3 text-sm text-gray-500">
{{if and .Data .Data.passenger_id}}
{{$beneficiary := index $.ViewState.beneficiaries .Data.passenger_id}}
{{if $beneficiary}}
<a href="/app/beneficiaries/{{.Data.passenger_id}}" class="text-co-blue hover:text-co-blue-dark">
{{$beneficiary.Data.first_name}} {{$beneficiary.Data.last_name}}
</a>
{{else}}
-
{{end}}
{{else}}
-
{{end}}
</td>
<td class="px-3 py-3 text-sm text-gray-500 break-words">
{{if .Departure}}
{{if .Departure.Properties}}
{{if .Departure.Properties.city}}
{{.Departure.Properties.city}}
{{else}}
{{.Departure.Properties.label}}
{{end}}
{{else}}
-
{{end}}
{{else}}
-
{{end}}
</td>
<td class="px-3 py-3 text-sm text-gray-500 break-words">
{{if .Destination}}
{{if .Destination.Properties}}
{{if .Destination.Properties.city}}
{{.Destination.Properties.city}}
{{else}}
{{.Destination.Properties.label}}
{{end}}
{{else}}
-
{{end}}
{{else}}
-
{{end}}
</td>
<td class="px-3 py-3 text-sm text-gray-500 whitespace-nowrap">
{{if .DateTime}}
{{timeFormat .DateTime "02/01/2006 15:04"}}
{{else}}
-
{{end}}
</td>
<td class="relative py-3 px-3 text-right text-sm font-medium whitespace-nowrap">
<a href="/app/journeys/?departure={{json .Departure}}&destination={{json .Destination}}&departuredate={{timeFormat .DateTime "2006-01-02"}}&departuretime={{timeFormat .DateTime "15:04"}}{{if and .Data .Data.passenger_id}}&passengerid={{.Data.passenger_id}}{{end}}" class="text-co-blue hover:text-co-blue-dark mr-4">
Rechercher
</a>
<a href="/app/journeys/saved-searches/{{.ID}}/delete"
class="text-red-600 hover:text-red-900"
onclick="return confirm('Êtes-vous sûr de vouloir supprimer cette recherche sauvegardée ?')">
Supprimer
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{else}}
<div class="text-center py-8">
<div class="text-gray-500">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">Aucune recherche sauvegardée</h3>
<p class="mt-1 text-sm text-gray-500">Effectuez une recherche et cliquez sur "Enregistrer pour plus tard" pour commencer.</p>
</div>
</div>
{{end}}
</div>
</div>
{{end}}

View File

@@ -21,7 +21,30 @@
<div class="bg-white shadow sm:rounded-2xl">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900 p-4 sm:px-6">Chercher une solution</h2>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<form method="GET">
<form method="GET" x-data="journeySearch()">
<div class="py-4">
<label for="beneficiary" class="block text-sm font-medium text-gray-700">Bénéficiaire (optionnel)</label>
<div class="mt-1 relative" x-data="beneficiaryAutocomplete()">
<input type="text"
x-model="input"
@input="onInput()"
@focus="showResults = true"
@blur="setTimeout(() => showResults = false, 200)"
class="p-2 shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl">
<ul x-show="showResults && filteredBeneficiaries.length > 0"
class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-xl py-1 text-base overflow-auto focus:outline-none sm:text-sm border border-gray-300"
tabindex="-1" role="listbox">
<template x-for="beneficiary in filteredBeneficiaries" :key="beneficiary.id">
<li class="text-gray-900 hover:bg-gray-200 cursor-default select-none relative py-2 pl-3 pr-9"
@click="selectBeneficiary(beneficiary)">
<span class="font-normal block truncate" x-text="beneficiary.name"></span>
</li>
</template>
</ul>
</div>
</div>
{{ $departureField := "departure" }}
{{ $departureLabel := "Départ" }}
@@ -33,9 +56,7 @@
{{ $destination := .ViewState.destination }}
{{ template "address_autocomplete" dict "FieldName" $destinationField "FieldLabel" $destinationLabel "Address" $destination }}
{{if ne .ViewState.passengerid "" }}
<input type="hidden" value="{{.ViewState.passengerid}}" name="passengerid" />
{{end}}
<input type="hidden" name="passengerid" value="{{.ViewState.passengerid}}" />
<div class="py-4 grid grid-cols-2">
<div class="lg:col-span-1">
@@ -55,11 +76,100 @@
</div>
<button type="submit"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 mt-8 w-full text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Chercher
</button>
<div class="flex gap-2 mt-8">
<button type="submit"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Chercher
</button>
{{if .ViewState.searched}}
<a href="/app/journeys/save?departure={{json .ViewState.departure}}&destination={{ json .ViewState.destination }}&departuredate={{.ViewState.departuredate}}&departuretime={{.ViewState.departuretime}}&passengerid={{.ViewState.passengerid}}"
class="rounded-2xl border border-gray-300 bg-white px-4 py-2 my-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto inline-flex items-center">
Enregistrer pour plus tard
</a>
{{end}}
</div>
</form>
<script>
function journeySearch() {
return {
selectedBeneficiaryId: '{{.ViewState.passengerid}}',
}
}
function beneficiaryAutocomplete() {
const beneficiaries = [
{{range .ViewState.beneficiaries_list}}
{
id: '{{.ID}}',
name: '{{.Data.first_name}} {{.Data.last_name}}',
address: {{json .Data.address}}
},
{{end}}
];
return {
input: '',
showResults: false,
filteredBeneficiaries: [],
selectedBeneficiary: null,
init() {
// Set initial value if passengerid is set
const currentPassengerId = '{{.ViewState.passengerid}}';
if (currentPassengerId) {
const beneficiary = beneficiaries.find(b => b.id === currentPassengerId);
if (beneficiary) {
this.input = beneficiary.name;
this.selectedBeneficiary = beneficiary;
}
}
},
onInput() {
if (this.input.length === 0) {
this.filteredBeneficiaries = [];
this.selectedBeneficiary = null;
this.updatePassengerId('');
return;
}
this.filteredBeneficiaries = beneficiaries.filter(beneficiary =>
beneficiary.name.toLowerCase().includes(this.input.toLowerCase())
).slice(0, 10); // Limit to 10 results
},
selectBeneficiary(beneficiary) {
this.input = beneficiary.name;
this.selectedBeneficiary = beneficiary;
this.showResults = false;
this.updatePassengerId(beneficiary.id);
this.setDepartureAddress(beneficiary.address);
},
updatePassengerId(id) {
const passengerInput = document.querySelector('input[name="passengerid"]');
if (passengerInput) {
passengerInput.value = id;
}
},
setDepartureAddress(address) {
if (address) {
// Find the departure address autocomplete container
const departureContainer = document.querySelector('input[name="departure"]').closest('[x-data]');
if (departureContainer && departureContainer._x_dataStack && departureContainer._x_dataStack[0]) {
const addressAutocomplete = departureContainer._x_dataStack[0];
// Update the address autocomplete component directly
addressAutocomplete.address = JSON.stringify(address);
addressAutocomplete.addressObject = address;
addressAutocomplete.input = address.properties.label;
}
}
}
}
}
</script>
</div>
</div>
</div>
@@ -113,6 +223,8 @@
</div>
</div>
</section>
{{else}}
{{template "saved_searches" .}}
{{end}}
</div>
</div>