1766 lines
		
	
	
		
			100 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			1766 lines
		
	
	
		
			100 KiB
		
	
	
	
		
			HTML
		
	
	
	
{{define "content"}}
 | 
						|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 py-8">
 | 
						|
    <div class="bg-white shadow rounded-lg overflow-hidden" style="height: calc(100vh - 12rem);" x-data="compactJourneySearch()">
 | 
						|
        <!-- Top Bar with Search Form -->
 | 
						|
        <div class="border-b border-gray-200 p-4">
 | 
						|
            <form method="GET" class="flex items-end">
 | 
						|
            <div class="flex-1" x-data='{
 | 
						|
                input: {{if .ViewState.departure}}"{{.ViewState.departure.Properties.label}}"{{else}}null{{end}},
 | 
						|
                address: {{if .ViewState.departure}}JSON.stringify({{template "geojson" .ViewState.departure}}){{else}}null{{end}},
 | 
						|
                addressObject: {{if .ViewState.departure}}{{template "geojson" .ViewState.departure }}{{else}}null{{end}},
 | 
						|
                responselength: 0,
 | 
						|
                async autocomplete() {
 | 
						|
                    if(this.input == null || this.input == "") {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    if(this.addressObject != null && this.input == this.addressObject.properties.label) {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    if(this.input.length < 3) {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    result = await fetch("https://api-adresse.data.gouv.fr/search/?q=" + this.input)
 | 
						|
                    json = await result.json()
 | 
						|
                    this.responselength = json["features"].length
 | 
						|
                    return json["features"]
 | 
						|
                },
 | 
						|
                select(a) {
 | 
						|
                    this.address = JSON.stringify(a)
 | 
						|
                    this.addressObject = a
 | 
						|
                    this.input = a.properties.label
 | 
						|
                }
 | 
						|
            }' class="relative">
 | 
						|
                <input type="hidden" name="departure" x-model="address">
 | 
						|
                <label for="departure-compact" class="block text-sm font-medium text-gray-700 mb-1">Départ</label>
 | 
						|
                <input type="text"
 | 
						|
                    id="departure-compact"
 | 
						|
                    class="p-2 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-l-lg"
 | 
						|
                    x-model="input"
 | 
						|
                    placeholder="Adresse de départ">
 | 
						|
                <ul x-show="responselength > 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" tabindex="-1">
 | 
						|
                    <template x-for="item in autocomplete">
 | 
						|
                        <a href="#">
 | 
						|
                            <li class="text-gray-900 hover:bg-gray-200 cursor-default select-none relative py-2 pl-3 pr-9"
 | 
						|
                                @click="select(item)">
 | 
						|
                                <span class="font-normal block truncate" x-text="item.properties.label"></span>
 | 
						|
                            </li>
 | 
						|
                        </a>
 | 
						|
                    </template>
 | 
						|
                </ul>
 | 
						|
            </div>
 | 
						|
 | 
						|
            <div class="flex-1" x-data='{
 | 
						|
                input: {{if .ViewState.destination}}"{{.ViewState.destination.Properties.label}}"{{else}}null{{end}},
 | 
						|
                address: {{if .ViewState.destination}}JSON.stringify({{template "geojson" .ViewState.destination}}){{else}}null{{end}},
 | 
						|
                addressObject: {{if .ViewState.destination}}{{template "geojson" .ViewState.destination }}{{else}}null{{end}},
 | 
						|
                responselength: 0,
 | 
						|
                async autocomplete() {
 | 
						|
                    if(this.input == null || this.input == "") {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    if(this.addressObject != null && this.input == this.addressObject.properties.label) {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    if(this.input.length < 3) {
 | 
						|
                        this.responselength = 0
 | 
						|
                        return []
 | 
						|
                    }
 | 
						|
                    result = await fetch("https://api-adresse.data.gouv.fr/search/?q=" + this.input)
 | 
						|
                    json = await result.json()
 | 
						|
                    this.responselength = json["features"].length
 | 
						|
                    return json["features"]
 | 
						|
                },
 | 
						|
                select(a) {
 | 
						|
                    this.address = JSON.stringify(a)
 | 
						|
                    this.addressObject = a
 | 
						|
                    this.input = a.properties.label
 | 
						|
                }
 | 
						|
            }' class="relative">
 | 
						|
                <input type="hidden" name="destination" x-model="address">
 | 
						|
                <label for="destination-compact" class="block text-sm font-medium text-gray-700 mb-1">Destination</label>
 | 
						|
                <input type="text"
 | 
						|
                    id="destination-compact"
 | 
						|
                    class="p-2 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-none"
 | 
						|
                    x-model="input"
 | 
						|
                    placeholder="Adresse de destination">
 | 
						|
                <ul x-show="responselength > 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" tabindex="-1">
 | 
						|
                    <template x-for="item in autocomplete">
 | 
						|
                        <a href="#">
 | 
						|
                            <li class="text-gray-900 hover:bg-gray-200 cursor-default select-none relative py-2 pl-3 pr-9"
 | 
						|
                                @click="select(item)">
 | 
						|
                                <span class="font-normal block truncate" x-text="item.properties.label"></span>
 | 
						|
                            </li>
 | 
						|
                        </a>
 | 
						|
                    </template>
 | 
						|
                </ul>
 | 
						|
            </div>
 | 
						|
 | 
						|
            <div class="w-40">
 | 
						|
                <label for="departuredate-compact" class="block text-sm font-medium text-gray-700 mb-1">Date</label>
 | 
						|
                <input type="date"
 | 
						|
                       id="departuredate-compact"
 | 
						|
                       name="departuredate"
 | 
						|
                       value="{{.ViewState.departuredate}}"
 | 
						|
                       class="p-2 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-none">
 | 
						|
            </div>
 | 
						|
 | 
						|
            <div class="w-32">
 | 
						|
                <label for="departuretime-compact" class="block text-sm font-medium text-gray-700 mb-1">Heure</label>
 | 
						|
                <input type="time"
 | 
						|
                       id="departuretime-compact"
 | 
						|
                       name="departuretime"
 | 
						|
                       value="{{.ViewState.departuretime}}"
 | 
						|
                       class="p-2 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-none">
 | 
						|
            </div>
 | 
						|
 | 
						|
            <button type="submit"
 | 
						|
                    class="px-4 py-2 border border-transparent text-sm font-medium rounded-r-lg text-white bg-co-blue hover:bg-co-blue-dark focus:outline-none">
 | 
						|
                Rechercher
 | 
						|
            </button>
 | 
						|
            </form>
 | 
						|
 | 
						|
            {{if .ViewState.searched}}
 | 
						|
            <div class="mt-2 flex items-center justify-between gap-4">
 | 
						|
                <!-- Filtres -->
 | 
						|
                <div class="flex-1 flex items-center gap-4 text-sm">
 | 
						|
                    <span class="text-gray-700 font-medium">Filtres:</span>
 | 
						|
                    <label class="inline-flex items-center cursor-pointer">
 | 
						|
                        <input type="checkbox" x-model="filters.transit" class="rounded border-gray-300 text-co-blue focus:ring-co-blue">
 | 
						|
                        <span class="ml-2 text-gray-600">Transports en commun</span>
 | 
						|
                    </label>
 | 
						|
                    <label class="inline-flex items-center cursor-pointer">
 | 
						|
                        <input type="checkbox" x-model="filters.solidarity" class="rounded border-gray-300 text-co-blue focus:ring-co-blue">
 | 
						|
                        <span class="ml-2 text-gray-600">Transport solidaire</span>
 | 
						|
                    </label>
 | 
						|
                    <label class="inline-flex items-center cursor-pointer">
 | 
						|
                        <input type="checkbox" x-model="filters.organizedCarpool" class="rounded border-gray-300 text-co-blue focus:ring-co-blue">
 | 
						|
                        <span class="ml-2 text-gray-600">Covoiturage solidaire</span>
 | 
						|
                    </label>
 | 
						|
                    <label class="inline-flex items-center cursor-pointer">
 | 
						|
                        <input type="checkbox" x-model="filters.carpool" class="rounded border-gray-300 text-co-blue focus:ring-co-blue">
 | 
						|
                        <span class="ml-2 text-gray-600">Covoiturage</span>
 | 
						|
                    </label>
 | 
						|
                    <label class="inline-flex items-center cursor-pointer">
 | 
						|
                        <input type="checkbox" x-model="filters.kb" class="rounded border-gray-300 text-co-blue focus:ring-co-blue">
 | 
						|
                        <span class="ml-2 text-gray-600">Solutions locales</span>
 | 
						|
                    </label>
 | 
						|
                </div>
 | 
						|
                <!-- Bouton Enregistrer -->
 | 
						|
                <a href="/app/journeys/save?departure={{json .ViewState.departure}}&destination={{json .ViewState.destination}}&departuredate={{.ViewState.departuredate}}&departuretime={{.ViewState.departuretime}}{{if ne .ViewState.passengerid ""}}&passengerid={{.ViewState.passengerid}}{{end}}"
 | 
						|
                   class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-2xl text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue flex-shrink-0">
 | 
						|
                    {{$.IconSet.Icon "hero:outline/bookmark" "h-4 w-4 mr-2"}}
 | 
						|
                    Enregistrer pour plus tard
 | 
						|
                </a>
 | 
						|
            </div>
 | 
						|
            {{end}}
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- Main Content: Results List (Left) + Map (Right) -->
 | 
						|
        <div class="flex overflow-hidden" style="height: calc(100% - 11rem);">
 | 
						|
        <!-- Results List (Left Side) -->
 | 
						|
        <div class="w-96 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            {{if .ViewState.searched}}
 | 
						|
            <div class="divide-y divide-gray-200">
 | 
						|
                <!-- Transit Results -->
 | 
						|
                {{range $index, $journey := .ViewState.journeys}}
 | 
						|
                <div x-show="filters.transit" @click="selectSolution('transit', {{$index}})"
 | 
						|
                     :class="selectedType === 'transit' && selectedIndex === {{$index}} ? 'bg-blue-50 border-l-4 border-co-blue' : 'hover:bg-gray-50'"
 | 
						|
                     class="p-4 cursor-pointer transition-colors">
 | 
						|
                    <div class="flex items-center justify-between mb-2">
 | 
						|
                        <div class="flex items-center gap-2">
 | 
						|
                            <span class="text-sm font-semibold text-gray-900">{{ timeFormat $journey.StartTime "15:04" }}</span>
 | 
						|
                            <span class="text-gray-400">→</span>
 | 
						|
                            <span class="text-sm font-semibold text-gray-900">{{ timeFormat $journey.EndTime "15:04" }}</span>
 | 
						|
                        </div>
 | 
						|
                        <span class="text-xs font-medium text-gray-600">{{ shortDuration $journey.Duration }}</span>
 | 
						|
                    </div>
 | 
						|
                    <div class="flex items-center gap-1 flex-wrap">
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
 | 
						|
                            {{$.IconSet.Icon "tabler-icons:bus" "h-3 w-3"}}
 | 
						|
                            <span class="ml-1">Transport</span>
 | 
						|
                        </span>
 | 
						|
                        {{range $leg := $journey.Legs}}
 | 
						|
                            {{if or (eq $leg.Mode "BUS") (eq $leg.Mode "COACH")}}
 | 
						|
                            <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold"
 | 
						|
                                  style="background-color: #{{$leg.RouteColor}}; color: #{{$leg.RouteTextColor}}">
 | 
						|
                                {{$leg.RouteShortName}}
 | 
						|
                            </span>
 | 
						|
                            {{else if or (eq $leg.Mode "REGIONAL_FAST_RAIL") (eq $leg.Mode "REGIONAL_RAIL")}}
 | 
						|
                            <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold"
 | 
						|
                                  style="background-color: #{{$leg.RouteColor}}; color: #{{$leg.RouteTextColor}}">
 | 
						|
                                {{$leg.RouteShortName}}
 | 
						|
                            </span>
 | 
						|
                            {{end}}
 | 
						|
                        {{end}}
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
                {{end}}
 | 
						|
 | 
						|
                <!-- Solidarity Transport Results -->
 | 
						|
                {{if .ViewState.driver_journeys}}
 | 
						|
                <div x-show="filters.solidarity" @click="selectSolution('solidarity', 0)"
 | 
						|
                     :class="selectedType === 'solidarity' ? 'bg-blue-50 border-l-4 border-co-lightblue' : 'hover:bg-gray-50'"
 | 
						|
                     class="p-4 cursor-pointer transition-colors">
 | 
						|
                    <div class="flex items-center justify-between mb-2">
 | 
						|
                        <span class="text-sm font-semibold text-gray-900">Transport solidaire</span>
 | 
						|
                        <span class="text-xs font-medium bg-blue-50 text-co-lightblue px-2 py-1 rounded-full">
 | 
						|
                            {{len .ViewState.driver_journeys}} conducteur{{if gt (len .ViewState.driver_journeys) 1}}s{{end}}
 | 
						|
                        </span>
 | 
						|
                    </div>
 | 
						|
                    <div class="flex items-center gap-1">
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-co-lightblue">
 | 
						|
                            {{$.IconSet.Icon "tabler-icons:car" "h-3 w-3"}}
 | 
						|
                            <span class="ml-1">Conducteurs disponibles</span>
 | 
						|
                        </span>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
                {{end}}
 | 
						|
 | 
						|
                <!-- Organized Carpool Results -->
 | 
						|
                {{if .ViewState.organized_carpools}}
 | 
						|
                <div x-show="filters.organizedCarpool" @click="selectSolution('organized_carpool', 0)"
 | 
						|
                     :class="selectedType === 'organized_carpool' ? 'bg-blue-50 border-l-4 border-co-blue' : 'hover:bg-gray-50'"
 | 
						|
                     class="p-4 cursor-pointer transition-colors">
 | 
						|
                    <div class="flex items-center justify-between mb-2">
 | 
						|
                        <span class="text-sm font-semibold text-gray-900">Covoiturage solidaire</span>
 | 
						|
                        <span class="text-xs font-medium bg-blue-50 text-co-blue px-2 py-1 rounded-full">
 | 
						|
                            {{len .ViewState.organized_carpools}} trajet{{if gt (len .ViewState.organized_carpools) 1}}s{{end}}
 | 
						|
                        </span>
 | 
						|
                    </div>
 | 
						|
                    <div class="flex items-center gap-1">
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">
 | 
						|
                            {{$.IconSet.Icon "tabler-icons:users" "h-3 w-3"}}
 | 
						|
                            <span class="ml-1">Trajets disponibles</span>
 | 
						|
                        </span>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
                {{end}}
 | 
						|
 | 
						|
                <!-- Carpool Operator Results -->
 | 
						|
                {{range $carpoolIndex, $carpoolFC := .ViewState.carpools}}
 | 
						|
                {{$carpoolData := index $carpoolFC.ExtraMembers "ocss"}}
 | 
						|
                <div x-show="filters.carpool" @click="selectSolution('carpool', {{$carpoolIndex}})"
 | 
						|
                     :class="selectedType === 'carpool' && selectedIndex === {{$carpoolIndex}} ? 'bg-gray-50 border-l-4 border-co-blue' : 'hover:bg-gray-50'"
 | 
						|
                     class="p-4 cursor-pointer transition-colors">
 | 
						|
                    <div class="flex items-center justify-between mb-2">
 | 
						|
                        <div class="flex items-center gap-2">
 | 
						|
                            <span class="text-sm font-semibold text-gray-900">{{$carpoolData.Driver.Alias}}</span>
 | 
						|
                        </div>
 | 
						|
                        <template x-if="carpools[{{$carpoolIndex}}] && carpools[{{$carpoolIndex}}].price">
 | 
						|
                        <span class="text-xs font-medium text-gray-600" x-text="carpools[{{$carpoolIndex}}].price.toFixed(2) + '€'"></span>
 | 
						|
                        </template>
 | 
						|
                    </div>
 | 
						|
                    {{if or $carpoolData.PassengerPickupAddress $carpoolData.PassengerDropAddress}}
 | 
						|
                    <div class="text-xs text-gray-600 mb-2">
 | 
						|
                        {{if $carpoolData.PassengerPickupAddress}}
 | 
						|
                        <div class="flex items-start gap-1">
 | 
						|
                            {{$.IconSet.Icon "hero:outline/map-pin" "h-3 w-3 mt-0.5 flex-shrink-0"}}
 | 
						|
                            <span>{{$carpoolData.PassengerPickupAddress}}</span>
 | 
						|
                        </div>
 | 
						|
                        {{end}}
 | 
						|
                        {{if $carpoolData.PassengerDropAddress}}
 | 
						|
                        <div class="flex items-start gap-1 mt-1">
 | 
						|
                            {{$.IconSet.Icon "hero:outline/flag" "h-3 w-3 mt-0.5 flex-shrink-0"}}
 | 
						|
                            <span>{{$carpoolData.PassengerDropAddress}}</span>
 | 
						|
                        </div>
 | 
						|
                        {{end}}
 | 
						|
                    </div>
 | 
						|
                    {{end}}
 | 
						|
                    <div class="flex items-center gap-1 flex-wrap">
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-co-blue">
 | 
						|
                            {{$.IconSet.Icon "tabler-icons:car" "h-3 w-3"}}
 | 
						|
                            <span class="ml-1">Covoiturage</span>
 | 
						|
                        </span>
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">
 | 
						|
                            {{$carpoolData.Operator}}
 | 
						|
                        </span>
 | 
						|
                        {{if $carpoolData.AvailableSteats}}
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">
 | 
						|
                            {{$carpoolData.AvailableSteats}} place(s)
 | 
						|
                        </span>
 | 
						|
                        {{end}}
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
                {{end}}
 | 
						|
 | 
						|
                <!-- Knowledge Base Results -->
 | 
						|
                {{range $index, $solution := .ViewState.kb_data}}
 | 
						|
                <div x-show="filters.kb" @click="selectSolution('kb', {{$index}})"
 | 
						|
                     :class="selectedType === 'kb' && selectedIndex === {{$index}} ? 'bg-yellow-50 border-l-4 border-co-orange' : 'hover:bg-gray-50'"
 | 
						|
                     class="p-4 cursor-pointer transition-colors">
 | 
						|
                    <div class="mb-2">
 | 
						|
                        <span class="text-sm font-semibold text-gray-900">{{if $solution.title}}{{$solution.title}}{{else if $solution.name}}{{$solution.name}}{{else}}Solution locale{{end}}</span>
 | 
						|
                    </div>
 | 
						|
                    <div class="flex items-center gap-1">
 | 
						|
                        <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-50 text-co-orange">
 | 
						|
                            {{$.IconSet.Icon "hero:outline/map" "h-3 w-3"}}
 | 
						|
                            <span class="ml-1">Solution locale</span>
 | 
						|
                        </span>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
                {{end}}
 | 
						|
            </div>
 | 
						|
            {{else}}
 | 
						|
            <div class="p-8 text-center text-gray-500">
 | 
						|
                <p>Effectuez une recherche pour voir les résultats</p>
 | 
						|
            </div>
 | 
						|
            {{end}}
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- Solidarity Transport Driver List (Middle Column) -->
 | 
						|
        <template x-if="selectedType === 'solidarity'">
 | 
						|
        <div x-transition
 | 
						|
             class="w-80 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <div class="p-4 border-b border-gray-200 bg-gray-50">
 | 
						|
                <h3 class="text-sm font-semibold text-gray-900">Conducteurs disponibles</h3>
 | 
						|
            </div>
 | 
						|
            <div class="divide-y divide-gray-200">
 | 
						|
                <template x-for="(journey, idx) in solidarityJourneys" :key="idx">
 | 
						|
                    <div @click="selectDriver(idx)"
 | 
						|
                         :class="selectedDriverIndex === idx ? 'bg-blue-50 border-l-4 border-co-lightblue' : 'hover:bg-gray-50'"
 | 
						|
                         class="p-4 cursor-pointer transition-colors">
 | 
						|
                        <div class="mb-2">
 | 
						|
                            <span class="text-sm font-semibold text-gray-900" x-text="journey.driverName"></span>
 | 
						|
                        </div>
 | 
						|
                        <div class="space-y-1 text-xs text-gray-600">
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span>Distance conducteur:</span>
 | 
						|
                                <span class="font-medium" x-text="journey.driverDistance + ' km'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span>Distance passager:</span>
 | 
						|
                                <span class="font-medium" x-text="journey.passengerDistance + ' km'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div x-show="journey.duration > 0" class="flex items-center gap-2">
 | 
						|
                                <span>Durée:</span>
 | 
						|
                                <span class="font-medium" x-text="Math.round(journey.duration / 60) + ' min'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span>Profil validé:</span>
 | 
						|
                                <template x-if="journey.profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-green text-white rounded-2xl">Oui</span>
 | 
						|
                                </template>
 | 
						|
                                <template x-if="!journey.profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
 | 
						|
                                </template>
 | 
						|
                            </div>
 | 
						|
                            <div x-show="journey.comment" class="pt-1">
 | 
						|
                                <span class="italic text-gray-500" x-text="journey.comment"></span>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </template>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Transit Journey Details (Middle Column) -->
 | 
						|
        <template x-if="selectedType === 'transit' && selectedIndex !== null">
 | 
						|
        <div x-transition
 | 
						|
             class="w-96 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <template x-if="selectedIndex !== null && transitJourneys[selectedIndex]">
 | 
						|
                <div>
 | 
						|
                    <!-- Header -->
 | 
						|
                    <div class="p-4 border-b border-gray-200 bg-co-blue">
 | 
						|
                        <div class="flex items-center justify-between text-white">
 | 
						|
                            <div class="flex items-center gap-3">
 | 
						|
                                <div class="text-2xl font-bold" x-text="transitJourneys[selectedIndex].startTime"></div>
 | 
						|
                                <div class="text-lg">→</div>
 | 
						|
                                <div class="text-2xl font-bold" x-text="transitJourneys[selectedIndex].endTime"></div>
 | 
						|
                            </div>
 | 
						|
                            <div class="text-sm font-medium text-white bg-co-lightblue px-3 py-1 rounded-full" x-text="transitJourneys[selectedIndex].duration"></div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
 | 
						|
                    <!-- Journey Steps -->
 | 
						|
                    <div class="p-4 space-y-3">
 | 
						|
                        <template x-for="(leg, idx) in transitJourneys[selectedIndex].detailedLegs" :key="idx">
 | 
						|
                            <div>
 | 
						|
                                <!-- Walk Leg -->
 | 
						|
                                <template x-if="leg.mode === 'WALK' && leg.distance">
 | 
						|
                                    <div class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
 | 
						|
                                        <div class="flex-shrink-0 w-10 h-10 rounded-co bg-gray-300 flex items-center justify-center">
 | 
						|
                                            {{$.IconSet.Icon "tabler-icons:walk" "h-5 w-5 stroke-gray-700"}}
 | 
						|
                                        </div>
 | 
						|
                                        <div class="flex-1 min-w-0">
 | 
						|
                                            <div class="text-sm font-medium text-gray-900">Marche à pied</div>
 | 
						|
                                            <div class="text-xs text-gray-600" x-text="leg.distance + ' mètres'"></div>
 | 
						|
                                        </div>
 | 
						|
                                    </div>
 | 
						|
                                </template>
 | 
						|
 | 
						|
                                <!-- Bus Leg -->
 | 
						|
                                <template x-if="leg.mode === 'BUS' || leg.mode === 'COACH'">
 | 
						|
                                    <div class="bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow">
 | 
						|
                                        <!-- Route Header -->
 | 
						|
                                        <div class="p-3 flex items-center gap-3 bg-gray-50">
 | 
						|
                                            <div class="flex-shrink-0 w-10 h-10 rounded-co flex items-center justify-center"
 | 
						|
                                                 :style="'background-color: #' + leg.color + '; color: #' + leg.textColor">
 | 
						|
                                                {{$.IconSet.Icon "tabler-icons:bus" "h-6 w-6"}}
 | 
						|
                                            </div>
 | 
						|
                                            <div class="flex-1 min-w-0">
 | 
						|
                                                <div class="text-lg font-bold text-gray-900" x-text="'Ligne ' + leg.routeShortName"></div>
 | 
						|
                                                <div class="text-xs text-gray-600" x-text="leg.agencyName"></div>
 | 
						|
                                            </div>
 | 
						|
                                        </div>
 | 
						|
 | 
						|
                                        <!-- Route Details -->
 | 
						|
                                        <div class="p-3 space-y-2 bg-gray-50">
 | 
						|
                                            <!-- Times -->
 | 
						|
                                            <div class="flex items-center justify-between text-sm">
 | 
						|
                                                <div class="flex items-center gap-2">
 | 
						|
                                                    <span class="font-semibold text-gray-900" x-text="leg.startTime"></span>
 | 
						|
                                                    <span class="text-gray-500">départ</span>
 | 
						|
                                                </div>
 | 
						|
                                                <div class="flex items-center gap-2">
 | 
						|
                                                    <span class="text-gray-500">arrivée</span>
 | 
						|
                                                    <span class="font-semibold text-gray-900" x-text="leg.endTime"></span>
 | 
						|
                                                </div>
 | 
						|
                                            </div>
 | 
						|
 | 
						|
                                            <!-- Stops -->
 | 
						|
                                            <div class="text-xs text-gray-600">
 | 
						|
                                                <div class="flex items-start gap-2 mb-1">
 | 
						|
                                                    <div class="w-2 h-2 rounded-full bg-co-green mt-1 flex-shrink-0"></div>
 | 
						|
                                                    <span class="font-medium" x-text="leg.fromName"></span>
 | 
						|
                                                </div>
 | 
						|
                                                <div class="flex items-start gap-2">
 | 
						|
                                                    <div class="w-2 h-2 rounded-full bg-co-red mt-1 flex-shrink-0"></div>
 | 
						|
                                                    <span class="font-medium" x-text="leg.toName"></span>
 | 
						|
                                                </div>
 | 
						|
                                            </div>
 | 
						|
 | 
						|
                                            <!-- Direction -->
 | 
						|
                                            <div x-show="leg.headsign" class="text-xs text-gray-500 flex items-center gap-1 pt-1 border-t border-gray-200">
 | 
						|
                                                <span>Direction</span>
 | 
						|
                                                <span class="font-medium text-gray-700" x-text="leg.headsign"></span>
 | 
						|
                                            </div>
 | 
						|
                                        </div>
 | 
						|
                                    </div>
 | 
						|
                                </template>
 | 
						|
 | 
						|
                                <!-- Rail Leg -->
 | 
						|
                                <template x-if="leg.mode === 'REGIONAL_FAST_RAIL' || leg.mode === 'REGIONAL_RAIL'">
 | 
						|
                                    <div class="bg-white border border-gray-200 rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow">
 | 
						|
                                        <!-- Route Header -->
 | 
						|
                                        <div class="p-3 flex items-center gap-3 bg-gray-50">
 | 
						|
                                            <div class="flex-shrink-0 w-10 h-10 rounded-co flex items-center justify-center"
 | 
						|
                                                 :style="'background-color: #' + leg.color + '; color: #' + leg.textColor">
 | 
						|
                                                {{$.IconSet.Icon "tabler-icons:train" "h-6 w-6"}}
 | 
						|
                                            </div>
 | 
						|
                                            <div class="flex-1 min-w-0">
 | 
						|
                                                <div class="text-lg font-bold text-gray-900" x-text="'TER ' + leg.routeShortName"></div>
 | 
						|
                                                <div class="text-xs text-gray-600" x-text="leg.agencyName"></div>
 | 
						|
                                            </div>
 | 
						|
                                        </div>
 | 
						|
 | 
						|
                                        <!-- Route Details -->
 | 
						|
                                        <div class="p-3 space-y-2 bg-gray-50">
 | 
						|
                                            <!-- Times -->
 | 
						|
                                            <div class="flex items-center justify-between text-sm">
 | 
						|
                                                <div class="flex items-center gap-2">
 | 
						|
                                                    <span class="font-semibold text-gray-900" x-text="leg.startTime"></span>
 | 
						|
                                                    <span class="text-gray-500">départ</span>
 | 
						|
                                                </div>
 | 
						|
                                                <div class="flex items-center gap-2">
 | 
						|
                                                    <span class="text-gray-500">arrivée</span>
 | 
						|
                                                    <span class="font-semibold text-gray-900" x-text="leg.endTime"></span>
 | 
						|
                                                </div>
 | 
						|
                                            </div>
 | 
						|
 | 
						|
                                            <!-- Stops -->
 | 
						|
                                            <div class="text-xs text-gray-600">
 | 
						|
                                                <div class="flex items-start gap-2 mb-1">
 | 
						|
                                                    <div class="w-2 h-2 rounded-full bg-co-green mt-1 flex-shrink-0"></div>
 | 
						|
                                                    <span class="font-medium" x-text="leg.fromName"></span>
 | 
						|
                                                </div>
 | 
						|
                                                <div class="flex items-start gap-2">
 | 
						|
                                                    <div class="w-2 h-2 rounded-full bg-co-red mt-1 flex-shrink-0"></div>
 | 
						|
                                                    <span class="font-medium" x-text="leg.toName"></span>
 | 
						|
                                                </div>
 | 
						|
                                            </div>
 | 
						|
 | 
						|
                                            <!-- Direction -->
 | 
						|
                                            <div x-show="leg.headsign" class="text-xs text-gray-500 flex items-center gap-1 pt-1 border-t border-gray-200">
 | 
						|
                                                <span>Direction</span>
 | 
						|
                                                <span class="font-medium text-gray-700" x-text="leg.headsign"></span>
 | 
						|
                                            </div>
 | 
						|
                                        </div>
 | 
						|
                                    </div>
 | 
						|
                                </template>
 | 
						|
                            </div>
 | 
						|
                        </template>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </template>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Organized Carpool List (Middle Column) -->
 | 
						|
        <template x-if="selectedType === 'organized_carpool'">
 | 
						|
        <div x-transition
 | 
						|
             class="w-80 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <div class="p-4 border-b border-gray-200 bg-gray-50">
 | 
						|
                <h3 class="text-sm font-semibold text-gray-900">Trajets disponibles</h3>
 | 
						|
            </div>
 | 
						|
            <div class="divide-y divide-gray-200">
 | 
						|
                <template x-for="(carpool, idx) in organizedCarpools" :key="idx">
 | 
						|
                    <div @click="selectOrganizedCarpool(idx)"
 | 
						|
                         :class="selectedOrganizedCarpoolIndex === idx ? 'bg-blue-50 border-l-4 border-co-blue' : 'hover:bg-gray-50'"
 | 
						|
                         class="p-4 cursor-pointer transition-colors">
 | 
						|
                        <div class="mb-2">
 | 
						|
                            <span class="text-sm font-semibold text-gray-900" x-text="carpool.driverName"></span>
 | 
						|
                        </div>
 | 
						|
                        <div class="space-y-1 text-xs text-gray-600">
 | 
						|
                            <div x-show="carpool.driverDepartureAddress" class="flex items-start gap-2">
 | 
						|
                                <span class="flex-shrink-0">Départ conducteur:</span>
 | 
						|
                                <span class="font-medium" x-text="carpool.driverDepartureAddress"></span>
 | 
						|
                            </div>
 | 
						|
                            <div x-show="carpool.driverArrivalAddress" class="flex items-start gap-2">
 | 
						|
                                <span class="flex-shrink-0">Arrivée conducteur:</span>
 | 
						|
                                <span class="font-medium" x-text="carpool.driverArrivalAddress"></span>
 | 
						|
                            </div>
 | 
						|
                            <div x-show="carpool.pickupDate" class="flex items-center gap-2">
 | 
						|
                                <span>Date et heure:</span>
 | 
						|
                                <span class="font-medium" x-text="carpool.pickupDate"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span>Profil validé:</span>
 | 
						|
                                <template x-if="carpool.profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-green text-white rounded-2xl">Oui</span>
 | 
						|
                                </template>
 | 
						|
                                <template x-if="!carpool.profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
 | 
						|
                                </template>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </template>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Organized Carpool Details (Third Column) -->
 | 
						|
        <template x-if="selectedType === 'organized_carpool' && selectedOrganizedCarpoolIndex !== null">
 | 
						|
        <div x-transition
 | 
						|
             class="w-96 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <template x-if="selectedOrganizedCarpoolIndex !== null && organizedCarpools[selectedOrganizedCarpoolIndex]">
 | 
						|
                <div>
 | 
						|
                    <!-- Header -->
 | 
						|
                    <div class="p-4 border-b border-gray-200 bg-co-blue">
 | 
						|
                        <div class="text-white">
 | 
						|
                            <div class="text-xl font-bold mb-2" x-text="organizedCarpools[selectedOrganizedCarpoolIndex].driverName"></div>
 | 
						|
                            <div class="text-sm" x-text="'Trajet du ' + organizedCarpools[selectedOrganizedCarpoolIndex].pickupDate"></div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
 | 
						|
                    <!-- Journey Details -->
 | 
						|
                    <div class="p-4 space-y-4">
 | 
						|
                        <!-- Driver Info -->
 | 
						|
                        <div class="bg-blue-50 rounded-lg p-3 border border-blue-200">
 | 
						|
                            <div class="text-xs font-semibold text-gray-700 mb-2">Conducteur</div>
 | 
						|
                            <div class="space-y-2 text-sm">
 | 
						|
                                <div>
 | 
						|
                                    <span class="text-gray-600">Nom:</span>
 | 
						|
                                    <span class="font-medium ml-2" x-text="organizedCarpools[selectedOrganizedCarpoolIndex].driverName"></span>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex items-center gap-2">
 | 
						|
                                    <span class="text-gray-600">Profil validé:</span>
 | 
						|
                                    <template x-if="organizedCarpools[selectedOrganizedCarpoolIndex].profileValidated">
 | 
						|
                                        <span class="p-1 px-2 text-xs bg-co-green text-white rounded-2xl">Oui</span>
 | 
						|
                                    </template>
 | 
						|
                                    <template x-if="!organizedCarpools[selectedOrganizedCarpoolIndex].profileValidated">
 | 
						|
                                        <span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
 | 
						|
                                    </template>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
 | 
						|
                        <!-- Locations -->
 | 
						|
                        <div class="space-y-3">
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-blue flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Départ conducteur</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="organizedCarpools[selectedOrganizedCarpoolIndex].driverDepartureAddress"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-green flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="14" height="14">
 | 
						|
                                        <path fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Prise en charge passager</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="'Coordonnées: ' + organizedCarpools[selectedOrganizedCarpoolIndex].departureLocation[1].toFixed(5) + ', ' + organizedCarpools[selectedOrganizedCarpoolIndex].departureLocation[0].toFixed(5)"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-red flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="14" height="14">
 | 
						|
                                        <path fill-rule="evenodd" d="M3 2.25a.75.75 0 01.75.75v.54l1.838-.46a9.75 9.75 0 016.725.738l.108.054a8.25 8.25 0 005.58.652l3.109-.732a.75.75 0 01.917.81 47.784 47.784 0 00.005 10.337.75.75 0 01-.574.812l-3.114.733a9.75 9.75 0 01-6.594-.77l-.108-.054a8.25 8.25 0 00-5.69-.625l-2.202.55V21a.75.75 0 01-1.5 0V3A.75.75 0 013 2.25z" clip-rule="evenodd" />
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Dépose passager</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="'Coordonnées: ' + organizedCarpools[selectedOrganizedCarpoolIndex].arrivalLocation[1].toFixed(5) + ', ' + organizedCarpools[selectedOrganizedCarpoolIndex].arrivalLocation[0].toFixed(5)"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-blue flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Arrivée conducteur</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="organizedCarpools[selectedOrganizedCarpoolIndex].driverArrivalAddress"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
 | 
						|
                        <!-- Action Button -->
 | 
						|
                        <div class="pt-4 border-t border-gray-200">
 | 
						|
                            <a :href="'/app/organized-carpool/drivers/' + organizedCarpools[selectedOrganizedCarpoolIndex].driverId + '/journeys/' + organizedCarpools[selectedOrganizedCarpoolIndex].id + '{{if ne .ViewState.passengerid ""}}?passengerid={{.ViewState.passengerid}}{{end}}'"
 | 
						|
                               class="block w-full text-center bg-co-blue text-white px-4 py-2 rounded-2xl hover:bg-blue-700 transition-colors">
 | 
						|
                                Organiser le covoiturage solidaire
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </template>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Knowledge Base Solution Details (Middle Column) -->
 | 
						|
        <template x-if="selectedType === 'kb' && selectedIndex !== null">
 | 
						|
        <div x-transition
 | 
						|
             class="w-80 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <template x-if="selectedIndex !== null && kbSolutions[selectedIndex]">
 | 
						|
                <div>
 | 
						|
                    <div class="p-4 border-b border-gray-200 bg-gray-50">
 | 
						|
                        <h3 class="text-sm font-semibold text-gray-900" x-text="kbSolutions[selectedIndex].title"></h3>
 | 
						|
                    </div>
 | 
						|
                    <div class="p-4">
 | 
						|
                        <div x-show="kbSolutions[selectedIndex].description" class="mb-4">
 | 
						|
                            <p class="text-sm text-gray-700" x-text="kbSolutions[selectedIndex].description"></p>
 | 
						|
                        </div>
 | 
						|
                        <div x-show="kbSolutions[selectedIndex].url" class="mb-4">
 | 
						|
                            <a :href="kbSolutions[selectedIndex].url"
 | 
						|
                               target="_blank"
 | 
						|
                               class="text-sm text-co-blue hover:underline">
 | 
						|
                                Voir plus →
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                        <div x-show="kbSolutions[selectedIndex].geography && kbSolutions[selectedIndex].geography.length > 0">
 | 
						|
                            <h4 class="text-xs font-semibold text-gray-700 mb-2">Zone couverte</h4>
 | 
						|
                            <div class="space-y-1">
 | 
						|
                                <template x-for="geo in kbSolutions[selectedIndex].geography">
 | 
						|
                                    <div class="text-xs text-gray-600">
 | 
						|
                                        <span x-text="geo.layer"></span>: <span class="font-medium" x-text="geo.name ? geo.name + ' (' + geo.code + ')' : geo.code"></span>
 | 
						|
                                    </div>
 | 
						|
                                </template>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </template>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Carpool Details (Third Column) -->
 | 
						|
        <template x-if="selectedType === 'carpool' && selectedIndex !== null">
 | 
						|
        <div x-transition
 | 
						|
             class="w-96 flex-shrink-0 bg-white border-r border-gray-200 overflow-y-auto">
 | 
						|
            <template x-if="selectedIndex !== null && carpools[selectedIndex]">
 | 
						|
                <div>
 | 
						|
                    <!-- Header -->
 | 
						|
                    <div class="p-4 border-b border-gray-200 bg-co-blue">
 | 
						|
                        <div class="text-white">
 | 
						|
                            <div class="text-xl font-bold mb-2" x-text="carpools[selectedIndex].driverAlias"></div>
 | 
						|
                            <div class="text-sm" x-text="carpools[selectedIndex].operator"></div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
 | 
						|
                    <!-- Journey Details -->
 | 
						|
                    <div class="p-4 space-y-4">
 | 
						|
                        <!-- Price -->
 | 
						|
                        <template x-if="carpools[selectedIndex].price">
 | 
						|
                        <div class="bg-gray-50 rounded-lg p-3 border border-gray-200">
 | 
						|
                            <div class="text-xs font-semibold text-gray-700 mb-1">Prix</div>
 | 
						|
                            <div class="text-2xl font-bold text-co-blue" x-text="carpools[selectedIndex].price.toFixed(2) + ' €'"></div>
 | 
						|
                            <template x-if="carpools[selectedIndex].seats">
 | 
						|
                                <div class="text-xs text-gray-600 mt-1" x-text="carpools[selectedIndex].seats + ' place' + (carpools[selectedIndex].seats > 1 ? 's' : '') + ' disponible' + (carpools[selectedIndex].seats > 1 ? 's' : '')"></div>
 | 
						|
                            </template>
 | 
						|
                        </div>
 | 
						|
                        </template>
 | 
						|
 | 
						|
                        <!-- Locations -->
 | 
						|
                        <div class="space-y-3">
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-green flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="14" height="14">
 | 
						|
                                        <path fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Départ</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="carpools[selectedIndex].pickupAddress || 'Adresse non disponible'"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="flex items-start gap-3">
 | 
						|
                                <div class="flex-shrink-0 w-8 h-8 rounded-co bg-co-red flex items-center justify-center">
 | 
						|
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="14" height="14">
 | 
						|
                                        <path fill-rule="evenodd" d="M3 2.25a.75.75 0 01.75.75v.54l1.838-.46a9.75 9.75 0 016.725.738l.108.054a8.25 8.25 0 005.58.652l3.109-.732a.75.75 0 01.917.81 47.784 47.784 0 00.005 10.337.75.75 0 01-.574.812l-3.114.733a9.75 9.75 0 01-6.594-.77l-.108-.054a8.25 8.25 0 00-5.69-.625l-2.202.55V21a.75.75 0 01-1.5 0V3A.75.75 0 013 2.25z" clip-rule="evenodd" />
 | 
						|
                                    </svg>
 | 
						|
                                </div>
 | 
						|
                                <div class="flex-1 min-w-0">
 | 
						|
                                    <div class="text-xs text-gray-500">Arrivée</div>
 | 
						|
                                    <div class="text-sm font-medium text-gray-900" x-text="carpools[selectedIndex].dropAddress || 'Adresse non disponible'"></div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
 | 
						|
                        <!-- Distance & Duration -->
 | 
						|
                        <div class="grid grid-cols-2 gap-3">
 | 
						|
                            <template x-if="carpools[selectedIndex].distance">
 | 
						|
                            <div class="bg-gray-50 rounded-lg p-3">
 | 
						|
                                <div class="text-xs text-gray-500 mb-1">Distance</div>
 | 
						|
                                <div class="text-sm font-semibold text-gray-900" x-text="(carpools[selectedIndex].distance / 1000).toFixed(1) + ' km'"></div>
 | 
						|
                            </div>
 | 
						|
                            </template>
 | 
						|
                            <template x-if="carpools[selectedIndex].duration">
 | 
						|
                            <div class="bg-gray-50 rounded-lg p-3">
 | 
						|
                                <div class="text-xs text-gray-500 mb-1">Durée</div>
 | 
						|
                                <div class="text-sm font-semibold text-gray-900" x-text="Math.floor(carpools[selectedIndex].duration / 60) + ' min'"></div>
 | 
						|
                            </div>
 | 
						|
                            </template>
 | 
						|
                        </div>
 | 
						|
 | 
						|
                        <!-- Action Button -->
 | 
						|
                        <template x-if="carpools[selectedIndex].webUrl">
 | 
						|
                        <div class="pt-4 border-t border-gray-200">
 | 
						|
                            <a :href="carpools[selectedIndex].webUrl"
 | 
						|
                               target="_blank"
 | 
						|
                               class="block w-full text-center bg-co-blue text-white px-4 py-2 rounded-2xl hover:bg-co-darkblue transition-colors">
 | 
						|
                                <span>Voir l'offre sur </span><span x-text="carpools[selectedIndex].operator"></span>
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                        </template>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </template>
 | 
						|
        </div>
 | 
						|
        </template>
 | 
						|
 | 
						|
        <!-- Map (Right Side) -->
 | 
						|
        <div class="flex-1 bg-gray-100 relative">
 | 
						|
            <!-- Driver Detail Panel -->
 | 
						|
            <div x-show="selectedType === 'solidarity' && selectedDriverIndex !== null"
 | 
						|
                 x-transition
 | 
						|
                 class="absolute top-4 left-4 right-4 bg-white rounded-lg shadow-lg p-4 z-10 max-w-md">
 | 
						|
                <template x-if="selectedDriverIndex !== null && solidarityJourneys[selectedDriverIndex]">
 | 
						|
                    <div>
 | 
						|
                        <div class="flex items-center justify-between mb-3">
 | 
						|
                            <h3 class="text-lg font-semibold text-gray-900" x-text="solidarityJourneys[selectedDriverIndex].driverName"></h3>
 | 
						|
                            <button @click="selectedDriverIndex = null" class="text-gray-400 hover:text-gray-600">
 | 
						|
                                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
						|
                                    <line x1="18" y1="6" x2="6" y2="18"></line>
 | 
						|
                                    <line x1="6" y1="6" x2="18" y2="18"></line>
 | 
						|
                                </svg>
 | 
						|
                            </button>
 | 
						|
                        </div>
 | 
						|
                        <div class="space-y-2 text-sm">
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span class="text-gray-500">Distance conducteur:</span>
 | 
						|
                                <span class="font-medium text-gray-900" x-text="solidarityJourneys[selectedDriverIndex].driverDistance + ' km'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span class="text-gray-500">Distance passager:</span>
 | 
						|
                                <span class="font-medium text-gray-900" x-text="solidarityJourneys[selectedDriverIndex].passengerDistance + ' km'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2" x-show="solidarityJourneys[selectedDriverIndex].duration > 0">
 | 
						|
                                <span class="text-gray-500">Durée:</span>
 | 
						|
                                <span class="font-medium text-gray-900" x-text="Math.round(solidarityJourneys[selectedDriverIndex].duration / 60) + ' min'"></span>
 | 
						|
                            </div>
 | 
						|
                            <div class="flex items-center gap-2">
 | 
						|
                                <span class="text-gray-500">Profil validé:</span>
 | 
						|
                                <template x-if="solidarityJourneys[selectedDriverIndex].profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-green text-white rounded-2xl">Oui</span>
 | 
						|
                                </template>
 | 
						|
                                <template x-if="!solidarityJourneys[selectedDriverIndex].profileValidated">
 | 
						|
                                    <span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
 | 
						|
                                </template>
 | 
						|
                            </div>
 | 
						|
                            <div x-show="solidarityJourneys[selectedDriverIndex].comment" class="pt-2 border-t border-gray-200">
 | 
						|
                                <span class="text-gray-700 italic" x-text="solidarityJourneys[selectedDriverIndex].comment"></span>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                        <div class="mt-4">
 | 
						|
                            <a :href="'/app/solidarity-transport/drivers/' + solidarityJourneys[selectedDriverIndex].driverId + '/journeys/' + solidarityJourneys[selectedDriverIndex].id"
 | 
						|
                               class="block w-full text-center px-4 py-2 bg-co-blue text-white rounded-lg hover:bg-co-blue-dark transition-colors">
 | 
						|
                                Organiser le transport solidaire
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </template>
 | 
						|
            </div>
 | 
						|
 | 
						|
            <div id="compact-journey-map" class="w-full h-full"></div>
 | 
						|
        </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
</div>
 | 
						|
 | 
						|
<script>
 | 
						|
function compactJourneySearch() {
 | 
						|
    const transitJourneys = [
 | 
						|
        {{range $journeyIndex, $journey := .ViewState.journeys}}
 | 
						|
        {
 | 
						|
            startTime: '{{ timeFormat $journey.StartTime "15:04" }}',
 | 
						|
            endTime: '{{ timeFormat $journey.EndTime "15:04" }}',
 | 
						|
            duration: '{{ shortDuration $journey.Duration }}',
 | 
						|
            legs: [
 | 
						|
                {{range $legIndex, $leg := $journey.Legs}}
 | 
						|
                {
 | 
						|
                    mode: '{{$leg.Mode}}',
 | 
						|
                    color: '{{$leg.RouteColor}}',
 | 
						|
                    from: {{if and $leg.From $leg.From.Lon $leg.From.Lat}}[{{$leg.From.Lon}}, {{$leg.From.Lat}}]{{else}}null{{end}},
 | 
						|
                    to: {{if and $leg.To $leg.To.Lon $leg.To.Lat}}[{{$leg.To.Lon}}, {{$leg.To.Lat}}]{{else}}null{{end}},
 | 
						|
                    polyline: {{if $leg.LegGeometry}}'{{$leg.LegGeometry.Points}}'{{else}}null{{end}},
 | 
						|
                    precision: {{if $leg.LegGeometry}}{{$leg.LegGeometry.Precision}}{{else}}6{{end}}
 | 
						|
                },
 | 
						|
                {{end}}
 | 
						|
            ].filter(function(leg) { return leg.from && leg.to; }),
 | 
						|
            detailedLegs: [
 | 
						|
                {{range $legIndex, $leg := $journey.Legs}}
 | 
						|
                {
 | 
						|
                    mode: '{{$leg.Mode}}',
 | 
						|
                    color: '{{$leg.RouteColor}}',
 | 
						|
                    textColor: '{{$leg.RouteTextColor}}',
 | 
						|
                    distance: {{if $leg.Distance}}{{$leg.Distance}}{{else}}0{{end}},
 | 
						|
                    duration: {{if $leg.Duration}}{{$leg.Duration}}{{else}}0{{end}},
 | 
						|
                    agencyName: {{if $leg.AgencyName}}'{{$leg.AgencyName}}'{{else}}null{{end}},
 | 
						|
                    routeShortName: {{if $leg.RouteShortName}}'{{$leg.RouteShortName}}'{{else}}null{{end}},
 | 
						|
                    headsign: {{if $leg.Headsign}}'{{$leg.Headsign}}'{{else}}null{{end}},
 | 
						|
                    startTime: '{{ timeFormat $leg.StartTime "15:04" }}',
 | 
						|
                    endTime: '{{ timeFormat $leg.EndTime "15:04" }}',
 | 
						|
                    fromName: {{if and $leg.From $leg.From.Name}}'{{$leg.From.Name}}'{{else}}null{{end}},
 | 
						|
                    toName: {{if and $leg.To $leg.To.Name}}'{{$leg.To.Name}}'{{else}}null{{end}}
 | 
						|
                },
 | 
						|
                {{end}}
 | 
						|
            ]
 | 
						|
        },
 | 
						|
        {{end}}
 | 
						|
    ];
 | 
						|
 | 
						|
    const solidarityJourneys = [
 | 
						|
        {{range $index, $driverJourney := .ViewState.driver_journeys}}
 | 
						|
        {{$driver := index $.ViewState.solidarity_drivers $driverJourney.DriverId}}
 | 
						|
        {
 | 
						|
            id: '{{$driverJourney.Id}}',
 | 
						|
            driverId: '{{$driverJourney.DriverId}}',
 | 
						|
            driverName: '{{$driver.Data.first_name}} {{$driver.Data.last_name}}',
 | 
						|
            driverDistance: {{$driverJourney.DriverDistance}},
 | 
						|
            passengerDistance: {{$driverJourney.PassengerDistance}},
 | 
						|
            duration: {{if $driverJourney.Duration}}{{$driverJourney.Duration}}{{else}}0{{end}},
 | 
						|
            polyline: {{if $driverJourney.JourneyPolyline}}'{{$driverJourney.JourneyPolyline}}'{{else}}null{{end}},
 | 
						|
            comment: {{if $driver.Data.other_properties}}{{if $driver.Data.other_properties.comment}}"{{$driver.Data.other_properties.comment}}"{{else}}null{{end}}{{else}}null{{end}},
 | 
						|
            profileValidated: {{solidarityDriverValidatedProfile $driver (solidarityDocuments $driver.ID)}},
 | 
						|
            driverLocation: {{if $driverJourney.DriverDeparture}}(function() {
 | 
						|
                try {
 | 
						|
                    const f = JSON.parse('{{$driverJourney.DriverDeparture.Serialized}}');
 | 
						|
                    return f.geometry && f.geometry.coordinates ? f.geometry.coordinates : null;
 | 
						|
                } catch(e) { return null; }
 | 
						|
            })(){{else}}null{{end}},
 | 
						|
            passengerPickup: {{if $driverJourney.PassengerPickup}}(function() {
 | 
						|
                try {
 | 
						|
                    const f = JSON.parse('{{$driverJourney.PassengerPickup.Serialized}}');
 | 
						|
                    return f.geometry && f.geometry.coordinates ? f.geometry.coordinates : null;
 | 
						|
                } catch(e) { return null; }
 | 
						|
            })(){{else}}null{{end}},
 | 
						|
            passengerDropoff: {{if $driverJourney.PassengerDrop}}(function() {
 | 
						|
                try {
 | 
						|
                    const f = JSON.parse('{{$driverJourney.PassengerDrop.Serialized}}');
 | 
						|
                    return f.geometry && f.geometry.coordinates ? f.geometry.coordinates : null;
 | 
						|
                } catch(e) { return null; }
 | 
						|
            })(){{else}}null{{end}},
 | 
						|
            driverDestination: {{if $driverJourney.DriverArrival}}(function() {
 | 
						|
                try {
 | 
						|
                    const f = JSON.parse('{{$driverJourney.DriverArrival.Serialized}}');
 | 
						|
                    return f.geometry && f.geometry.coordinates ? f.geometry.coordinates : null;
 | 
						|
                } catch(e) { return null; }
 | 
						|
            })(){{else}}null{{end}}
 | 
						|
        },
 | 
						|
        {{end}}
 | 
						|
    ];
 | 
						|
 | 
						|
    const organizedCarpools = [
 | 
						|
        {{range $index, $carpool := .ViewState.organized_carpools}}
 | 
						|
        {{$driver := index $.ViewState.solidarity_drivers $carpool.Driver.Id}}
 | 
						|
        {
 | 
						|
            id: '{{$carpool.Id}}',
 | 
						|
            driverId: '{{$carpool.Driver.Id}}',
 | 
						|
            driverName: '{{$driver.Data.first_name}} {{$driver.Data.last_name}}',
 | 
						|
            departureLocation: {{if and $carpool.PassengerPickupLng $carpool.PassengerPickupLat}}[{{$carpool.PassengerPickupLng}}, {{$carpool.PassengerPickupLat}}]{{else}}null{{end}},
 | 
						|
            arrivalLocation: {{if and $carpool.PassengerDropLng $carpool.PassengerDropLat}}[{{$carpool.PassengerDropLng}}, {{$carpool.PassengerDropLat}}]{{else}}null{{end}},
 | 
						|
            distance: {{if $carpool.Distance}}{{$carpool.Distance}}{{else}}0{{end}},
 | 
						|
            passengerPickupAddress: {{if $carpool.PassengerPickupAddress}}"{{$carpool.PassengerPickupAddress}}"{{else}}null{{end}},
 | 
						|
            passengerDropAddress: {{if $carpool.PassengerDropAddress}}"{{$carpool.PassengerDropAddress}}"{{else}}null{{end}},
 | 
						|
            driverDepartureAddress: {{if $carpool.DriverDepartureAddress}}"{{$carpool.DriverDepartureAddress}}"{{else}}null{{end}},
 | 
						|
            driverArrivalAddress: {{if $carpool.DriverArrivalAddress}}"{{$carpool.DriverArrivalAddress}}"{{else}}null{{end}},
 | 
						|
            pickupDate: {{if $carpool.PassengerPickupDate}}"{{$carpool.PassengerPickupDate.AsTime.Format "02/01/2006 15:04"}}"{{else}}null{{end}},
 | 
						|
            profileValidated: {{carpoolDriverValidatedProfile $driver (carpoolDocuments $driver.ID)}},
 | 
						|
            polyline: {{if $carpool.JourneyPolyline}}'{{$carpool.JourneyPolyline}}'{{else}}null{{end}},
 | 
						|
            driverDepartureLocation: {{if and $carpool.DriverDepartureLng $carpool.DriverDepartureLat}}[{{$carpool.DriverDepartureLng}}, {{$carpool.DriverDepartureLat}}]{{else}}null{{end}},
 | 
						|
            driverArrivalLocation: {{if and $carpool.DriverArrivalLng $carpool.DriverArrivalLat}}[{{$carpool.DriverArrivalLng}}, {{$carpool.DriverArrivalLat}}]{{else}}null{{end}}
 | 
						|
        },
 | 
						|
        {{end}}
 | 
						|
    ];
 | 
						|
 | 
						|
    const carpools = [
 | 
						|
        {{range $carpoolIndex, $carpoolFC := .ViewState.carpools}}
 | 
						|
        {{$carpoolData := index $carpoolFC.ExtraMembers "ocss"}}
 | 
						|
        {{$departure := index $carpoolFC.Features 0}}
 | 
						|
        {{$arrival := index $carpoolFC.Features 1}}
 | 
						|
        {
 | 
						|
            operator: '{{$carpoolData.Operator}}',
 | 
						|
            driverAlias: {{if $carpoolData.Driver}}'{{$carpoolData.Driver.Alias}}'{{else}}'Conducteur'{{end}},
 | 
						|
            pickupLocation: [{{$carpoolData.PassengerPickupLng}}, {{$carpoolData.PassengerPickupLat}}],
 | 
						|
            dropLocation: [{{$carpoolData.PassengerDropLng}}, {{$carpoolData.PassengerDropLat}}],
 | 
						|
            driverDepartureLocation: {{if and $carpoolData.DriverDepartureLng $carpoolData.DriverDepartureLat}}[{{$carpoolData.DriverDepartureLng}}, {{$carpoolData.DriverDepartureLat}}]{{else}}null{{end}},
 | 
						|
            driverArrivalLocation: {{if and $carpoolData.DriverArrivalLng $carpoolData.DriverArrivalLat}}[{{$carpoolData.DriverArrivalLng}}, {{$carpoolData.DriverArrivalLat}}]{{else}}null{{end}},
 | 
						|
            pickupAddress: {{if $carpoolData.PassengerPickupAddress}}'{{$carpoolData.PassengerPickupAddress}}'{{else}}null{{end}},
 | 
						|
            dropAddress: {{if $carpoolData.PassengerDropAddress}}'{{$carpoolData.PassengerDropAddress}}'{{else}}null{{end}},
 | 
						|
            distance: {{if $carpoolData.Distance}}{{$carpoolData.Distance}}{{else}}null{{end}},
 | 
						|
            duration: {{if $carpoolData.Duration}}{{$carpoolData.Duration.Seconds}}{{else}}null{{end}},
 | 
						|
            price: {{if and $carpoolData.Price $carpoolData.Price.Amount}}{{$carpoolData.Price.Amount}}{{else}}null{{end}},
 | 
						|
            seats: {{if $carpoolData.AvailableSteats}}{{$carpoolData.AvailableSteats}}{{else}}null{{end}},
 | 
						|
            webUrl: {{if $carpoolData.JourneySchedule}}{{if $carpoolData.JourneySchedule.WebUrl}}'{{$carpoolData.JourneySchedule.WebUrl}}'{{else}}null{{end}}{{else}}null{{end}},
 | 
						|
            polyline: {{if $carpoolData.JourneyPolyline}}'{{$carpoolData.JourneyPolyline}}'{{else}}null{{end}}
 | 
						|
        },
 | 
						|
        {{end}}
 | 
						|
    ];
 | 
						|
 | 
						|
    const kbSolutions = [
 | 
						|
        {{range $index, $solution := .ViewState.kb_data}}
 | 
						|
        {
 | 
						|
            title: {{if $solution.title}}"{{$solution.title}}"{{else if $solution.name}}"{{$solution.name}}"{{else}}"Solution locale"{{end}},
 | 
						|
            description: {{if $solution.description}}"{{$solution.description}}"{{else}}null{{end}},
 | 
						|
            url: {{if $solution.url}}"{{$solution.url}}"{{else}}null{{end}},
 | 
						|
            geography: {{if $solution.geography}}{{json $solution.geography}}{{else}}[]{{end}}
 | 
						|
        },
 | 
						|
        {{end}}
 | 
						|
    ];
 | 
						|
 | 
						|
    return {
 | 
						|
        selectedType: null,
 | 
						|
        selectedIndex: null,
 | 
						|
        selectedDriverIndex: null,
 | 
						|
        selectedOrganizedCarpoolIndex: null,
 | 
						|
        map: null,
 | 
						|
        startMarker: null,
 | 
						|
        endMarker: null,
 | 
						|
        routeMarkers: [],
 | 
						|
        transitJourneys: transitJourneys,
 | 
						|
        solidarityJourneys: solidarityJourneys,
 | 
						|
        organizedCarpools: organizedCarpools,
 | 
						|
        carpools: carpools,
 | 
						|
        kbSolutions: kbSolutions,
 | 
						|
 | 
						|
        filters: {
 | 
						|
            transit: true,
 | 
						|
            solidarity: true,
 | 
						|
            organizedCarpool: true,
 | 
						|
            carpool: true,
 | 
						|
            kb: true
 | 
						|
        },
 | 
						|
 | 
						|
        init() {
 | 
						|
            // Initialize map
 | 
						|
            if (typeof maplibregl !== 'undefined' && typeof pmtiles !== 'undefined') {
 | 
						|
                let protocol = new pmtiles.Protocol();
 | 
						|
                maplibregl.addProtocol("pmtiles", protocol.tile);
 | 
						|
 | 
						|
                {{if and .ViewState.departure .ViewState.departure.Geometry}}
 | 
						|
                const departureFeature = {{json .ViewState.departure}};
 | 
						|
                const destinationFeature = {{if .ViewState.destination}}{{json .ViewState.destination}}{{else}}null{{end}};
 | 
						|
 | 
						|
                this.map = new maplibregl.Map({
 | 
						|
                    container: 'compact-journey-map',
 | 
						|
                    style: '/public/maps/protomaps-light/style.json',
 | 
						|
                    center: departureFeature.geometry.coordinates,
 | 
						|
                    zoom: 12
 | 
						|
                });
 | 
						|
 | 
						|
                this.map.on('load', () => {
 | 
						|
                    // Create custom departure marker with map-pin from heroicons
 | 
						|
                    const departureEl = document.createElement('div');
 | 
						|
                    departureEl.className = 'w-8 h-8 rounded-co bg-co-green border border-white shadow-md flex items-center justify-center';
 | 
						|
                    departureEl.innerHTML = `
 | 
						|
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="16" height="16">
 | 
						|
                            <path fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
 | 
						|
                        </svg>
 | 
						|
                    `;
 | 
						|
                    this.startMarker = new maplibregl.Marker({element: departureEl})
 | 
						|
                        .setLngLat(departureFeature.geometry.coordinates)
 | 
						|
                        .addTo(this.map);
 | 
						|
 | 
						|
                    if (destinationFeature) {
 | 
						|
                        // Create custom destination marker with flag from heroicons
 | 
						|
                        const destinationEl = document.createElement('div');
 | 
						|
                        destinationEl.className = 'w-8 h-8 rounded-co bg-co-red border border-white shadow-md flex items-center justify-center';
 | 
						|
                        destinationEl.innerHTML = `
 | 
						|
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="16" height="16">
 | 
						|
                                <path fill-rule="evenodd" d="M3 2.25a.75.75 0 01.75.75v.54l1.838-.46a9.75 9.75 0 016.725.738l.108.054a8.25 8.25 0 005.58.652l3.109-.732a.75.75 0 01.917.81 47.784 47.784 0 00.005 10.337.75.75 0 01-.574.812l-3.114.733a9.75 9.75 0 01-6.594-.77l-.108-.054a8.25 8.25 0 00-5.69-.625l-2.202.55V21a.75.75 0 01-1.5 0V3A.75.75 0 013 2.25z" clip-rule="evenodd" />
 | 
						|
                            </svg>
 | 
						|
                        `;
 | 
						|
                        this.endMarker = new maplibregl.Marker({element: destinationEl})
 | 
						|
                            .setLngLat(destinationFeature.geometry.coordinates)
 | 
						|
                            .addTo(this.map);
 | 
						|
 | 
						|
                        // Fit bounds to show both markers
 | 
						|
                        const bounds = new maplibregl.LngLatBounds()
 | 
						|
                            .extend(departureFeature.geometry.coordinates)
 | 
						|
                            .extend(destinationFeature.geometry.coordinates);
 | 
						|
                        this.map.fitBounds(bounds, { padding: 50 });
 | 
						|
                    }
 | 
						|
                });
 | 
						|
                {{else}}
 | 
						|
                this.map = new maplibregl.Map({
 | 
						|
                    container: 'compact-journey-map',
 | 
						|
                    style: '/public/maps/protomaps-light/style.json',
 | 
						|
                    center: [2.3522, 48.8566],
 | 
						|
                    zoom: 12
 | 
						|
                });
 | 
						|
                {{end}}
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        selectSolution(type, index) {
 | 
						|
            // If clicking on the already selected solution, close it
 | 
						|
            if (this.selectedType === type && this.selectedIndex === index) {
 | 
						|
                this.clearRoutes();
 | 
						|
                this.selectedType = null;
 | 
						|
                this.selectedIndex = null;
 | 
						|
                this.selectedDriverIndex = null;
 | 
						|
                this.selectedOrganizedCarpoolIndex = null;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Clear existing route layers first
 | 
						|
            this.clearRoutes();
 | 
						|
 | 
						|
            // Reset selections when changing solution type
 | 
						|
            if (type !== 'solidarity') {
 | 
						|
                this.selectedDriverIndex = null;
 | 
						|
            }
 | 
						|
            if (type !== 'organized_carpool') {
 | 
						|
                this.selectedOrganizedCarpoolIndex = null;
 | 
						|
            }
 | 
						|
 | 
						|
            // Update selection
 | 
						|
            this.selectedType = type;
 | 
						|
            this.selectedIndex = index;
 | 
						|
 | 
						|
            // Use setTimeout to ensure clearing completes before displaying new route
 | 
						|
            setTimeout(() => {
 | 
						|
                // Display the selected solution on the map
 | 
						|
                if (type === 'transit') {
 | 
						|
                    this.displayTransitRoute(index);
 | 
						|
                } else if (type === 'solidarity') {
 | 
						|
                    this.selectedDriverIndex = null;
 | 
						|
                    this.displaySolidarityRoute(index);
 | 
						|
                } else if (type === 'organized_carpool') {
 | 
						|
                    this.selectedOrganizedCarpoolIndex = null;
 | 
						|
                    this.displayOrganizedCarpoolRoute(index);
 | 
						|
                } else if (type === 'carpool') {
 | 
						|
                    this.displayCarpoolRoute(index);
 | 
						|
                } else if (type === 'kb') {
 | 
						|
                    this.displayKBSolution(index);
 | 
						|
                }
 | 
						|
            }, 500);
 | 
						|
        },
 | 
						|
 | 
						|
        selectDriver(driverIndex) {
 | 
						|
            // Clear routes before selecting new driver
 | 
						|
            this.clearRoutes();
 | 
						|
 | 
						|
            // Update driver selection
 | 
						|
            this.selectedDriverIndex = driverIndex;
 | 
						|
 | 
						|
            // Use setTimeout to ensure clearing completes before displaying new route
 | 
						|
            setTimeout(() => {
 | 
						|
                this.displaySolidarityRoute(0);
 | 
						|
            }, 500);
 | 
						|
        },
 | 
						|
 | 
						|
        selectOrganizedCarpool(carpoolIndex) {
 | 
						|
            // Clear routes before selecting new carpool
 | 
						|
            this.clearRoutes();
 | 
						|
 | 
						|
            // Update carpool selection
 | 
						|
            this.selectedOrganizedCarpoolIndex = carpoolIndex;
 | 
						|
 | 
						|
            // Use setTimeout to ensure clearing completes before displaying new route
 | 
						|
            setTimeout(() => {
 | 
						|
                this.displayOrganizedCarpoolRoute(carpoolIndex);
 | 
						|
            }, 500);
 | 
						|
        },
 | 
						|
 | 
						|
        clearRoutes() {
 | 
						|
            // Remove route markers
 | 
						|
            this.routeMarkers.forEach(marker => marker.remove());
 | 
						|
            this.routeMarkers = [];
 | 
						|
 | 
						|
            // Remove existing layers and sources
 | 
						|
            if (this.map && this.map.loaded()) {
 | 
						|
                // Create a copy of the layers array to avoid modification during iteration
 | 
						|
                const layers = [...this.map.getStyle().layers];
 | 
						|
                const layersToRemove = layers.filter(layer => layer.id.startsWith('route-'));
 | 
						|
 | 
						|
                layersToRemove.forEach((layer) => {
 | 
						|
                    if (this.map.getLayer(layer.id)) {
 | 
						|
                        this.map.removeLayer(layer.id);
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                // Create a copy of the sources object keys
 | 
						|
                const sources = Object.keys(this.map.getStyle().sources);
 | 
						|
                const sourcesToRemove = sources.filter(sourceId => sourceId.startsWith('route-'));
 | 
						|
 | 
						|
                sourcesToRemove.forEach((sourceId) => {
 | 
						|
                    if (this.map.getSource(sourceId)) {
 | 
						|
                        this.map.removeSource(sourceId);
 | 
						|
                    }
 | 
						|
                });
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        displayTransitRoute(index) {
 | 
						|
            if (!this.map || !this.map.loaded() || !this.transitJourneys[index]) return;
 | 
						|
 | 
						|
            const journey = this.transitJourneys[index];
 | 
						|
            const allCoords = [];
 | 
						|
 | 
						|
            journey.legs.forEach((leg, legIndex) => {
 | 
						|
                const lineColor = leg.mode === 'WALK' ? '#9ca3af' : '#' + leg.color;
 | 
						|
                const lineWidth = leg.mode === 'WALK' ? 2 : 4;
 | 
						|
                const lineDasharray = leg.mode === 'WALK' ? [2, 2] : undefined;
 | 
						|
 | 
						|
                let coordinates;
 | 
						|
                if (leg.polyline && typeof polyline !== 'undefined') {
 | 
						|
                    // Decode polyline with the precision from the API response
 | 
						|
                    const decoded = polyline.decode(leg.polyline, leg.precision);
 | 
						|
                    // Convert from [lat, lon] to [lon, lat] for MapLibre
 | 
						|
                    coordinates = decoded.map((coord) => [coord[1], coord[0]]);
 | 
						|
                    coordinates.forEach((coord) => allCoords.push(coord));
 | 
						|
                } else {
 | 
						|
                    // Fallback to straight line
 | 
						|
                    coordinates = [leg.from, leg.to];
 | 
						|
                    allCoords.push(leg.from);
 | 
						|
                    allCoords.push(leg.to);
 | 
						|
                }
 | 
						|
 | 
						|
                this.map.addSource('route-transit-' + index + '-' + legIndex, {
 | 
						|
                    'type': 'geojson',
 | 
						|
                    'data': {
 | 
						|
                        'type': 'Feature',
 | 
						|
                        'properties': {},
 | 
						|
                        'geometry': {
 | 
						|
                            'type': 'LineString',
 | 
						|
                            'coordinates': coordinates
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                const layerConfig = {
 | 
						|
                    'id': 'route-transit-' + index + '-' + legIndex,
 | 
						|
                    'type': 'line',
 | 
						|
                    'source': 'route-transit-' + index + '-' + legIndex,
 | 
						|
                    'layout': {
 | 
						|
                        'line-join': 'round',
 | 
						|
                        'line-cap': 'round'
 | 
						|
                    },
 | 
						|
                    'paint': {
 | 
						|
                        'line-color': lineColor,
 | 
						|
                        'line-width': lineWidth
 | 
						|
                    }
 | 
						|
                };
 | 
						|
 | 
						|
                if (lineDasharray) {
 | 
						|
                    layerConfig.paint['line-dasharray'] = lineDasharray;
 | 
						|
                }
 | 
						|
 | 
						|
                this.map.addLayer(layerConfig);
 | 
						|
            });
 | 
						|
 | 
						|
            // Fit map to show all coordinates
 | 
						|
            if (allCoords.length > 0) {
 | 
						|
                const bounds = allCoords.reduce((bounds, coord) => {
 | 
						|
                    return bounds.extend(coord);
 | 
						|
                }, new maplibregl.LngLatBounds(allCoords[0], allCoords[0]));
 | 
						|
 | 
						|
                this.map.fitBounds(bounds, {
 | 
						|
                    padding: 50
 | 
						|
                });
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        displaySolidarityRoute(index) {
 | 
						|
            if (!this.map || !this.map.loaded()) return;
 | 
						|
 | 
						|
            // If a specific driver is selected, show their route
 | 
						|
            if (this.selectedDriverIndex !== null && this.solidarityJourneys[this.selectedDriverIndex]) {
 | 
						|
                const journey = this.solidarityJourneys[this.selectedDriverIndex];
 | 
						|
                const allCoords = [];
 | 
						|
 | 
						|
                // If we have a detailed polyline, use it
 | 
						|
                if (journey.polyline && typeof polyline !== 'undefined') {
 | 
						|
                    const decoded = polyline.decode(journey.polyline, 5); // Assume precision 5 for Google polyline
 | 
						|
                    // Convert from [lat, lon] to [lon, lat] for MapLibre
 | 
						|
                    const coordinates = decoded.map((coord) => [coord[1], coord[0]]);
 | 
						|
 | 
						|
                    coordinates.forEach((coord) => allCoords.push(coord));
 | 
						|
 | 
						|
                    this.map.addSource('route-solidarity-polyline', {
 | 
						|
                        'type': 'geojson',
 | 
						|
                        'data': {
 | 
						|
                            'type': 'Feature',
 | 
						|
                            'properties': {},
 | 
						|
                            'geometry': {
 | 
						|
                                'type': 'LineString',
 | 
						|
                                'coordinates': coordinates
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    });
 | 
						|
 | 
						|
                    this.map.addLayer({
 | 
						|
                        'id': 'route-solidarity-polyline',
 | 
						|
                        'type': 'line',
 | 
						|
                        'source': 'route-solidarity-polyline',
 | 
						|
                        'paint': {
 | 
						|
                            'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-lightblue').trim(),
 | 
						|
                            'line-width': 4
 | 
						|
                        }
 | 
						|
                    });
 | 
						|
                } else {
 | 
						|
                    // Fallback to simple line segments if no polyline
 | 
						|
                    if (journey.driverLocation && journey.passengerPickup) {
 | 
						|
                        allCoords.push(journey.driverLocation, journey.passengerPickup);
 | 
						|
 | 
						|
                        this.map.addSource('route-solidarity-driver-pickup', {
 | 
						|
                            'type': 'geojson',
 | 
						|
                            'data': {
 | 
						|
                                'type': 'Feature',
 | 
						|
                                'properties': {},
 | 
						|
                                'geometry': {
 | 
						|
                                    'type': 'LineString',
 | 
						|
                                    'coordinates': [journey.driverLocation, journey.passengerPickup]
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
 | 
						|
                        this.map.addLayer({
 | 
						|
                            'id': 'route-solidarity-driver-pickup',
 | 
						|
                            'type': 'line',
 | 
						|
                            'source': 'route-solidarity-driver-pickup',
 | 
						|
                            'paint': {
 | 
						|
                                'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-lightblue').trim(),
 | 
						|
                                'line-width': 4
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (journey.passengerPickup && journey.passengerDropoff) {
 | 
						|
                        allCoords.push(journey.passengerDropoff);
 | 
						|
 | 
						|
                        this.map.addSource('route-solidarity-passenger', {
 | 
						|
                            'type': 'geojson',
 | 
						|
                            'data': {
 | 
						|
                                'type': 'Feature',
 | 
						|
                                'properties': {},
 | 
						|
                                'geometry': {
 | 
						|
                                    'type': 'LineString',
 | 
						|
                                    'coordinates': [journey.passengerPickup, journey.passengerDropoff]
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
 | 
						|
                        this.map.addLayer({
 | 
						|
                            'id': 'route-solidarity-passenger',
 | 
						|
                            'type': 'line',
 | 
						|
                            'source': 'route-solidarity-passenger',
 | 
						|
                            'paint': {
 | 
						|
                                'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-blue').trim(),
 | 
						|
                                'line-width': 4
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (journey.passengerDropoff && journey.driverDestination) {
 | 
						|
                        allCoords.push(journey.driverDestination);
 | 
						|
 | 
						|
                        this.map.addSource('route-solidarity-driver-dest', {
 | 
						|
                            'type': 'geojson',
 | 
						|
                            'data': {
 | 
						|
                                'type': 'Feature',
 | 
						|
                                'properties': {},
 | 
						|
                                'geometry': {
 | 
						|
                                    'type': 'LineString',
 | 
						|
                                    'coordinates': [journey.passengerDropoff, journey.driverDestination]
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
 | 
						|
                        this.map.addLayer({
 | 
						|
                            'id': 'route-solidarity-driver-dest',
 | 
						|
                            'type': 'line',
 | 
						|
                            'source': 'route-solidarity-driver-dest',
 | 
						|
                            'paint': {
 | 
						|
                                'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-blue').trim(),
 | 
						|
                                'line-width': 4,
 | 
						|
                                'line-dasharray': [2, 2]
 | 
						|
                            }
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                // Add markers for driver departure and destination
 | 
						|
                if (journey.driverLocation) {
 | 
						|
                    const driverStartEl = document.createElement('div');
 | 
						|
                    driverStartEl.className = 'w-8 h-8 rounded-co bg-co-blue border border-white shadow-md flex items-center justify-center';
 | 
						|
                    driverStartEl.innerHTML = `
 | 
						|
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                            <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                        </svg>
 | 
						|
                    `;
 | 
						|
                    const driverStartMarker = new maplibregl.Marker({element: driverStartEl})
 | 
						|
                        .setLngLat(journey.driverLocation)
 | 
						|
                        .addTo(this.map);
 | 
						|
                    this.routeMarkers.push(driverStartMarker);
 | 
						|
                }
 | 
						|
 | 
						|
                if (journey.driverDestination) {
 | 
						|
                    const driverEndEl = document.createElement('div');
 | 
						|
                    driverEndEl.className = 'w-8 h-8 rounded-co bg-co-blue border border-white shadow-md flex items-center justify-center';
 | 
						|
                    driverEndEl.innerHTML = `
 | 
						|
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                            <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                        </svg>
 | 
						|
                    `;
 | 
						|
                    const driverEndMarker = new maplibregl.Marker({element: driverEndEl})
 | 
						|
                        .setLngLat(journey.driverDestination)
 | 
						|
                        .addTo(this.map);
 | 
						|
                    this.routeMarkers.push(driverEndMarker);
 | 
						|
                }
 | 
						|
 | 
						|
                // Fit bounds to show the journey
 | 
						|
                if (allCoords.length > 0) {
 | 
						|
                    const bounds = allCoords.reduce((bounds, coord) => {
 | 
						|
                        return bounds.extend(coord);
 | 
						|
                    }, new maplibregl.LngLatBounds(allCoords[0], allCoords[0]));
 | 
						|
 | 
						|
                    this.map.fitBounds(bounds, {
 | 
						|
                        padding: 80
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                // Show all driver locations when no specific driver is selected
 | 
						|
                const driverLocations = [];
 | 
						|
                this.solidarityJourneys.forEach((journey) => {
 | 
						|
                    if (journey.driverLocation) {
 | 
						|
                        driverLocations.push(journey.driverLocation);
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                if (driverLocations.length === 0) return;
 | 
						|
 | 
						|
                // Create custom marker element for each driver
 | 
						|
                this.solidarityJourneys.forEach((journey, idx) => {
 | 
						|
                    if (!journey.driverLocation) return;
 | 
						|
 | 
						|
                    const el = document.createElement('div');
 | 
						|
                    el.className = 'w-8 h-8 rounded-co bg-co-blue border border-white shadow-md flex items-center justify-center cursor-pointer transition-all';
 | 
						|
                    el.setAttribute('data-driver-index', idx);
 | 
						|
 | 
						|
                    // Add person icon (using SVG)
 | 
						|
                    el.innerHTML = `
 | 
						|
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                            <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                        </svg>
 | 
						|
                    `;
 | 
						|
 | 
						|
                    // Add click handler
 | 
						|
                    el.addEventListener('click', (e) => {
 | 
						|
                        e.stopPropagation();
 | 
						|
                        this.selectedDriverIndex = idx;
 | 
						|
                        this.displaySolidarityRoute(0);
 | 
						|
                    });
 | 
						|
 | 
						|
                    const marker = new maplibregl.Marker({element: el})
 | 
						|
                        .setLngLat(journey.driverLocation)
 | 
						|
                        .addTo(this.map);
 | 
						|
 | 
						|
                    this.routeMarkers.push(marker);
 | 
						|
                });
 | 
						|
 | 
						|
                // Fit bounds to show all driver locations
 | 
						|
                if (driverLocations.length > 0) {
 | 
						|
                    const bounds = driverLocations.reduce((bounds, coord) => {
 | 
						|
                        return bounds.extend(coord);
 | 
						|
                    }, new maplibregl.LngLatBounds(driverLocations[0], driverLocations[0]));
 | 
						|
 | 
						|
                    this.map.fitBounds(bounds, {
 | 
						|
                        padding: 80
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        displayOrganizedCarpoolRoute(index) {
 | 
						|
            if (!this.map || !this.map.loaded() || !this.organizedCarpools[index]) return;
 | 
						|
 | 
						|
            const carpool = this.organizedCarpools[index];
 | 
						|
            const allCoords = [];
 | 
						|
 | 
						|
            // If we have a detailed polyline, use it
 | 
						|
            if (carpool.polyline && typeof polyline !== 'undefined') {
 | 
						|
                const decoded = polyline.decode(carpool.polyline, 5);
 | 
						|
                const coordinates = decoded.map((coord) => [coord[1], coord[0]]);
 | 
						|
 | 
						|
                coordinates.forEach((coord) => allCoords.push(coord));
 | 
						|
 | 
						|
                this.map.addSource('route-carpool-polyline', {
 | 
						|
                    'type': 'geojson',
 | 
						|
                    'data': {
 | 
						|
                        'type': 'Feature',
 | 
						|
                        'properties': {},
 | 
						|
                        'geometry': {
 | 
						|
                            'type': 'LineString',
 | 
						|
                            'coordinates': coordinates
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                this.map.addLayer({
 | 
						|
                    'id': 'route-carpool-polyline',
 | 
						|
                    'type': 'line',
 | 
						|
                    'source': 'route-carpool-polyline',
 | 
						|
                    'paint': {
 | 
						|
                        'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-blue').trim(),
 | 
						|
                        'line-width': 4
 | 
						|
                    }
 | 
						|
                });
 | 
						|
            }
 | 
						|
 | 
						|
            // Add markers for driver departure and arrival
 | 
						|
            if (carpool.driverDepartureLocation) {
 | 
						|
                const driverStartEl = document.createElement('div');
 | 
						|
                driverStartEl.className = 'w-8 h-8 rounded-co bg-co-blue border border-white shadow-md flex items-center justify-center';
 | 
						|
                driverStartEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const driverStartMarker = new maplibregl.Marker({element: driverStartEl})
 | 
						|
                    .setLngLat(carpool.driverDepartureLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(driverStartMarker);
 | 
						|
                allCoords.push(carpool.driverDepartureLocation);
 | 
						|
            }
 | 
						|
 | 
						|
            if (carpool.driverArrivalLocation) {
 | 
						|
                const driverEndEl = document.createElement('div');
 | 
						|
                driverEndEl.className = 'w-8 h-8 rounded-co bg-co-blue border border-white shadow-md flex items-center justify-center';
 | 
						|
                driverEndEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const driverEndMarker = new maplibregl.Marker({element: driverEndEl})
 | 
						|
                    .setLngLat(carpool.driverArrivalLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(driverEndMarker);
 | 
						|
                allCoords.push(carpool.driverArrivalLocation);
 | 
						|
            }
 | 
						|
 | 
						|
            // Add passenger pickup and drop locations to bounds (markers already exist)
 | 
						|
            if (carpool.departureLocation) {
 | 
						|
                allCoords.push(carpool.departureLocation);
 | 
						|
            }
 | 
						|
            if (carpool.arrivalLocation) {
 | 
						|
                allCoords.push(carpool.arrivalLocation);
 | 
						|
            }
 | 
						|
 | 
						|
            // Fit bounds to show all points
 | 
						|
            if (allCoords.length > 0) {
 | 
						|
                const bounds = allCoords.reduce((bounds, coord) => {
 | 
						|
                    return bounds.extend(coord);
 | 
						|
                }, new maplibregl.LngLatBounds(allCoords[0], allCoords[0]));
 | 
						|
 | 
						|
                this.map.fitBounds(bounds, {
 | 
						|
                    padding: 80
 | 
						|
                });
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        displayCarpoolRoute(index) {
 | 
						|
            if (!this.map || !this.map.loaded() || !this.carpools[index]) return;
 | 
						|
 | 
						|
            const carpool = this.carpools[index];
 | 
						|
            const allCoords = [];
 | 
						|
 | 
						|
            // If we have a detailed polyline, use it
 | 
						|
            if (carpool.polyline && typeof polyline !== 'undefined') {
 | 
						|
                const decoded = polyline.decode(carpool.polyline, 5);
 | 
						|
                const coordinates = decoded.map((coord) => [coord[1], coord[0]]);
 | 
						|
 | 
						|
                coordinates.forEach((coord) => allCoords.push(coord));
 | 
						|
 | 
						|
                this.map.addSource('route-carpool-rdex-polyline', {
 | 
						|
                    'type': 'geojson',
 | 
						|
                    'data': {
 | 
						|
                        'type': 'Feature',
 | 
						|
                        'properties': {},
 | 
						|
                        'geometry': {
 | 
						|
                            'type': 'LineString',
 | 
						|
                            'coordinates': coordinates
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                this.map.addLayer({
 | 
						|
                    'id': 'route-carpool-rdex-polyline',
 | 
						|
                    'type': 'line',
 | 
						|
                    'source': 'route-carpool-rdex-polyline',
 | 
						|
                    'paint': {
 | 
						|
                        'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-blue').trim(),
 | 
						|
                        'line-width': 4
 | 
						|
                    }
 | 
						|
                });
 | 
						|
            } else {
 | 
						|
                // If no polyline, draw lines between driver departure -> pickup -> drop -> driver arrival
 | 
						|
                const routeCoords = [];
 | 
						|
 | 
						|
                // Always use at least pickup and drop (they should always exist)
 | 
						|
                if (carpool.driverDepartureLocation) {
 | 
						|
                    routeCoords.push(carpool.driverDepartureLocation);
 | 
						|
                    allCoords.push(carpool.driverDepartureLocation);
 | 
						|
                }
 | 
						|
                if (carpool.pickupLocation) {
 | 
						|
                    routeCoords.push(carpool.pickupLocation);
 | 
						|
                    allCoords.push(carpool.pickupLocation);
 | 
						|
                }
 | 
						|
                if (carpool.dropLocation) {
 | 
						|
                    routeCoords.push(carpool.dropLocation);
 | 
						|
                    allCoords.push(carpool.dropLocation);
 | 
						|
                }
 | 
						|
                if (carpool.driverArrivalLocation) {
 | 
						|
                    routeCoords.push(carpool.driverArrivalLocation);
 | 
						|
                    allCoords.push(carpool.driverArrivalLocation);
 | 
						|
                }
 | 
						|
 | 
						|
                console.log('Carpool route coords:', routeCoords);
 | 
						|
 | 
						|
                if (routeCoords.length >= 2) {
 | 
						|
                    this.map.addSource('route-carpool-rdex-simple', {
 | 
						|
                        'type': 'geojson',
 | 
						|
                        'data': {
 | 
						|
                            'type': 'Feature',
 | 
						|
                            'properties': {},
 | 
						|
                            'geometry': {
 | 
						|
                                'type': 'LineString',
 | 
						|
                                'coordinates': routeCoords
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    });
 | 
						|
 | 
						|
                    this.map.addLayer({
 | 
						|
                        'id': 'route-carpool-rdex-simple',
 | 
						|
                        'type': 'line',
 | 
						|
                        'source': 'route-carpool-rdex-simple',
 | 
						|
                        'paint': {
 | 
						|
                            'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-blue').trim(),
 | 
						|
                            'line-width': 4,
 | 
						|
                            'line-dasharray': [2, 2] // dashed line to indicate it's not the actual route
 | 
						|
                        }
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Add markers for driver departure and arrival (if they exist)
 | 
						|
            if (carpool.driverDepartureLocation) {
 | 
						|
                const driverStartEl = document.createElement('div');
 | 
						|
                driverStartEl.className = 'w-8 h-8 rounded-co bg-blue-600 border border-white shadow-md flex items-center justify-center';
 | 
						|
                driverStartEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const driverStartMarker = new maplibregl.Marker({element: driverStartEl})
 | 
						|
                    .setLngLat(carpool.driverDepartureLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(driverStartMarker);
 | 
						|
            }
 | 
						|
 | 
						|
            if (carpool.driverArrivalLocation) {
 | 
						|
                const driverEndEl = document.createElement('div');
 | 
						|
                driverEndEl.className = 'w-8 h-8 rounded-co bg-blue-600 border border-white shadow-md flex items-center justify-center';
 | 
						|
                driverEndEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
 | 
						|
                        <circle cx="12" cy="7" r="4"></circle>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const driverEndMarker = new maplibregl.Marker({element: driverEndEl})
 | 
						|
                    .setLngLat(carpool.driverArrivalLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(driverEndMarker);
 | 
						|
            }
 | 
						|
 | 
						|
            // Add marker for pickup location (green)
 | 
						|
            if (carpool.pickupLocation) {
 | 
						|
                const pickupEl = document.createElement('div');
 | 
						|
                pickupEl.className = 'w-8 h-8 rounded-co bg-green-600 border border-white shadow-md flex items-center justify-center';
 | 
						|
                pickupEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
 | 
						|
                        <circle cx="12" cy="10" r="3"></circle>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const pickupMarker = new maplibregl.Marker({element: pickupEl})
 | 
						|
                    .setLngLat(carpool.pickupLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(pickupMarker);
 | 
						|
            }
 | 
						|
 | 
						|
            // Add marker for drop location (red)
 | 
						|
            if (carpool.dropLocation) {
 | 
						|
                const dropEl = document.createElement('div');
 | 
						|
                dropEl.className = 'w-8 h-8 rounded-co bg-red-600 border border-white shadow-md flex items-center justify-center';
 | 
						|
                dropEl.innerHTML = `
 | 
						|
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
						|
                        <path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>
 | 
						|
                        <line x1="4" y1="22" x2="4" y2="15"></line>
 | 
						|
                    </svg>
 | 
						|
                `;
 | 
						|
                const dropMarker = new maplibregl.Marker({element: dropEl})
 | 
						|
                    .setLngLat(carpool.dropLocation)
 | 
						|
                    .addTo(this.map);
 | 
						|
                this.routeMarkers.push(dropMarker);
 | 
						|
            }
 | 
						|
 | 
						|
            // Fit bounds to show all points
 | 
						|
            if (allCoords.length > 0) {
 | 
						|
                const bounds = allCoords.reduce((bounds, coord) => {
 | 
						|
                    return bounds.extend(coord);
 | 
						|
                }, new maplibregl.LngLatBounds(allCoords[0], allCoords[0]));
 | 
						|
 | 
						|
                this.map.fitBounds(bounds, {
 | 
						|
                    padding: 80
 | 
						|
                });
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        async displayKBSolution(index) {
 | 
						|
            if (!this.map || !this.map.loaded() || !this.kbSolutions[index]) return;
 | 
						|
 | 
						|
            const solution = this.kbSolutions[index];
 | 
						|
 | 
						|
            if (!solution.geography || solution.geography.length === 0) return;
 | 
						|
 | 
						|
            // Collect geography features from the solution
 | 
						|
            const allFeatures = [];
 | 
						|
            for (const geo of solution.geography) {
 | 
						|
                // Use the geography feature that was fetched by the backend
 | 
						|
                if (geo.geography && geo.geography.geometry) {
 | 
						|
                    allFeatures.push(geo.geography);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (allFeatures.length === 0) return;
 | 
						|
 | 
						|
            // Add all polygons to the map
 | 
						|
            allFeatures.forEach((feature, idx) => {
 | 
						|
                const sourceId = 'route-kb-' + index + '-' + idx;
 | 
						|
 | 
						|
                this.map.addSource(sourceId, {
 | 
						|
                    'type': 'geojson',
 | 
						|
                    'data': feature
 | 
						|
                });
 | 
						|
 | 
						|
                // Add fill layer
 | 
						|
                this.map.addLayer({
 | 
						|
                    'id': sourceId + '-fill',
 | 
						|
                    'type': 'fill',
 | 
						|
                    'source': sourceId,
 | 
						|
                    'paint': {
 | 
						|
                        'fill-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-orange').trim(),
 | 
						|
                        'fill-opacity': 0.2
 | 
						|
                    }
 | 
						|
                });
 | 
						|
 | 
						|
                // Add outline layer
 | 
						|
                this.map.addLayer({
 | 
						|
                    'id': sourceId + '-outline',
 | 
						|
                    'type': 'line',
 | 
						|
                    'source': sourceId,
 | 
						|
                    'paint': {
 | 
						|
                        'line-color': getComputedStyle(document.documentElement).getPropertyValue('--color-co-orange').trim(),
 | 
						|
                        'line-width': 2
 | 
						|
                    }
 | 
						|
                });
 | 
						|
            });
 | 
						|
 | 
						|
            // Fit bounds to show all polygons
 | 
						|
            const bounds = new maplibregl.LngLatBounds();
 | 
						|
            allFeatures.forEach(feature => {
 | 
						|
                if (feature.geometry.type === 'Polygon') {
 | 
						|
                    feature.geometry.coordinates[0].forEach(coord => {
 | 
						|
                        bounds.extend(coord);
 | 
						|
                    });
 | 
						|
                } else if (feature.geometry.type === 'MultiPolygon') {
 | 
						|
                    feature.geometry.coordinates.forEach(polygon => {
 | 
						|
                        polygon[0].forEach(coord => {
 | 
						|
                            bounds.extend(coord);
 | 
						|
                        });
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
            if (!bounds.isEmpty()) {
 | 
						|
                this.map.fitBounds(bounds, {
 | 
						|
                    padding: 50
 | 
						|
                });
 | 
						|
            }
 | 
						|
        }
 | 
						|
    };
 | 
						|
}
 | 
						|
</script>
 | 
						|
{{end}}
 |