resolve conflicts

This commit is contained in:
2025-06-19 07:28:29 +02:00
403 changed files with 100820 additions and 3088 deletions

View File

@@ -1,6 +1,15 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@theme {
--color-co-blue: #00727e;
--color-co-lightblue: #907eff;
--color-co-red: #de1581;
--color-co-green: #6cc11f;
--color-co-yellow: #ffdd00;
--color-co-orange: #ff5300;
--radius-co: 40%;
}
@font-face {
font-family: "Manometer";
@@ -16,4 +25,7 @@
html {
font-family: Bitter, serif;
}
}
input {
padding: 2px;
}
}

View File

@@ -1,6 +1,11 @@
import '@kingshott/iodine';
import Alpine from 'alpinejs'
import { Protocol } from "pmtiles";
import { layers, namedFlavor } from '@protomaps/basemaps';
window.Alpine = Alpine
Alpine.start()
Alpine.start()
let protocol = new Protocol();
maplibregl.addProtocol("pmtiles",protocol.tile);

View File

@@ -1,13 +1,12 @@
{{ define "address_autocomplete" }}
<div class="col-span-6 relative" x-data="{
input: {{if .Address}}'{{.Address.Properties.label}}'{{else}}null{{end}},
address: {{if .Address}}JSON.stringify({{printf "%s" .Address.MarshalJSON}}){{else}}null{{end}},
addressObject: {{if .Address}}{{printf "%s" .Address.MarshalJSON}}{{else}}null{{end}},
<div class="col-span-6 relative" x-data='{
input: {{if .Address}}"{{.Address.properties.label}}"{{else}}null{{end}},
address: {{if .Address}}JSON.stringify({{template "geojson" .Address}}){{else}}null{{end}},
addressObject: {{if .Address}}{{template "geojson" .Address }}{{else}}null{{end}},
responselength: 0,
async autocomplete() {
if(this.input == null || this.input == '') {
if(this.input == null || this.input == "") {
this.responselength = 0
return []
}
@@ -15,26 +14,31 @@
this.responselength = 0
return []
}
result = await fetch('https://geocode.ridygo.fr/v1/autocomplete/\?text=' + this.input)
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()
console.log(json)
this.responselength = json['features'].length
return json['features']
this.responselength = json["features"].length
return json["features"]
},
select(a) {
this.address = JSON.stringify(a)
this.addressObject = a
this.input = a.properties.label
}
}">
}'>
<input type="hidden" name="{{ .FieldName }}" x-model="address">
<label for="address" class="block text-sm font-medium text-gray-700">{{ if .FieldLabel }}{{.FieldLabel}}{{else}}Adresse{{end}}</label>
<input type="text"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-2xl"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-2xl"
x-model="input">
<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 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" tabindex="-1" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
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" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
<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"
@@ -45,4 +49,15 @@
</template>
</ul>
</div>
{{ end }}
{{ end }}
{{define "geojson"}}{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [{{index .geometry.coordinates 0}}, {{index .geometry.coordinates 1}}],
},
"properties": {
"label": "{{.properties.label}}"
}
}{{end}}

View File

@@ -1,19 +1,20 @@
{{define "mainmenu"}}
{{range .LayoutState.MenuItems}}
{{range .LayoutState.Menu}}
{{if moduleAvailable .name }}
<nav class="px-2 space-y-1">
<a href="{{.Link}}"
{{ if .Active }}
<a href="{{.link}}"
{{ if eq .name $.LayoutState.ActiveMenu }}
class="bg-white text-co-blue group flex items-center px-2 py-2 text-base font-medium rounded-2xl">
{{ else}}
class="text-white hover:bg-white hover:bg-opacity-5 group flex items-center px-2 py-2 text-base font-medium rounded-2xl">
class="text-white hover:bg-white hover:bg-opacity-5 hover:text-co-blue group flex items-center px-2 py-2 text-base font-medium rounded-2xl">
{{end}}
{{$.IconSet.Icon .Icon "mr-4 flex-shrink-0 h-6 w-6"}}
{{$.IconSet.Icon .icon "mr-4 flex-shrink-0 h-6 w-6"}}
{{.Title}}
{{.title}}
</a>
</nav>
{{end}}
{{end}}
{{end}}
{{end}}

View File

@@ -0,0 +1,64 @@
{{ define "address_autocomplete" }}
<div class="col-span-6 relative" x-data='{
input: {{if .Address}}"{{.Address.Properties.label}}"{{else}}null{{end}},
address: {{if .Address}}JSON.stringify({{template "geojson" .Address}}){{else}}null{{end}},
addressObject: {{if .Address}}{{template "geojson" .Address }}{{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()
console.log(json)
this.responselength = json["features"].length
return json["features"]
},
select(a) {
this.address = JSON.stringify(a)
this.addressObject = a
this.input = a.properties.label
}
}'>
<input type="hidden" name="{{ .FieldName }}" x-model="address">
<label for="address" class="block text-sm font-medium text-gray-700">{{ if .FieldLabel }}{{.FieldLabel}}{{else}}Adresse{{end}}</label>
<input type="text"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm border-gray-300 rounded-2xl"
x-model="input">
<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" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
<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>
{{ end }}
{{define "geojson"}}{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [{{index .Point 0}}, {{index .Point 1}}],
},
"properties": {
"label": "{{.Properties.label}}"
}
}{{end}}

View File

@@ -0,0 +1,56 @@
{{ define "submit_with_sms" }}
{{ $dialog := (or .ComponentState.dialogVar "dialog") }}
{{ $fieldName := (or .ComponentState.fieldName "message") }}
{{ $submitText := (or .ComponentState.submitText "Valider") }}
{{ $headerText := (or .ComponentState.headerText "Envoyer un message") }}
{{ $cancelText := (or .ComponentState.cancelText "Annuler" )}}
{{ $validateText := (or .ComponentState.validateText "Envoyer" )}}
{{ $infoText := (or .ComponentState.infoText "Le message suivant sera envoyé. Vous pouvez le personnaliser." )}}
{{ $doNotSendText := (or .ComponentState.doNotSendText "Ne pas envoyer le message par SMS." )}}
{{ $doNotSendOption := eq .ComponentState.doNotSendOption true }}
<div x-data="{ {{ $dialog }}: false}" class="text-center">
<button @click="{{ $dialog }} = !{{ $dialog }}" type="button" class="bg-co-blue border-gray-300 border px-4 py-2 text-white items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{ .ComponentState.submitText }}
</button>
<div x-show="{{$dialog}}" class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full max-w-lg sm:p-6">
<div>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-co bg-co-blue text-white">
{{$.IconSet.Icon "hero:outline/paper-airplane" "h-6 w-6"}}
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">{{ $headerText }}</h3>
<div class="mt-2">
<p class="text-sm text-gray-500"></p>
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700">{{$infoText}}</label>
<div class="mt-1">
<textarea rows="4" name="message" id="message" class="block w-full rounded-2xl border-gray-300 shadow-sm focus:border-co-blue focus:ring-co-blue sm:text-sm">{{template "sms_template" .SMSState }}</textarea>
</div>
</div>
{{ if $doNotSendOption}}
<div>
<div class="mt-1 flex flex-row justify-items-center items-center">
<div class="flex-none m-4"><input type="checkbox" name="do_not_send" id="do_not_send" class="block w-full rounded-2xl border-gray-300 shadow-sm focus:border-co-blue focus:ring-co-blue sm:text-sm" /></div>
<label for="do_not_send" class="flex text-sm text-gray-700 align-middle">{{$doNotSendText}}</label>
</div>
</div>
</div>
{{end}}
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
<button @click="{{$dialog}} = !{{$dialog}}" type="button" class="mt-3 inline-flex w-full justify-center rounded-l-2xl border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-1 sm:mt-0 sm:text-sm">{{ $cancelText }}</button>
<button type="submit" class="inline-flex w-full justify-center rounded-r-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-2 sm:text-sm">{{ $validateText }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{ end }}

View File

@@ -22,7 +22,7 @@
this.responselength = 0
this.address = this.def
this.input = `{{ .Default.properties.label}}`
result = await fetch('https://geocode.ridygo.fr/v1/autocomplete/\?text=' + this.input)
result = await fetch('https://api-adresse.data.gouv.fr/search/?q=' + this.input)
json = await result.json()
bb = json['features'][0]
@@ -33,7 +33,7 @@
return [bb]
}
result = await fetch('https://geocode.ridygo.fr/v1/autocomplete/\?text=' + this.input)
result = await fetch('https://api-adresse.data.gouv.fr/search/?q=' + this.input)
json = await result.json()
this.responselength = json['features'].length
@@ -64,4 +64,4 @@
</template>
</ul>
</div>
{{ end }}
{{ end }}

View File

@@ -0,0 +1,134 @@
{{define "event_files"}}
<div class="px-4 py-6 sm:px-6"
x-data="{
fields: {
name: null,
type: null,
file: null,
},
rules: {
name: ['required'],
type: ['required'],
file: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
type: {valid: null},
file: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
{{if eq (len .ViewState.documents) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun document</p>
{{end}}
{{if gt (len .ViewState.documents) 0}}
<div class="-mx-4 mb-10 ring-1 ring-gray-300 sm:-mx-6 md:mx-0 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Type</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Nom du document</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Ajouté le</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
<span class="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody>
{{range .ViewState.documents}}
<tr>
<td class="relative py-4 pl-4 sm:pl-6 pr-3 text-sm">
<div class="font-medium text-gray-900">
<span class="bg-co-blue text-xs text-white rounded-xl p-1 mr-2">{{index $.ViewState.file_types_map .Metadata.Type}}</span>
</div>
</td>
<td class="px-3 py-3.5 text-sm text-gray-900 lg:table-cell">{{.Metadata.Name}}</td>
<td class="px-3 py-3.5 text-sm text-gray-500 lg:table-cell">{{.LastModified.Format "02/01/2006"}}</td>
<td class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right text-sm font-medium">
<a href="/app/agenda/{{$.ViewState.event.ID}}/documents/{{.FileName}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le document</span></button>
</a>
</td>
</tr>
{{end}}
<!-- More plans... -->
</tbody>
</table>
</div>
{{end}}
{{ if eq (index .ViewState.event.Owners 0) .Group.ID }}
<h3 class="text-lg">Ajouter un document</h3>
<form method="POST" action="/app/agenda/{{.ViewState.event.ID}}/documents" @submit="submit" enctype="multipart/form-data">
<div class="md:grid md:grid-cols-6 p-2">
<div class="sm:col-span-2">
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
<select id="type" name="type" class="mt-1 block w-full rounded-l-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
x-model="fields.type" @blur="validateField('type')"
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option value="" selected>Sélectionner un type</option>
{{range .ViewState.events_file_types}}
<option value="{{.}}">{{index $.ViewState.file_types_map .}}</option>
{{end}}
</select>
</div>
<div class="sm:col-span-4">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name"
placeholder="Nom du fichier"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'" />
</div>
<div class="sm:col-span-6 mt-4">
<label for="cover-photo" class="block text-sm font-medium text-gray-700">Téléchargement</label>
<div class="mt-1 flex justify-center rounded-md border-2 border-dashed px-6 pt-5 pb-6"
x-on:drop="console.log('toto')"
:class="formValidation.fields.file.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<div class="space-y-1 text-center">
{{.IconSet.Icon "hero:outline/folder-plus" "mx-auto h-12 w-12 text-gray-400"}}
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer rounded-md bg-white font-medium text-co-blue focus-within:outline-none focus-within:ring-2 focus-within:ring-co-blue focus-within:ring-offset-2 hover:text-co-blue">
<span>Sélectionnez un fichier </span>
<input id="file-upload" name="file-upload" type="file" class="sr-only"
x-model="fields.file" @blur="validateField('file')">
</label>
<!-- <p class="pl-1">ou glissez-déposez</p> -->
</div>
<p class="text-xs text-gray-500">Jusqu'à 10MB</p>
<p class="text-co-blue p-2" x-text="fields.file" x-if="fields.file"></p>
</div>
</div>
</div>
</div>
<button type="submit"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 mt-8 w-full text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Ajouter le document
</button>
</form>
{{end}}
</div>
{{end}}

View File

@@ -4,18 +4,20 @@
<h1 class="text-2xl font-semibold text-gray-900">Ajouter à l'agenda</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8" x-data="{
fields: {
name: null,
type: null,
description: null,
description: '',
allday: false,
startdate: null,
enddate: null,
starttime: null,
endtime: null,
max_subscribers: 0,
file_name: null,
file_type: null,
file: null,
},
rules: {
name: ['required'],
@@ -25,7 +27,10 @@
starttime: ['optional'],
endtime: ['optional'],
description: ['optional'],
max_subscribers: ['required', 'min:0']
max_subscribers: ['required', 'min:0'],
file_name: ['optional'],
file_type: ['optional'],
file: ['optional'],
},
formValidation: {
valid: false,
@@ -39,9 +44,22 @@
endtime: {valid: null},
allday: {valid: null},
max_subscribers: {valid: null},
file_name: {valid: null},
file_type: {valid: null},
file: {valid: null},
}
},
isFormValid: true,
init() {
let quill = new Quill(this.$refs.quill, { theme: 'snow' })
quill.root.innerHTML = this.fields.description
quill.on('text-change', () => {
this.fields.description = quill.root.innerHTML
})
},
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
@@ -62,12 +80,14 @@
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<form id="eventForm" class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations sur le dispositif</h3>
<p class="mt-1 text-sm text-gray-500">Informations générales sur le dispositif d'accompagnement à ajouter à l'agenda</p>
<p class="mt-1 text-sm text-gray-500">Informations générales sur le dispositif d'accompagnement à
ajouter à l'agenda</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
@@ -80,8 +100,7 @@
</div>
<div class="sm:col-span-3">
<label for="type" class="block text-sm font-medium text-gray-700">Type de dispositif</label>
<select id="type" name="type"
x-model="fields.type" @blur="validateField('type')"
<select id="type" name="type" x-model="fields.type" @blur="validateField('type')"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm rounded-2xl"
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option></option>
@@ -94,10 +113,12 @@
<div class="col-span-6">
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
<div class="mt-1">
<textarea rows="4" name="description" id="descrpition"
x-model="fields.description" @blur="validateField('description')"
<!-- <textarea rows="4" name="description" id="descrpition" x-model="fields.description"
@blur="validateField('description')"
:class="formValidation.fields.description.valid == false ? 'border-co-red border-2' : 'border-gray-300'"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea> -->
<input type="hidden" name="description" x-model="fields.description" />
<div x-ref="quill" class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-b-2xl">></div>
</div>
</div>
@@ -119,12 +140,14 @@
<div class="col-span-6">
<button type="button" class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2" role="switch" aria-checked="false"
:class="fields.allday ? 'bg-co-blue' : 'bg-gray-200'"
<button type="button"
class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2"
role="switch" aria-checked="false" :class="fields.allday ? 'bg-co-blue' : 'bg-gray-200'"
@click="fields.allday = ! fields.allday">
<span class="sr-only">Use setting</span>
<!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" -->
<span aria-hidden="true" class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
<span aria-hidden="true"
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
:class="fields.allday ? 'translate-x-5' : 'translate-x-0'"></span>
</button> <span class="text-md font-medium text-gray-700 ml-2">Toute la journée</span>
<input type="hidden" name="allday" x-model="fields.allday">
@@ -134,42 +157,46 @@
<div class="sm:col-span-6">
<div class="inline-flex w-full">
<div class="flex-1">
<label for="startdate" class="block text-sm font-medium text-gray-700">Date de début</label>
<label for="startdate" class="block text-sm font-medium text-gray-700">Date de
début</label>
<input type="date" name="startdate" id="startdate" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-l-2xl"
x-model="fields.startdate" @blur="validateField('startdate')"
:class="formValidation.fields.startdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-l-2xl"
x-model="fields.startdate" @blur="validateField('startdate')"
:class="formValidation.fields.startdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="flex-1">
<label for="enddate" class="block text-sm font-medium text-gray-700">Date de fin</label>
<label for="enddate" class="block text-sm font-medium text-gray-700">Date de
fin</label>
<input type="date" name="enddate" id="enddate" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.enddate" @blur="validateField('enddate')"
:class="formValidation.fields.enddate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.enddate" @blur="validateField('enddate')"
:class="formValidation.fields.enddate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
<div class="sm:col-span-6" x-show="!fields.allday">
<div class="inline-flex w-full">
<div class="flex-1">
<label for="startdate" class="block text-sm font-medium text-gray-700">Horaire de début</label>
<label for="startdate" class="block text-sm font-medium text-gray-700">Horaire de
début</label>
<input type="time" name="starttime" id="starttime" placeholder="00:00"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-l-2xl"
x-model="fields.starttime" @blur="validateField('starttime')"
:class="formValidation.fields.starttime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-l-2xl"
x-model="fields.starttime" @blur="validateField('starttime')"
:class="formValidation.fields.starttime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="flex-1">
<label for="endtime" class="block text-sm font-medium text-gray-700">Horaire de fin</label>
<label for="endtime" class="block text-sm font-medium text-gray-700">Horaire de
fin</label>
<input type="time" name="endtime" id="endtime" placeholder="00:00"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.endtime" @blur="validateField('endtime')"
:class="formValidation.fields.endtime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.endtime" @blur="validateField('endtime')"
:class="formValidation.fields.endtime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
@@ -184,12 +211,14 @@
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Paramètres</h3>
<p class="mt-1 text-sm text-gray-500">Paramètres du dispositift (nombre de places disponibles, etc...)</p>
<p class="mt-1 text-sm text-gray-500">Paramètres du dispositif (nombre de places disponibles,
etc...)</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2 align-middle">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="max_subscribers" class="block text-sm font-medium text-gray-700">Places disponibles (0 = illimité)</label>
<label for="max_subscribers" class="block text-sm font-medium text-gray-700">Places
disponibles (0 = illimité)</label>
<input type="number" name="max_subscribers" id="max_subscribers"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.max_subscribers" @blur="validateField('max_subscribers')"
@@ -199,8 +228,54 @@
</div>
</div>
</div>
<!-- <div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-8">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Ajouter un document</h3>
<p class="mt-1 text-sm text-gray-500">Téléchargez un document lié à cet événement</p>
</div>
<div class="md:grid md:grid-cols-6 p-2">
<div class="sm:col-span-2">
<label for="file_type" class="block text-sm font-medium text-gray-700">Type</label>
<select id="file_type" name="file_type" class="mt-1 block w-full rounded-l-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
x-model="fields.file_type" @blur="validateField('file_type')"
:class="formValidation.fields.file_type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option value="" selected>Sélectionner un type</option>
{{range .ViewState.events_file_types}}
<option value="{{.}}">{{index $.ViewState.file_types_map .}}</option>
{{end}}
</select>
</div>
<div class="sm:col-span-4">
<label for="file_name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="file_name" id="file_name"
placeholder="Nom du fichier"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.file_name" @blur="validateField('file_name')"
:class="formValidation.fields.file_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'" />
</div>
<div class="sm:col-span-6 mt-4">
<label for="cover-photo" class="block text-sm font-medium text-gray-700">Téléchargement</label>
<div class="mt-1 flex justify-center rounded-md border-2 border-dashed px-6 pt-5 pb-6"
x-on:drop="console.log('toto')"
:class="formValidation.fields.file.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<div class="space-y-1 text-center">
{{.IconSet.Icon "hero:outline/folder-plus" "mx-auto h-12 w-12 text-gray-400"}}
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer rounded-md bg-white font-medium text-co-blue focus-within:outline-none focus-within:ring-2 focus-within:ring-co-blue focus-within:ring-offset-2 hover:text-co-blue">
<span>Sélectionnez un fichier </span>
<input id="file-upload" name="file-upload" type="file" class="sr-only"
x-model="fields.file" @blur="validateField('file')">
</label>
</div>
<p class="text-xs text-gray-500">Jusqu'à 10MB</p>
<p class="text-co-blue p-2" x-text="fields.file" x-if="fields.file"></p>
</div>
</div>
</div>
</div>
</div>
</div> -->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
@@ -214,4 +289,4 @@
</form>
</div>
{{end}}
{{end}}

View File

@@ -78,13 +78,14 @@
{{if .ViewState.event.Description}}
<div class="sm:col-span-2">
<dt class="text-sm font-medium text-gray-500">Description</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.event.Description}}</dd>
<dd class="mt-1 text-sm text-gray-900">{{ unescapeHTML .ViewState.event.Description}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
</section>
{{ if eq (index .ViewState.event.Owners 0) .Group.ID }}
<section aria-labelledby="subscribers-table"></section>
<div class="bg-white shadow sm:rounded-lg">
@@ -205,5 +206,17 @@
</div>
</section>
</div>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
<div class="space-y-6 lg:col-start-1 lg:col-span-2">
<section aria-labelledby="event-documents">
<div class="bg-white mt-4 px-4 py-5 shadow sm:rounded-lg sm:px-6">
<h2 id="documents-title" class="text-lg font-medium text-gray-900">Documents</h2>
<div class="flex justify-between items-center">
<div>{{template "event_files" .}}</div>
</div>
</div>
</section>
</div>
</div>
</main>
{{ end }}
{{ end }}

View File

@@ -29,7 +29,7 @@
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<div class="overflow-hidden shadow md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
@@ -116,4 +116,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -44,6 +44,16 @@
}
},
isFormValid: true,
init() {
let quill = new Quill(this.$refs.quill, { theme: 'snow' })
quill.root.innerHTML = this.fields.description
quill.on('text-change', () => {
this.fields.description = quill.root.innerHTML
})
},
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
@@ -99,10 +109,12 @@
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
<div class="mt-1">
<textarea rows="4" name="description" id="descrpition" value="{{.ViewState.event.Description}}"
<!--<textarea rows="4" name="description" id="descrpition" value="{{.ViewState.event.Description}}"
x-model="fields.description" @blur="validateField('description')"
:class="formValidation.fields.description.valid == false ? 'border-co-red border-2' : 'border-gray-300'"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>-->
<input type="hidden" name="description" x-model="fields.description" />
<div x-ref="quill" class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-b-2xl">></div>
</div>
</div>
@@ -252,4 +264,4 @@
</div>
</div>
{{end}}
{{end}}
{{end}}

View File

@@ -5,7 +5,7 @@
<link rel="stylesheet" href="/public/css/main.css" />
<!-- <script defer type="text/javascript" src="/public/js/main.js" defer></script> -->
<script src="https://cdn.jsdelivr.net/npm/@kingshott/iodine@8.1.0/dist/iodine.min.umd.js" defer></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</head>
<body class="h-full">
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8 h-">
@@ -37,4 +37,4 @@
</div>
</body>
</html>
{{end}}
{{end}}

View File

@@ -0,0 +1,58 @@
{{define "beneficiary_diags"}}
{{ $calendarIcon := .IconSet.Icon "hero:outline/calendar" "h-6 w-6" }}
{{ $carIcon := .IconSet.Icon "tabler-icons:car" "h-6 w-6"}}
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<div class="flex items-center space-x-5">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Diagnostics réalisés</h2>
<a href="{{.ViewState.beneficiary.ID}}/create-diag">
<button type="button"
class="flex justify-around rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Créer un diagnostic
</button>
</a>
</div>
</div>
<div class="border-t border-gray-200">
{{ $diagCount := len .ViewState.diags }}
<ul role="list" class="divide-y divide-gray-200 flex-1">
{{if eq $diagCount 0}}
<li class="py-2 px-4">
<p class="py-5 mt-1 max-w-2xl text-sm text-gray-500">Aucun diagnostique effectué pour le moment.</p>
</li>
{{else}}
{{range .ViewState.diags}}
{{ $diags := .ID }}
{{if eq .Deleted false}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<a href="/app/diags/{{$diags}}"class="mt-1 text-sm text-gray-900">{{.Name}}</a>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
<a href="/app/diags/{{$diags}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le diagnostic</span></button>
</a>
</li>
{{end}}
{{if eq .Deleted true}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{.Name}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">Ce diagnostic a été retiré</p>
</div>
</li>
{{end}}
{{end}}
{{end}}
</ul>
</div>
</div>
{{end}}

View File

@@ -120,7 +120,9 @@ x-data="{
</div>
<p class="text-xs text-gray-500">Jusqu'à 10MB</p>
<p class="text-co-blue p-2" x-text="fields.file" x-if="fields.file"></p>
<template x-if="fields.file">
<p class="text-co-blue p-2" x-text="fields.file"></p>
</template>
</div>
</div>
</div>

View File

@@ -0,0 +1,7 @@
{{define "beneficiary_solidarity_transport"}}
<div class="px-4 py-6 sm:px-6 text-center">
<p class="text-center text-lg">Trajets réalisés : {{ .ViewState.solidarity_transport_stats.count }}</p>
<p class="text-center text-lg">Kilomètres réalisés : {{ .ViewState.solidarity_transport_stats.km}} km</p>
</div>
{{end}}

View File

@@ -0,0 +1,46 @@
{{define "beneficiary_wallet"}}
<div class="px-4 py-6 sm:px-6 text-center"
x-data="{
walletdialog: false
}">
<div class="px-4 py-5 sm:px-6">
<p class="text-center text-lg">Solde : {{if .ViewState.beneficiary.Data.wallet}}{{ .ViewState.beneficiary.Data.wallet }}{{else}}0{{end}} €</p>
<button @click="walletdialog = !walletdialog"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 mt-8 w-full text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Créditer le compte
</button>
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true"
x-show="walletdialog">
<div class="fixed inset-0 bg-gray-900 opacity-30 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Créditer le compte</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Créditer le compte mobilité de l'utilisateur</p>
</div>
</div>
</div>
<form method="POST" action="/app/wallets/{{.ViewState.beneficiary.ID}}/credit" class="my-4">
<div class="my-8">
<input type="number" id="amount" name="amount" value="0"
class="w-full shadow-sm focus:ring-co-blue focus:border-co-blue px-2 p-1 sm:text-sm border-gray-300 rounded-2xl">
</div>
<div class="mt-5 sm:mt-6">
<button type="submit" class="inline-flex w-full justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:text-sm">Ajouter</button>
</div>
<div class="mt-5 sm:mt-6">
<button @click="walletdialog=false" type="button" class="inline-flex w-full justify-center max-w-xs bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,110 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Ajouter un diagnostic</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
name: null,
namespace: 'parcoursmob_beneficiaries',
},
rules: {
name: ['required'],
namespace: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations obligatoires
pour créer un diagnostic dans PARCOURSMOB</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name" autocomplete="name"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
</div>
</div>
<input type="hidden" name="namespace" value="parcoursmob_beneficiaries" />
<!-- <div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma JSON</h3>
<p class="mt-1 text-sm text-gray-500">Schéma JSON pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="json_schema" class="block text-sm font-medium text-gray-700">Schéma JSON</label>
<textarea name="json_schema" id="json_schema" autocomplete="json_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.json_schema" @blur="validateField('json_schema')"
:class="formValidation.fields.json_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma UI</h3>
<p class="mt-1 text-sm text-gray-500">Schéma UI pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="ui_schema" class="block text-sm font-medium text-gray-700">Schéma UI</label>
<textarea name="ui_schema" id="ui_schema" autocomplete="ui_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.ui_schema" @blur="validateField('ui_schema')"
:class="formValidation.fields.ui_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div> -->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/beneficiaries/{{.ViewState.beneficiary}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Créer le diagnostique</button>
</div>
</form>
</div>
{{end}}

View File

@@ -14,6 +14,8 @@
birthdate: null,
file_number: null
},
other_properties: {},
other_properties_serialized: null,
rules: {
first_name: ['required'],
last_name: ['required'],
@@ -41,6 +43,7 @@
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.other_properties_serialized = JSON.stringify(this.other_properties)
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
@@ -50,6 +53,7 @@
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<input type="hidden" name="other_properties" x-model="other_properties_serialized" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
@@ -101,7 +105,7 @@
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="file_number" class="block text-sm font-medium text-gray-700">Numéro de dossier (CAF / Pole Emploi ...)</label>
<label for="file_number" class="block text-sm font-medium text-gray-700">Numéro de dossier</label>
<input type="text" name="file_number" id="file_number" placeholder=""
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.file_number" @blur="validateField('file_number')"
@@ -133,6 +137,62 @@
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="situation" class="block text-sm font-medium text-gray-700">Situation sociale</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="situation" name="situation" autocomplete="situation" x-model="other_properties.situation"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="">Inconnu</option>
<option value="BRSA">BRSA</option>
<option value="Demandeur d'emploi">Demandeur d'emploi</option>
<option value="Chantier">Chantier d'insertion</option>
<option value="Jeune -25">Jeune -25</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="situation" class="block text-sm font-medium text-gray-700">Motif d'inscription</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="situation" name="situation" autocomplete="situation" x-model="other_properties.subscription_reason"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="Autre">Inconnu</option>
<option value="Pas de permis">Pas de permis</option>
<option value="Pas de véhicule">Pas de véhicule</option>
<option value="Perte d'autonomie">Perte d'autonomie</option>
<option value="Autre">Autre</option>
</select>
</div>
</div>
{{if moduleAvailable "solidarity_transport"}}
<div class="col-span-6 sm:col-span-3">
<label for="status" class="block text-sm font-medium text-gray-700">Statut (prioritaire / non prioritaire)</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="status" name="status" autocomplete="status" x-model="other_properties.status"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="Non prioritaire">Non prioritaire</option>
<option value="Prioritaire">Prioritaire</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_subscription_date" class="block text-sm font-medium text-gray-700">Date de dernière adhésion</label>
<input type="date" name="last_subscription_date" id="last_subscription_date" autocomplete="last_subscription_date" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.last_subscription_date">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="previous_solidarity_transport" class="block text-sm font-medium text-gray-700">Nombre de transports solidaires précédents</label>
<input type="number" name="previous solidarity_transport" id="previous_solidarity_transport" placeholder="0"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.previous_solidarity_transport">
</div>
{{end}}
<div class="col-span-6 sm:col-span-3">
<label for="comment" class="block text-sm font-medium text-gray-700">Commentaire</label>
<textarea name="comment" id="comment"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.comment"></textarea>
</div>
<!-- <div class="col-span-3 sm:col-span-3">
<label class="block text-sm font-medium text-gray-700"> Photo </label>
@@ -180,4 +240,4 @@
</div>
</form>
</div>
{{end}}
{{end}}

View File

@@ -12,16 +12,18 @@
<div>
<h1 class="text-2xl font-bold text-gray-900">{{.ViewState.beneficiary.Data.first_name}}
{{.ViewState.beneficiary.Data.last_name}}</h1>
<p class="text-sm font-bold text-co-red">{{if .ViewState.beneficiary.Data.archived}}Bénéficiaire archivé{{end}}
<p class="text-sm font-medium text-gray-500">{{if .ViewState.beneficiary.Metadata.created}}Ajouté le <time
datetime="2022-07-25">{{.ViewState.beneficiary.Metadata.created}}</time> par
<a href="#" class="text-gray-900">Conseiller 1</a>{{end}}
datetime="2022-07-25">{{.ViewState.beneficiary.Metadata.created}}</time>{{end}}
</p>
</div>
</div>
<div
class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
<!-- <button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Supprimer</button> -->
{{if not .ViewState.beneficiary.Data.archived}}<a href="/app/beneficiaries/{{ .ViewState.beneficiary.ID }}/archive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Archiver</button></a>{{end}}
{{if .ViewState.beneficiary.Data.archived}}<a href="/app/beneficiaries/{{ .ViewState.beneficiary.ID }}/unarchive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Désarchiver</button></a>{{end}}
<a href="/app/beneficiaries/{{.ViewState.beneficiary.ID}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a>
</div>
@@ -38,6 +40,16 @@
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Profil validé</dt>
<dd class="mt-1 text-sm text-gray-900">
{{if beneficiaryValidatedProfile .ViewState.beneficiary .ViewState.documents}}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</dd>
</div>
{{if .ViewState.beneficiary.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
@@ -71,10 +83,40 @@
{{end}}
{{if .ViewState.beneficiary.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dt class="text-sm font-medium text-gray-500">Numéro de dossier</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.file_number}}</dd>
</div>
{{end}}
{{if and .ViewState.beneficiary.Data.other_properties .ViewState.beneficiary.Data.other_properties.status}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Statut</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.other_properties.status}}</dd>
</div>
{{end}}
{{if and .ViewState.beneficiary.Data.other_properties .ViewState.beneficiary.Data.other_properties.situation}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Situation sociale</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.other_properties.situation}}</dd>
</div>
{{end}}
{{if and .ViewState.beneficiary.Data.other_properties .ViewState.beneficiary.Data.other_properties.subscription_reason}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Motif d'inscription</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.other_properties.subscription_reason}}</dd>
</div>
{{end}}
{{if and .ViewState.beneficiary.Data.other_properties .ViewState.beneficiary.Data.other_properties.last_subscription_date}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Dernière adhésion</dt>
<dd class="mt-1 text-sm text-gray-900">{{ (timeFrom .ViewState.beneficiary.Data.other_properties.last_subscription_date).Format "02/01/2006" }}</dd>
</div>
{{end}}
{{if and .ViewState.beneficiary.Data.other_properties .ViewState.beneficiary.Data.other_properties.comment}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Commentaire</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.other_properties.comment}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
@@ -133,10 +175,28 @@
:class="tab == 'documents' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Documents </a>
{{if moduleAvailable "solidarity_transport"}}
<a href="#" @click="tab = 'wallet'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'wallet' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Compte mobilité </a>
<a href="#" @click="tab = 'solidarity_transport'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'solidarity_transport' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Transport solidaire </a>
{{end}}
<a href="#" @click="tab = 'organizations'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'organizations' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Gestionnaires </a>
<!--<a href="#" @click="tab = 'diags'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'diags' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Diagnostics </a>-->
</nav>
</div>
</div>
@@ -144,7 +204,10 @@
<div x-show="tab == 'documents'">{{template "beneficiary_files" .}}</div>
<div x-show="tab == 'notes'">{{template "beneficiary_notes" .}}</div>
<div x-show="tab == 'wallet'">{{template "beneficiary_wallet" .}}</div>
{{if moduleAvailable "solidarity_transport"}}<div x-show="tab == 'solidarity_transport'">{{template "beneficiary_solidarity_transport" .}}</div>{{end}}
<div x-show="tab == 'organizations'">{{template "beneficiary_organizations" .}}</div>
<!--<div x-show="tab == 'diags'">{{template "beneficiary_diags" .}}</div>-->
</div>
</div>
</section>
@@ -155,4 +218,4 @@
</section>
</div>
</main>
{{end}}
{{end}}

View File

@@ -14,6 +14,13 @@
Exporter
</button>
</a>
<a href="/app/beneficiaries/?archived=true">
<button type="button"
class="inline-flex items-center justify-center bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/archive-box" "h-5 w-5 mr-3"}}
Bénéficiaires archivés
</button>
</a>
<a href="/app/beneficiaries/create">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
@@ -45,7 +52,7 @@
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<div class="overflow-hidden shadow md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
@@ -163,4 +170,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -15,6 +15,8 @@
file_number: '{{ .ViewState.Data.file_number }}',
gender: {{.ViewState.Data.gender}}
},
other_properties: {{if .ViewState.Data.other_properties}}{{ json .ViewState.Data.other_properties }}{{else}}{}{{end}},
other_properties_serialized: null,
rules: {
first_name: ['required'],
last_name: ['required'],
@@ -42,6 +44,7 @@
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.other_properties_serialized = JSON.stringify(this.other_properties)
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
@@ -51,6 +54,7 @@
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<input type="hidden" name="other_properties" x-model="other_properties_serialized" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
@@ -103,7 +107,7 @@
</div>
<div class="col-span-6 sm:col-span-3">
<label for="file_number" class="block text-sm font-medium text-gray-700">Numéro de dossier (CAF / Pole Emploi ...)</label>
<label for="file_number" class="block text-sm font-medium text-gray-700">Numéro de dossier</label>
<input type="text" name="file_number" id="file_number"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.file_number" @blur="validateField('file_number')"
@@ -126,8 +130,8 @@
<div class="col-span-6 sm:col-span-3">
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="gender" name="gender" autocomplete="gender" x-model="gender"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<select id="gender" name="gender" autocomplete="gender" x-model="fields.gender"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-3xs-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="0">Inconnu</option>
<option value="1" :selected="fields.gender == '1'">Masculin</option>
<option value="2" :selected="fields.gender == '2'">Féminin</option>
@@ -135,6 +139,63 @@
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="situation" class="block text-sm font-medium text-gray-700">Situation sociale</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="situation" name="situation" autocomplete="situation" x-model="other_properties.situation"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="">Inconnu</option>
<option value="BRSA">BRSA</option>
<option value="Demandeur d'emploi">Demandeur d'emploi</option>
<option value="Chantier">Chantier</option>
<option value="Jeune -25">Jeune -25</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="situation" class="block text-sm font-medium text-gray-700">Motif d'inscription</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="subscription_reason" name="subscription_reason" autocomplete="subscription_reason" x-model="other_properties.subscription_reason"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="Autre">Inconnu</option>
<option value="Pas de permis">Pas de permis</option>
<option value="Pas de véhicule">Pas de véhicule</option>
<option value="Perte d'autonomie">Perte d'autonomie</option>
<option value="Autre">Autre</option>
</select>
</div>
</div>
{{if moduleAvailable "solidarity_transport"}}
<div class="col-span-6 sm:col-span-3">
<label for="status" class="block text-sm font-medium text-gray-700">Statut (prioritaire / non prioritaire)</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="status" name="status" autocomplete="status" x-model="other_properties.status"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="Non prioritaire">Non prioritaire</option>
<option value="Prioritaire">Prioritaire</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_subscription_date" class="block text-sm font-medium text-gray-700">Date de dernière adhésion</label>
<input type="date" name="last_subscription_date" id="last_subscription_date" autocomplete="last_subscription_date" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.last_subscription_date">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="previous_solidarity_transport" class="block text-sm font-medium text-gray-700">Nombre de transports solidaires précédents</label>
<input type="text" name="previous solidarity_transport" id="previous_solidarity_transport" placeholder="0"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.previous_solidarity_transport">
</div>
{{end}}
<div class="col-span-6 sm:col-span-3">
<label for="comment" class="block text-sm font-medium text-gray-700">Commentaire</label>
<textarea name="comment" id="comment"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.comment"></textarea>
</div>
<!-- <div class="col-span-3 sm:col-span-3">
<label class="block text-sm font-medium text-gray-700"> Photo </label>
@@ -186,4 +247,4 @@
</div>
</form>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,32 @@
{{define "bookings_widget"}}
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="-ml-4 -mt-2 px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Prochaines réservations</h3>
</div>
<!-- <div class="ml-4 mt-2 flex-shrink-0">
<button type="button" class="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Voir</button>
</div> -->
</div>
<ul role="list" class="divide-y divide-gray-200 flex-1">
{{range .}}
{{if or (eq .Data.Deleted nil) (eq .Data.Deleted false)}}
<li class="py-2 px-4 flex">
<a href="/app/vehicles-management/bookings/{{.ID}}" class="flex w-full">
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Du {{(timeFrom .Startdate).Format "02/01"}} au {{(timeFrom .Enddate).Format "02/01"}}</p>
</div>
</a>
</li>
{{end}}
{{end}}
</ul>
<a href="/app/vehicles-management/">
<button class="w-full p-2 text-center bg-co-blue text-white rounded-b-2xl text-sm">
Reservation des véhicules
</button>
</a>
</div>
{{end}}

View File

@@ -69,9 +69,15 @@
{{template "beneficiaries_widget" .ViewState.beneficiaries}}
{{template "agenda_widget" .ViewState.events}}
{{if moduleAvailable "agenda"}}
{{template "agenda_widget" .ViewState.events}}
{{end}}
{{if moduleAvailable "vehicles_management"}}
{{template "bookings_widget" .ViewState.fleets}}
{{end}}
</div>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,132 @@
{{define "diags_files"}}
<div class="px-4 py-6 sm:px-6"
x-data="{
fields: {
name: null,
type: diagnostic,
file: null,
},
rules: {
name: ['required'],
type: ['required'],
file: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
type: {valid: null},
file: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
{{if eq (len .ViewState.documents) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun document</p>
{{end}}
{{if gt (len .ViewState.documents) 0}}
<div class="-mx-4 mb-10 ring-1 ring-gray-300 sm:-mx-6 md:mx-0 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Type</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Nom du document</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Ajouté le</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
<span class="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody>
{{range .ViewState.documents}}
<tr>
<td class="relative py-4 pl-4 sm:pl-6 pr-3 text-sm">
<div class="font-medium text-gray-900">
<span class="bg-co-blue text-xs text-white rounded-xl p-1 mr-2">{{index $.ViewState.file_types_map .Metadata.Type}}</span>
</div>
</td>
<td class="px-3 py-3.5 text-sm text-gray-900 lg:table-cell">{{.Metadata.Name}}</td>
<td class="px-3 py-3.5 text-sm text-gray-500 lg:table-cell">{{.LastModified.Format "02/01/2006"}}</td>
<td class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right text-sm font-medium">
<a href="/app/diags/{{$.ViewState.diag.ID}}/documents/{{.FileName}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le document</span></button>
</a>
</td>
</tr>
{{end}}
<!-- More plans... -->
</tbody>
</table>
</div>
{{end}}
<h3 class="text-lg">Ajouter un document</h3>
<form method="POST" action="/app/diags/{{.ViewState.diag.ID}}/documents" @submit="submit" enctype="multipart/form-data">
<div class="md:grid md:grid-cols-6 p-2">
<div class="sm:col-span-2">
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
<select id="type" name="type" class="mt-1 block w-full rounded-l-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
x-model="fields.type" @blur="validateField('type')"
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option value="" selected>Sélectionner un type</option>
{{range .ViewState.events_file_types}}
<option value="{{.}}">{{index $.ViewState.file_types_map .}}</option>
{{end}}
</select>
</div>
<div class="sm:col-span-4">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name"
placeholder="Nom du fichier"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'" />
</div>
<div class="sm:col-span-6 mt-4">
<label for="cover-photo" class="block text-sm font-medium text-gray-700">Téléchargement</label>
<div class="mt-1 flex justify-center rounded-md border-2 border-dashed px-6 pt-5 pb-6"
x-on:drop="console.log('toto')"
:class="formValidation.fields.file.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<div class="space-y-1 text-center">
{{.IconSet.Icon "hero:outline/folder-plus" "mx-auto h-12 w-12 text-gray-400"}}
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer rounded-md bg-white font-medium text-co-blue focus-within:outline-none focus-within:ring-2 focus-within:ring-co-blue focus-within:ring-offset-2 hover:text-co-blue">
<span>Sélectionnez un fichier </span>
<input id="file-upload" name="file-upload" type="file" class="sr-only"
x-model="fields.file" @blur="validateField('file')">
</label>
<!-- <p class="pl-1">ou glissez-déposez</p> -->
</div>
<p class="text-xs text-gray-500">Jusqu'à 10MB</p>
<p class="text-co-blue p-2" x-text="fields.file" x-if="fields.file"></p>
</div>
</div>
</div>
</div>
<button type="submit"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 mt-8 w-full text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Ajouter le document
</button>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,32 @@
{{define "content"}}
<div>
<form method="POST" >
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full max-w-lg sm:p-6">
<div>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-co bg-co-blue text-white">
{{.IconSet.Icon "hero:outline/information-circle" "h-6 w-6"}}
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Confirmation de retrait</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Voulez-vous vraiment retirer ce dignostique ?</p>
</div>
</div>
</div>
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
<a href="/app/diags/{{.ViewState.diag.ID}}" class="mt-3 inline-flex w-full justify-center rounded-l-2xl border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-1 sm:mt-0 sm:text-sm">Annuler</a>
<button type="submit" class="inline-flex w-full justify-center rounded-r-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-2 sm:text-sm">Confirmation</button>
</div>
</div>
</div>
</div>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,80 @@
{{ define "content" }}
<main class="py-10">
<div class="max-w-3xl mx-auto px-4 sm:px-6 md:flex md:items-center md:justify-between md:space-x-5 lg:max-w-7xl lg:px-8">
<div class="flex items-center space-x-5">
<div>
<h1 class="text-2xl font-bold text-gray-900">{{.ViewState.diag.Name}}</h1>
</div>
{{$diag := .ViewState.diag.ID}}
</div>
<div
class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
{{if eq .ViewState.diag.Deleted false}}
<a href="/app/diags/{{$diag}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a>
<a href="/app/diags/{{$diag}}/delete" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-red hover:bg-co-red focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Retirer</button></a>
{{end}}
</div>
</div>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
<div class="space-y-6 lg:col-start-1 lg:col-span-2">
<section aria-labelledby="diag-information-title">
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h2 id="diag" class="text-lg leading-6 font-medium text-gray-900">Informations</h2>
<p class="mt-1 max-w-2xl text-sm text-gray-500">Informations sur le diagnostic.</p>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date du diagnostic</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .ViewState.diag.Diagdate).Format "02/01/2006"}}</dd>
</div>
{{if eq .ViewState.diag.Namespace "parcoursmob_beneficiaries"}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Type</dt>
<dd class="mt-1 text-sm text-gray-900">Diagnostic personnelle</dd>
</div>
{{end}}
{{if eq .ViewState.diag.Namespace "parcoursmob_vehicles"}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Type</dt>
<dd class="mt-1 text-sm text-gray-900">Diagnostic automobile</dd>
</div>
{{end}}
{{if eq .ViewState.diag.Namespace "parcoursmob_bookings"}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Type</dt>
<dd class="mt-1 text-sm text-gray-900">Diagnostic véhicule réservé</dd>
</div>
{{end}}
<!-- {{if .ViewState.diag.Json_schema}}
<div class="sm:col-span-2">
<dt class="text-sm font-medium text-gray-500">JSON_schema</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.diag.Json_schema}}</dd>
</div>
{{end}}
{{if .ViewState.diag.Ui_schema}}
<div class="sm:col-span-2">
<dt class="text-sm font-medium text-gray-500">UI_schema</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.diag.Ui_schema}}</dd>
</div>
{{end}} -->
</dl>
</div>
</div>
</section>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
<div class="space-y-6 lg:col-start-1 lg:col-span-2">
<section aria-labelledby="event-documents">
<div class="bg-white mt-4 px-4 py-5 shadow sm:rounded-lg sm:px-6">
<h2 id="documents-title" class="text-lg font-medium text-gray-900">Documents</h2>
<div class="flex justify-between items-center">
<div>{{template "diags_files" .}}</div>
</div>
</div>
</section>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,73 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Diagnostics retirés</h1>
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<a href="/app/diags/">
<button type="button"
class="inline-flex items-center justify-center mr-3 bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Retour
</button>
</a>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Nom du diagnostic
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Type
</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
<span class="sr-only">Actions</span>
</th>
</tr>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.diags}}
{{if eq .Deleted true}}
<a href="/app/diags/{{.ID}}">
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<div class="font-medium text-gray-900">{{.Name}}</div>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if eq .Namespace "parcoursmob_beneficiaries"}}
<div class="text-gray-500">Diagnostic personnelle</div>
{{end}}
{{if eq .Namespace "parcoursmob_vehicles"}}
<div class="text-gray-500">Diagnostic automobile</div>
{{end}}
{{if eq .Namespace "parcoursmob_bookings"}}
<div class="text-gray-500">Diagnostic véhicule réservé</div>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a href="/app/diags/{{.ID}}" class="text-co-blue hover:text-co-blue">Voir</a>
</td>
</tr>
</a>
{{end}}
{{end}}
</tbody>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,84 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Diagnostics effectués</h1>
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<!-- <div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<a href="/app/diags/history">
<button type="button"
class="inline-flex items-center justify-center mr-3 bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/calendar" "h-5 w-5 mr-3"}}
Historique
</button>
</a>
</div> -->
</div>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Nom du diagnostic
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Type
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Date
</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
<span class="sr-only">Actions</span>
</th>
</tr>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.diags}}
<a href="/app/diags/{{.ID}}">
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<div class="font-medium text-gray-900">{{.Name}}</div>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if eq .Namespace "parcoursmob_beneficiaries"}}
<div class="text-gray-500">Diagnostic personnelle</div>
{{end}}
{{if eq .Namespace "parcoursmob_vehicles"}}
<div class="text-gray-500">Diagnostic automobile</div>
{{end}}
{{if eq .Namespace "parcoursmob_bookings"}}
<div class="text-gray-500">Diagnostic véhicule réservé</div>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<div class="font-medium text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</div>
</td>
<td class="whitespace nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if eq .Deleted true}}
<div class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-red hover:bg-co-red focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Diagnostic retiré</div>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a href="/app/diags/{{.ID}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le diagnostic</span></button>
</a>
</td>
</tr>
</a>
{{end}}
</tbody>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,117 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Éditer un diagnostique</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
name: '{{.ViewState.diag.Name}}',
namespace: '{{.ViewState.diag.Namespace}}',
json_schema: '{{.ViewState.diag.Json_schema}}',
ui_schema: '{{.ViewState.diag.Ui_schema}}',
},
rules: {
name: ['required'],
namespace: ['required'],
json_schema: ['required'],
ui_schema: ['required']
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
json_schema: {valid: null},
ui_schema: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations obligatoires
pour éditer un diagnostique dans PARCOURSMOB</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name" autocomplete="given-name"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'"
value="{{.ViewState.diag.Name}}">
</div>
</div>
</div>
</div>
</div>
<input type="hidden" name="namespace" value="{{.ViewState.diag.Namespace}}" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma JSON</h3>
<p class="mt-1 text-sm text-gray-500">Schéma JSON pour le diagnostique</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="json_schema" class="block text-sm font-medium text-gray-700">Schéma JSON</label>
<textarea name="json_schema" id="json_schema" autocomplete="json_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.json_schema" @blur="validateField('json_schema')"
:class="formValidation.fields.json_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma UI</h3>
<p class="mt-1 text-sm text-gray-500">Schéma UI pour le diagnostique</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="ui_schema" class="block text-sm font-medium text-gray-700">Schéma UI</label>
<textarea name="ui_schema" id="ui_schema" autocomplete="ui_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.ui_schema" @blur="validateField('ui_schema')"
:class="formValidation.fields.ui_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/diags/{{.ViewState.diag.ID}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Éditer le diagnostique</button>
</div>
</form>
</div>
{{end}}

View File

@@ -1,97 +1,62 @@
{{define "journeys_all"}}
<div>
{{ if gt (len .ViewState.carpools) 0}}
{{$carpool := (index .ViewState.carpools 0)}}
<div class="px-4 pt-4 flex text-sm text-grey-900 font-bold">
<div class="px-4 pt-4 flex text-sm text-grey-900">
<div class="flex-1">
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}} Covoiturage
{{if $carpool.days.monday}}
entre {{$carpool.outward.monday.mintime}} et {{$carpool.outward.monday.maxtime}}
{{else if $carpool.days.tuesday}}
entre {{$carpool.outward.tuesday.mintime}} et {{$carpool.outward.tuesday.maxtime}}
{{else if $carpool.days.wednesday}}
entre {{$carpool.outward.wednesday.mintime}} et {{$carpool.outward.wednesday.maxtime}}
{{else if $carpool.days.thursday}}
entre {{$carpool.outward.thursday.mintime}} et {{$carpool.outward.thursday.maxtime}}
{{else if $carpool.days.friday}}
entre {{$carpool.outward.friday.mintime}} et {{$carpool.outward.friday.maxtime}}
{{else if $carpool.days.saturday}}
entre {{$carpool.outward.saturday.mintime}} et {{$carpool.outward.saturday.maxtime}}
{{else if $carpool.days.sunday}}
entre {{$carpool.outward.sunday.mintime}} et {{$carpool.outward.sunday.maxtime}}
{{end}}
(Temps trajet : {{divideFloat64 $carpool.duration 60.0 | printf "%.0f"}} Minutes)
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
<span class=" font-bold">{{len .ViewState.carpools}} trajets disponibles</span> sur les applications de covoiturage.
</div>
<div>
<span class="ml-2 rounded-xl px-2 py-1 bg-co-blue flex items-center justify-center ring-8 ring-white text-sm text-white whitespace-nowrap">
RIDYGO
</span>
</div>
</div>
<div class="flex items-center justify-center text-sm my-4">
<span class="ml-2 mt-1">
{{$carpool.from.address}}, {{$carpool.from.city}}
</span>
{{$.IconSet.Icon "hero:outline/chevron-right" "h-3 w-3 stroke-gray-800 m-2"}}
<span class="ml-2 mt-1">
{{$carpool.to.address}}, {{$carpool.to.city}}
</span>
</div>
<div class="p-4 pb-8 flex items-center justify-center">
<span class="text-xs text-md">Conducteur : </span>
<span class="ml-2 mt-1 h-5 rounded-xl bg-gray-200 flex items-center justify-center ring-8 ring-white text-black p-2 text-sm">
{{$carpool.driver.alias}}
</span>
<div></div>
</div>
<div class="p-4 text-center">
<button class="rounded-xl text-md px-4 py-1 bg-gray-200 text-co-blue" @click="tab = 'carpool'">{{ len .ViewState.carpools}} solutions en covoiturage : les voir toutes</button>
<button class="rounded-xl text-md px-4 py-1 bg-gray-200 text-co-blue" @click="tab = 'carpool'">Voir les trajets disponibles</button>
</div>
{{end}}
{{ if gt (len .ViewState.journeys.Journeys) 0}}
<div class="px-4 pt-4 flex text-sm text-grey-900 font-bold border-t-2">
{{ if and .ViewState.journeys (gt (len .ViewState.journeys) 0)}}
{{$itinerary := index .ViewState.journeys 0}}
<div class="px-4 pt-4 flex text-sm text-grey-900 font-bold">
<div class="flex-1">
{{.IconSet.Icon "tabler-icons:bus" "h-6 w-6 inline-flex mr-4"}}
{{(timeFrom (index .ViewState.journeys.Journeys 0).Departure).Format "15:04"}} - {{(timeFrom (index .ViewState.journeys.Journeys 0).Arrival).Format "15:04"}}
({{(index .ViewState.journeys.Journeys 0).Duration.Minutes | printf "%.0f"}} Minutes)
{{$itinerary.StartTime.Format "15:04"}} - {{$itinerary.EndTime.Format "15:04"}}
({{divideInt $itinerary.Duration 60}} Minutes)
</div>
<div></div>
</div>
<div class="p-4 pb-8 flex">
{{$firstwalk := true}}
{{range (index .ViewState.journeys.Journeys 0).Sections}}
{{if eq .Type "street_network"}}
<span class="ml-2 mt-1 h-5 w-5 rounded-co bg-gray-200 flex items-center justify-center ring-8 ring-white text-white">
{{$.IconSet.Icon "tabler-icons:walk" "h-4 w-4 stroke-gray-800"}}
{{range $itinerary.Legs }}
{{if or (or (or (eq .Mode "BUS") (eq .Mode "REGIONAL_FAST_RAIL")) (eq .Mode "REGIONAL_RAIL") (eq .Mode "COACH")) }}
<!--<span class="ml-2 px-2 py-1 text-sm text-gray-500 whitespace-nowrap">
{{.AgencyName}}
</span>-->
<span class="ml-2 rounded-xl px-2 py-1 flex items-center justify-center ring-8 ring-white text-sm whitespace-nowrap" style="background-color: #{{.RouteColor}}; color: #{{.RouteTextColor}}">
{{.RouteShortName}}
</span>
{{if $firstwalk}}
{{$firstwalk = false}}
{{$.IconSet.Icon "hero:outline/chevron-right" "h-3 w-3 stroke-gray-800 m-2"}}
{{end}}
{{end}}
{{if eq .Type "public_transport"}}
<span class="ml-2 rounded-xl px-2 py-1 bg-co-blue flex items-center justify-center ring-8 ring-white text-sm text-white whitespace-nowrap">
{{if eq .Display.Network "Antibes - Envibus"}}Envibus{{else}}{{.Display.Network}}{{end}} Ligne {{.Display.Label}}
</span>
{{$.IconSet.Icon "hero:outline/chevron-right" "h-3 w-3 stroke-gray-800 m-2"}}
{{end}}
{{end}}
<div class="flex-1"></div>
<button class="text-sm px-2 py-1 bg-gray-200 text-co-blue rounded-xl" @click="tab = 'public-transit'">Voir le détail</button>
</div>
<div class="p-4 text-center">
<button class="rounded-xl text-md px-4 py-1 bg-gray-200 text-co-blue" @click="tab = 'public-transit'">{{ len .ViewState.journeys.Journeys}} solutions en transports en commun : les voir toutes</button>
<button class="rounded-xl text-md px-4 py-1 bg-gray-200 text-co-blue" @click="tab = 'public-transit'">{{ len .ViewState.journeys}} solutions en transports en commun : les voir toutes</button>
</div>
{{end}}
{{if moduleAvailable "organized_carpool"}}
{{template "journeys_organized_carpool" .}}
{{end}}
{{if moduleAvailable "solidarity_transport"}}
{{template "journeys_solidarity_transport" .}}
{{end}}
{{if moduleAvailable "vehicles"}}
<!--VEHICLES-->
<div class="px-4 pt-16 flex text-sm text-grey-900 border-t-2">
<div class="px-4 pt-16 flex text-sm text-grey-900">
<div class="flex-1">
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
<span class=" font-bold">{{len .ViewState.vehicles}} véhicules</span> partagés disponibles ce jour là et la semaine suivante
@@ -101,5 +66,6 @@
<div class="p-4 text-center">
<a href="/app/vehicles/"><button class="text-md px-4 py-1 bg-gray-200 text-co-blue rounded-xl">Réserver un véhicule</button></a>
</div>
{{end}}
</div>
{{end}}
{{end}}

View File

@@ -1,10 +1,11 @@
{{define "journeys_carpool"}}
<div>
{{ if eq (len .ViewState.carpools) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun covoiturage disponible pour ce trajet.</p>
{{end}}
{{$first := true}}
{{$i := 0}}
{{range .ViewState.carpools}}
{{if $first}}
{{$first = false}}
@@ -13,44 +14,132 @@
<div class="p-4 border-t-2 pb-8">
{{end}}
<div class="flex text-sm text-grey-900 font-bold">
{{if .days.monday}}
<div class="flex-1">Départ entre {{.outward.monday.mintime}} et {{.outward.monday.maxtime}}</div>
{{else if .days.tuesday}}
<div class="flex-1">Départ entre {{.outward.tuesday.mintime}} et {{.outward.tuesday.maxtime}}</div>
{{else if .days.wednesday}}
<div class="flex-1">Départ entre {{.outward.wednesday.mintime}} et {{.outward.wednesday.maxtime}}</div>
{{else if .days.thursday}}
<div class="flex-1">Départ entre {{.outward.thursday.mintime}} et {{.outward.thursday.maxtime}}</div>
{{else if .days.friday}}
<div class="flex-1">Départ entre {{.outward.friday.mintime}} et {{.outward.friday.maxtime}}</div>
{{else if .days.saturday}}
<div class="flex-1">Départ entre {{.outward.saturday.mintime}} et {{.outward.saturday.maxtime}}</div>
{{else if .days.sunday}}
<div class="flex-1">Départ entre {{.outward.sunday.mintime}} et {{.outward.sunday.maxtime}}</div>
{{end}}
<div>{{divideFloat64 .duration 60.0 | printf "%.0f"}} Minutes</div>
<div class="flex-1">Trajet en covoiturage avec {{.ExtraMembers.ocss.Driver.Alias}}</div>
</div>
<div class="flex items-center justify-center text-sm my-4">
<!--{{if .ExtraMembers.ocss.PassengerPickupDate}}<div class="text-xs">Départ {{(timeFrom .ExtraMembers.ocss.PassengerPickupDate)}}</div>{{end}}-->
<!--<div class="flex items-center justify-center text-sm my-4">
<span class="ml-2 mt-1">
{{.from.address}}, {{.from.city}}
</span>
{{$.IconSet.Icon "hero:outline/chevron-right" "h-3 w-3 stroke-gray-800 m-2"}}
<span class="ml-2 mt-1">
{{.to.address}}, {{.to.city}}
</span>
</div>
</div>-->
<div class="p-4 pb-8 flex items-center justify-center">
<span class="text-xs text-md">Avec </span>
<!--<span class="text-xs text-md">Avec </span>
<span class="ml-2 mt-1 h-5 rounded-xl bg-gray-200 flex items-center justify-center ring-8 ring-white text-black p-2 text-sm">
{{.driver.alias}}
</span>
</span>-->
<span class="text-xs text-md"> sur l'application </span>
<span class="ml-2 rounded-xl px-2 py-1 bg-co-blue flex items-center justify-center ring-8 ring-white text-sm text-white whitespace-nowrap">
RIDYGO
Blablacar Daily
</span>
</div>
<p class=" text-center text-xs">Accéder au trajet :<br /> <a href="{{.ExtraMembers.ocss.WebUrl}}" class="text-co-blue">{{.ExtraMembers.ocss.WebUrl}}</a></p>
<div x-data="{dialog{{$i}}: false}" class="text-center">
<button @click="dialog{{$i}} = !dialog{{$i}}" class="m-4 rounded-xl px-4 py-1 mt-8 bg-gray-200 text-co-blue text-sm">Envoyer le lien par SMS</button>
<div x-show="dialog{{$i}}" class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<form method="POST" action="/app/sms/send">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full max-w-lg sm:p-6">
<div>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-co bg-co-blue text-white">
{{$.IconSet.Icon "hero:outline/information-circle" "h-6 w-6"}}
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Envoyer un message</h3>
<div class="mt-2">
<p class="text-sm text-gray-500"></p>
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700">Message</label>
<div class="mt-1">
<textarea rows="4" name="message" id="message" class="block w-full rounded-2xl border-gray-300 shadow-sm focus:border-co-blue focus:ring-co-blue sm:text-sm">Un covoiturage est disponible sur {{.ExtraMembers.ocss.WebUrl}}.</textarea>
</div>
</div>
</div>
</div>
<br /><br />
<div x-data="{
text: '',
beneficiariesListOpen: false,
beneficiaries: {{json $.ViewState.beneficiaries}},
filteredBeneficiaries: (beneficiaries, text) => {
if(text=='') {
return beneficiaries
}
return beneficiaries.filter(b => b['data']['first_name'].toLowerCase().includes(text.toLowerCase()) || b['data']['last_name'].toLowerCase().includes(text.toLowerCase()))
},
fields: {
beneficiaryid: null,
},
selectbeneficiary(beneficiary) {
this.fields.beneficiaryid = beneficiary.id
this.text = beneficiary.data.first_name + ' ' + beneficiary.data.last_name
this.beneficiariesListOpen = false
},
}">
<input type="hidden" name="beneficiaryid" x-model="fields.beneficiaryid">
<label for="combobox" class="block text-sm font-medium text-gray-700">Selectionner un bénéficiaire</label>
<div class="relative mt-1 mb-4">
<input autocomplete="off" @focus="beneficiariesListOpen = true" x-model="text" id="combobox" type="text" class="w-full rounded-2xl border border-gray-300 bg-white py-2 pl-3 pr-12 shadow-sm focus:border-co-blue focus:outline-none focus:ring-1 focus:ring-co-blue sm:text-sm" role="combobox" aria-controls="options" aria-expanded="false">
<button @click="beneficiariesListOpen = ! beneficiariesListOpen" type="button" class="absolute inset-y-0 right-0 flex items-center rounded-r-2xl px-2 focus:outline-none">
<!-- Heroicon name: solid/selector -->
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<ul x-show="beneficiariesListOpen" class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-xl bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" id="options" role="listbox">
<!--
Combobox option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
Active: "text-white bg-indigo-600", Not Active: "text-gray-900"
-->
<template x-for="beneficiary in filteredBeneficiaries(beneficiaries, text)">
<li @click="selectbeneficiary(beneficiary)" class="relative cursor-default hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900" id="option-0" role="option" tabindex="-1">
<!-- Selected: "font-semibold" -->
<span class="truncate" x-text="beneficiary.data.first_name"></span> <span class="truncate" x-text="beneficiary.data.last_name"></span>
<!--
Checkmark, only display for selected option.
Active: "text-white", Not Active: "text-indigo-600"
-->
<span class="absolute inset-y-0 right-0 flex items-center pr-4 text-co-blue">
<!-- Heroicon name: solid/check -->
<!-- <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg> -->
</span>
</li>
</template>
<!-- More items... -->
</ul>
</div>
</div>
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
<button @click="dialog{{$i}} = !dialog{{$i}}" type="button" class="mt-3 inline-flex w-full justify-center rounded-l-2xl border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-1 sm:mt-0 sm:text-sm">Annuler</button>
<button type="submit" class="inline-flex w-full justify-center rounded-r-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-2 sm:text-sm">Envoyer</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{{end}}
{{end}}
</div>
{{end}}

View File

@@ -0,0 +1,54 @@
{{define "journeys_organized_carpool"}}
{{if .ViewState.organized_carpools}}
{{ if eq (len .ViewState.organized_carpools) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun covoitureur solidaire disponible pour ce trajet.</p>
{{else}}
<h3 class="p-4 text-lg">Covoitureurs solidaires</h3>
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Covoitureurs disponibles
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Départ conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Arrivée conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Distance du covoiturage
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{ range .ViewState.organized_carpools }}
{{ $driver := (index $.ViewState.solidarity_drivers .Driver.Id)}}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ $driver.Data.first_name }} {{ $driver.Data.last_name }}</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .DriverDepartureAddress }}</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .DriverArrivalAddress }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Distance }} km</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue hover:text-co-blue"
href="/app/organized-carpool/drivers/{{$driver.ID}}/journeys/{{.Id}}?{{unescapeHTML $.ViewState.querystring}}">
Organiser
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}
{{end}}
{{end}}

View File

@@ -1,6 +1,7 @@
{{define "journeys_others"}}
<!--VEHICLES-->
{{if moduleAvailable "vehicles"}}
<div class="p-4 flex text-sm text-grey-900">
<div class="flex-1">
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
@@ -34,5 +35,5 @@
<div class="p-4 text-center">
<a href="/app/vehicles/"><button class="text-md px-4 py-1 bg-gray-200 text-co-blue rounded-xl">Réserver un véhicule</button></a>
</div>
{{end}}
{{end}}
{{end}}

View File

@@ -0,0 +1,108 @@
{{ define "journeys_public_transit" }}
{{ range .ViewState.journeys }}
<div class="p-4 pb-8">
<div class="flex text-md text-grey-900 font-bold">
<div class="flex-1">{{ .StartTime.Format "15:04" }} - {{ .EndTime.Format "15:04" }}</div>
<div>{{ divideInt .Duration 60 }} Minutes</div>
</div>
<div class="flow-root">
<ul role="list" class="-mb-8">
{{$firstwalk := true}}
{{range .Legs}}
{{if eq .Mode "WALK" }}
{{if .Distance}}
<li>
<div class="relative py-4">
{{if $firstwalk}}
{{$firstwalk = false}}
<span class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
{{end}}
<div class="relative flex space-x-3">
<div>
<span class="ml-2 h-4 w-4 rounded-co bg-gray-200 flex items-center justify-center ring-8 ring-white text-white">
{{$.IconSet.Icon "tabler-icons:walk" "h-3 w-3 stroke-gray-800"}}
</span>
</div>
<div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5">
{{if .Distance}}
<div>
<p class="text-xs text-gray-500">Marcher <a href="#" class="font-medium text-gray-900">{{ .Distance }}m</a></p>
</div>
{{ else }}
<div>
<p class="text-xs text-gray-500">Attendre <a href="#" class="font-medium text-gray-900">{{ divideInt .Duration 60 }} minutes</a></p>
</div>
{{ end }}
</div>
</div>
</div>
</li>
{{end}}
{{end}}
{{if or (eq .Mode "BUS") (eq .Mode "COACH")}}
<li>
<div class="relative py-4">
<span class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
<div class="relative flex space-x-3">
<div>
<span class="h-8 w-8 rounded-co bg-co-blue flex items-center justify-center ring-8 ring-white" style="background-color: #{{.RouteColor}}; color: #{{.RouteTextColor}}">
{{$.IconSet.Icon "tabler-icons:bus" "h-5 w-5 stroke-white"}}
</span>
</div>
<div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5">
<div>
<p class="text-md text-gray-500">{{.AgencyName}} <a href="#" class="font-medium text-gray-900">Ligne {{.RouteShortName}}</a></p>
</div>
</div>
</div>
<div class="ml-16 pt-2">
<div>
<p class="text-sm text-gray-500">Départ <a href="#" class="font-medium text-gray-900">{{.StartTime.Format "15:04"}}</a> - Arrivée <a href="#" class="font-medium text-gray-900">{{.EndTime.Format "15:04"}}</a></p>
</div>
<div>
<p class="text-sm text-gray-500">De <a href="#" class="font-medium text-gray-900">{{.From.Name}}</a> à <a href="#" class="font-medium text-gray-900">{{.To.Name}}</a></p>
</div>
<div>
<p class="text-sm text-gray-500">Direction <a href="#" class="font-medium text-gray-900">{{.Headsign}}</a></p>
</div>
</div>
</div>
</li>
{{end}}
{{if or (eq .Mode "REGIONAL_FAST_RAIL") (eq .Mode "REGIONAL_RAIL") }}
<li>
<div class="relative py-4">
<span class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
<div class="relative flex space-x-3">
<div>
<span class="h-8 w-8 rounded-co bg-co-blue flex items-center justify-center ring-8 ring-white" style="background-color: #{{.RouteColor}}; color: #{{.RouteTextColor}}">
{{$.IconSet.Icon "tabler-icons:train" "h-5 w-5 stroke-white"}}
</span>
</div>
<div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5">
<div>
<p class="text-md text-gray-500">{{.AgencyName}} <a href="#" class="font-medium text-gray-900">TER {{.RouteShortName}}</a></p>
</div>
</div>
</div>
<div class="ml-16 pt-2">
<div>
<p class="text-sm text-gray-500">Départ <a href="#" class="font-medium text-gray-900">{{.StartTime.Format "15:04"}}</a> - Arrivée <a href="#" class="font-medium text-gray-900">{{.EndTime.Format "15:04"}}</a></p>
</div>
<div>
<p class="text-sm text-gray-500">De <a href="#" class="font-medium text-gray-900">{{.From.Name}}</a> à <a href="#" class="font-medium text-gray-900">{{.To.Name}}</a></p>
</div>
<div>
<p class="text-sm text-gray-500">Direction <a href="#" class="font-medium text-gray-900">{{.Headsign}}</a></p>
</div>
</div>
</div>
</li>
{{end}}
{{end}}
</ul>
</div>
</div>
{{ end }}
{{ end }}

View File

@@ -1,10 +1,11 @@
{{define "journeys_public_transit"}}
{{ if eq (len .ViewState.journeys.Journeys) 0}}
{{ if or (not .ViewState.journeys) (eq (len .ViewState.journeys.Journeys) 0)}}
<p class="p-12 text-gray-500 text-center text-md">Aucun transport en commun pour ce trajet.</p>
{{end}}
{{$first := true}}
{{if .ViewState.journeys}}
{{range .ViewState.journeys.Journeys}}
{{if $first}}
{{$first = false}}
@@ -78,4 +79,5 @@
</div>
</div>
{{end}}
{{end}}
{{end}}
{{end}}

View File

@@ -0,0 +1,65 @@
{{define "journeys_solidarity_transport"}}
{{ if eq (len .ViewState.driver_journeys) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun conducteur solidaire disponible pour ce trajet.</p>
{{else}}
<h3 class="p-4 text-lg">Transport solidaire</h3>
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Conducteurs disponibles
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Distance conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Distance passager
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Commentaire
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Profil validé
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{ range .ViewState.driver_journeys }}
{{ $driver := (index $.ViewState.solidarity_drivers .DriverId)}}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ $driver.Data.first_name }} {{ $driver.Data.last_name }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .DriverDistance }} km</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .PassengerDistance }} km</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{if $driver.Data.other_properties}}{{ $driver.Data.other_properties.comment }}{{end}}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if solidarityDriverValidatedProfile $driver (solidarityDocuments $driver.ID) }}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue hover:text-co-blue"
href="/app/solidarity-transport/drivers/{{$driver.ID}}/journeys/{{.Id}}">
Organiser
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}
{{end}}

View File

@@ -6,13 +6,13 @@
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<a href="/app/journeys/groups_covoiturage">
<!--<a href="/app/journeys/groups_covoiturage">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Gestion des groupes
</button>
</a>
</a>-->
</div>
</div>
@@ -39,14 +39,14 @@
<label for="departuredate" class="block text-sm font-medium text-gray-700">Le</label>
<div class="mt-1">
<input type="date" id="departuredate" name="departuredate" value="{{.ViewState.departuredate}}"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-l-2xl border-r-1">
class="p-2 shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-l-2xl border-r-1">
</div>
</div>
<div class="lg:col-span-1">
<label for="departuretime" class="block text-sm font-medium text-gray-700">A</label>
<div class="mt-1">
<input type="time" id="departuretime" name="departuretime" value="{{.ViewState.departuretime}}"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-r-2xl border-l-0">
class="p-2 shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-r-2xl border-l-0">
</div>
</div>
@@ -81,7 +81,7 @@
<option value="public-transit">Transports</option>
<!-- <option value="active-modes">Modes actifs</option> -->
<option value="solidarity-transport">Transport solidaire</option> -->
<option value="others">Autres</option>
</select>
@@ -105,11 +105,18 @@
:class="tab == 'public-transit' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Transports </a>
<!-- <a href="#" @click="tab = 'active-modes'"
{{ if moduleAvailable "solidarity_transport"}}
<a href="#" @click="tab = 'solidarity-transport'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'active-modes' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Modes actifs </a> -->
:class="tab == 'solidarity-transport' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Transport solidaire </a>
{{end}}
{{if moduleAvailable "organized_carpool"}}
<a href="#" @click="tab = 'organized-carpool'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'organized-carpool' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Covoiturage solidaire </a>
{{end}}
<a href="#" @click="tab = 'others'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'others' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
@@ -122,6 +129,8 @@
<div x-show="tab == 'all'">{{template "journeys_all" .}}</div>
<div x-show="tab == 'carpool'">{{template "journeys_carpool" .}}</div>
<div x-show="tab == 'public-transit'">{{template "journeys_public_transit" .}}</div>
<div x-show="tab == 'organized-carpool'">{{template "journeys_organized_carpool" .}}</div>
<div x-show="tab == 'solidarity-transport'">{{template "journeys_solidarity_transport" .}}</div>
<div x-show="tab == 'others'">{{template "journeys_others" .}}</div>
</div>
</div>
@@ -131,4 +140,4 @@
</div>
</div>
{{end}}
{{end}}

View File

@@ -5,9 +5,17 @@
<head>
<title>PARCOURSMOB</title>
<link rel="stylesheet" href="/public/css/main.css" />
<!-- <script defer type="text/javascript" src="/public/js/main.js" defer></script> -->
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/maplibre-gl@^5.2.0/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@kingshott/iodine@8.1.0/dist/iodine.min.umd.js" defer></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@^5.2.0/dist/maplibre-gl.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pmtiles@^4.3.0/dist/pmtiles.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@protomaps/basemaps@5/dist/basemaps.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/polyline@0.2.0/src/polyline.js"></script>
<!--<script defer type="text/javascript" src="/public/js/main.js" defer></script>-->
</head>
<body class="h-full" x-data="{ offCanvasMenu: false }">
@@ -177,4 +185,4 @@
</body>
</html>
{{end}}
{{end}}

View File

@@ -54,7 +54,7 @@
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.admins.Data.first_name}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Prénon</dt>
<dt class="text-sm font-medium text-gray-500">Prénom</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.admins.Data.last_name}}
</dd>
</div>

View File

@@ -0,0 +1,82 @@
{{ define "carpool_bookings_list" }}
{{if eq (len .ViewState.bookings) 0}}
<div class="m-10 text-center text-gray-600">Aucun trajet déclaré</div>
{{else}}
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Passager
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Départ
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Destination
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Date
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Statut
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.bookings}}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/organized-carpool/drivers/{{.DriverId}}">
{{ (index $.ViewState.drivers_map .DriverId).Data.first_name }}
{{ (index $.ViewState.drivers_map .DriverId).Data.last_name }}
</a>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/beneficiaries/{{.PassengerId}}">
{{ (index $.ViewState.passengers_map .PassengerId).Data.first_name }}
{{ (index $.ViewState.passengers_map .PassengerId).Data.last_name }}
</a>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerPickup.Properties.label }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerDrop.Properties.label }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerPickupDate.Format "02/01/2006 15:04" }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ if eq .Status "WAITING_CONFIRMATION"}}
<span class="p-1 text-xs bg-gray-300 rounded-xl">Attente confirmation</span>
{{ else if eq .Status "VALIDATED"}}
<span class="p-1 text-xs bg-co-green rounded-xl">Validé</span>
{{ else if eq .Status "CANCELLED"}}
<span class="p-1 text-xs bg-co-red text-white rounded-xl">Annulé</span>
{{ end }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/organized-carpool/bookings/{{.Id}}">
Voir
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}
{{end}}

View File

@@ -0,0 +1,119 @@
{{define "driver_availabilities"}}
<div class="bg-white shadow sm:rounded-lg"
x-data="{
availabilitiesdialog: false
}">
<div class="px-4 py-5 sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Trajets</h2>
</div>
<div class="border-t border-gray-200 px-4">
<div class="">
{{ range .ViewState.trips }}
{{$departure := index .Features 0}}
{{$destination := index .Features 1}}
<div class="my-10">
<div class="flex flex-row my-2">
{{range .ExtraMembers.properties.schedules}}
<span class="p-1 text-xs mr-2 bg-co-blue text-white rounded-lg">
{{ if eq .day "SUN"}}Dimanche
{{ else if eq .day "MON"}}Lundi
{{ else if eq .day "TUE"}}Mardi
{{ else if eq .day "WED"}}Mercredi
{{ else if eq .day "THU"}}Jeudi
{{ else if eq .day "FRI"}}Vendredi
{{ else if eq .day "SAT"}}Samedi
{{ end }}
{{.time_of_day}}
</span>
{{end}}
</div>
<div class="flex flex-row">
<div class="flex-1 text-sm">
{{$departure.Properties.label}}
</div>
<div>
{{$.IconSet.Icon "hero:outline/chevron-right" "h-5 w-5 mr-3"}}
</div>
<div class="flex-1 text-sm text-right">
{{$destination.Properties.label}}
</div>
</div>
<div>
<div class="flex-none text-sm text-right"><a class="text-co-blue" href="/app/organized-carpool/drivers/{{$.ViewState.driver.ID}}/trips/{{.ExtraMembers.id}}/delete">Supprimer</a></div>
</div>
</div>
{{ end }}
</div>
<button type="button" @click="availabilitiesdialog = !availabilitiesdialog"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue my-4 px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Ajouter un trajet
</button>
</div>
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true"
x-show="availabilitiesdialog">
<div class="fixed inset-0 bg-gray-900 opacity-30 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Ajouter un trajet</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Paramétrer un nouveau trajet du conducteur</p>
</div>
</div>
</div>
<form method="POST" action="/app/organized-carpool/drivers/{{.ViewState.driver.ID}}/trips" class="my-4">
<div class="my-8">
{{ $fieldName := "address_departure" }}
{{ template "address_autocomplete" (dict "FieldName" $fieldName "Address" .ViewState.driver.Data.address "FieldLabel" "Départ") }}
</div>
<div class="my-8">
{{ $fieldName := "address_destination" }}
{{ template "address_autocomplete" (dict "FieldName" $fieldName "Address" .ViewState.driver.Data.address_destination "FieldLabel" "Destination") }}
</div>
<div class="my-4">
<input name="days.monday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Lundi &nbsp;&nbsp;
<input name="days.tuesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mardi &nbsp;&nbsp;
<input name="days.wednesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mercredi &nbsp;&nbsp;
<input name="days.thursday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Jeudi &nbsp;&nbsp;
<input name="days.friday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Vendredi &nbsp;&nbsp;
<input name="days.saturday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Samedi &nbsp;&nbsp;
<input name="days.sunday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Dimanche &nbsp;&nbsp;
</div>
<div class="my-4 inline-flex justify-items-center">
<div class="p-1">Aller à :</div>
<input type="time" id="outwardtime" name="outwardtime" value="08:00"
class="shadow-sm focus:ring-co-blue focus:border-co-blue p-1 sm:text-sm border-gray-300 rounded-2xl" />
</div>
<div class="my-4 inline-flex justify-items-center">
<div class="p-1">Retour à :</div>
<input type="time" id="returntime" name="returntime" value="18:00"
class="shadow-sm focus:ring-co-blue focus:border-co-blue p-1 sm:text-sm border-gray-300 rounded-2xl" />
</div>
<div class="mt-5 sm:mt-6">
<button type="submit" class="inline-flex w-full justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:text-sm">Ajouter</button>
</div>
<div class="mt-5 sm:mt-6">
<button @click="availabilitiesdialog=false" type="button" class="inline-flex w-full justify-center max-w-xs bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,80 @@
{{ define "carpool_drivers_list" }}
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="m-4 sm:ml-16 sm:flex-none">
<!--<a href="/api/cache/{{.ViewState.CacheId}}/export">
<button type="button"
class="inline-flex items-center justify-center bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/document-arrow-down" "h-5 w-5 mr-3"}}
Exporter
</button>
</a>-->
<a href="/app/organized-carpool/?archived=true">
<button type="button"
class="inline-flex items-center justify-center bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/archive-box" "h-5 w-5 mr-3"}}
Covoitureurs archivés
</button>
</a>
<a href="/app/organized-carpool/drivers/create">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Ajouter un covoitureur solidaire
</button>
</a>
</div>
</div>
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Nom
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Adresse départ
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Adresse destination
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Téléphone
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Profil validé
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{ range .ViewState.drivers }}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Data.first_name }} {{ .Data.last_name }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{if .Data.address}}{{.Data.address.properties.label}}{{end}}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{if .Data.address_destination}}{{.Data.address_destination.properties.label}}{{end}}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Data.phone_number }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue hover:text-co-blue"
href="/app/organized-carpool/drivers/{{.ID}}">
Voir
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}

View File

@@ -0,0 +1,271 @@
{{define "journey_preview"}}
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2 grid-cols-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Le trajet</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">Informations sur le trajet</h2>
</div>
</div>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div class="p-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Départ passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{(index .journey.Features 0).Properties.MustString "label"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Destination passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{(index .journey.Features 1).Properties.MustString "label"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Départ conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{(index .journey.Features 0).Properties.MustString "label"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Destination conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{(index .journey.Features 1).Properties.MustString "label"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Prix (passager)</dt>
<dd class="mt-1 text-sm text-gray-900">0 EUR</dd>
</div>
</dl>
</div>
<div>
<div id="map" class="w-full h-full"></div>
<script>
let protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles",protocol.tile);
var map = new maplibregl.Map({
container: 'map', // container id
style: "/public/maps/protomaps-light/style.json",
});
const geojsonPoints = {
"type": "FeatureCollection",
"features": [
{{ json (index .journey.Features 0) }},
{{ json (index .journey.Features 1) }},
]
}
map.on('load', async () => {
map.addSource('trip', {
type: "geojson",
data: {{ json (index .journey.Features 2) }}
})
map.addLayer({
'id': 'trip',
'type': 'line',
'source': 'trip',
'layout': {
'line-join': 'round',
'line-cap': 'round',
},
'paint': {
'line-color': '#243887',
'line-width': 3
},
});
map.addSource('points', {
type: "geojson",
data: geojsonPoints
})
map.addLayer({
'id': 'points',
'type': 'circle',
'source': 'points',
'paint': {
'circle-color': '#243887',
'circle-radius': 8
},
});
var bbox=turf.bbox(geojsonPoints)
map.fitBounds(bbox, { padding: 30 })
})
</script>
</div>
</div>
</div>
<div class="py-4 grid grid-cols-1 gap-6 sm:grid-cols-2">
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2 grid-cols-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Conducteur</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">{{.driver.Data.first_name}} {{.driver.Data.last_name}}</h2>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
{{if .driver.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.email}}</dd>
</div>
{{end}}
{{if .driver.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.phone_number}}</dd>
</div>
{{end}}
{{if .driver.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .driver.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .driver.Data.gender (ne .driver.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .driver.Data.gender}}</dd>
</div>
{{end}}
{{if .driver.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .driver.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.file_number}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Passager</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">{{if ne .passenger.ID "" }}{{.passenger.Data.first_name}} {{.passenger.Data.last_name}}{{end}}</h2>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
{{if eq .passenger.ID ""}}
<form method="GET">
<div x-data="{
text: '',
beneficiariesListOpen: false,
beneficiaries: {{json .beneficiaries}},
filteredBeneficiaries: (beneficiaries, text) => {
if(text=='') {
return beneficiaries
}
return beneficiaries.filter(b => b['data']['first_name'].toLowerCase().includes(text.toLowerCase()) || b['data']['last_name'].toLowerCase().includes(text.toLowerCase()))
},
fields: {
beneficiaryid: {{if .search}}'{{.search.beneficiary.ID}}'{{else}}null{{end}},
},
selectbeneficiary(beneficiary) {
this.fields.beneficiaryid = beneficiary.id
this.text = beneficiary.data.first_name + ' ' + beneficiary.data.last_name
this.beneficiariesListOpen = false
},
}">
<input type="hidden" name="passengerid" x-model="fields.beneficiaryid">
<label for="combobox" class="block text-sm font-medium text-gray-700">Selectionner un bénéficiaire</label>
<div class="relative mt-1 mb-4">
<input autocomplete="off" @focus="beneficiariesListOpen = true" x-model="text" id="combobox" type="text" class="w-full rounded-2xl border border-gray-300 bg-white py-2 pl-3 pr-12 shadow-sm focus:border-co-blue focus:outline-none focus:ring-1 focus:ring-co-blue sm:text-sm" role="combobox" aria-controls="options" aria-expanded="false">
<button @click="beneficiariesListOpen = ! beneficiariesListOpen" type="button" class="absolute inset-y-0 right-0 flex items-center rounded-r-2xl px-2 focus:outline-none">
<!-- Heroicon name: solid/selector -->
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<ul x-show="beneficiariesListOpen" class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-xl bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" id="options" role="listbox">
<!--
Combobox option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
Active: "text-white bg-indigo-600", Not Active: "text-gray-900"
-->
<template x-for="beneficiary in filteredBeneficiaries(beneficiaries, text)">
<li @click="selectbeneficiary(beneficiary)" class="relative cursor-default hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900" id="option-0" role="option" tabindex="-1">
<!-- Selected: "font-semibold" -->
<span class="truncate" x-text="beneficiary.data.first_name"></span> <span class="truncate" x-text="beneficiary.data.last_name"></span>
<!--
Checkmark, only display for selected option.
Active: "text-white", Not Active: "text-indigo-600"
-->
<span class="absolute inset-y-0 right-0 flex items-center pr-4 text-co-blue">
<!-- Heroicon name: solid/check -->
<!-- <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg> -->
</span>
</li>
</template>
<!-- More items... -->
</ul>
</div>
</div>
<button type="submit" class="inline-flex w-full justify-center max-w-xs bg-co-blue border-gray-300 border px-4 py-2 text-white items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Valider
</button>
</div>
</form>
{{ else }}
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
{{if .passenger.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.email}}</dd>
</div>
{{end}}
{{if .passenger.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.phone_number}}</dd>
</div>
{{end}}
{{if .passenger.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .passenger.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .passenger.Data.gender (ne .passenger.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .passenger.Data.gender}}</dd>
</div>
{{end}}
{{if .passenger.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .passenger.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.file_number}}</dd>
</div>
{{end}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Compte mobilié (solde)</dt>
<dd class="mt-1 text-sm text-gray-900">{{if .passenger.Data.wallet}}{{ .passenger.Data.wallet }}{{else}}0{{end}} EUR</dd>
</div>
</dl>
</div>
{{end}}
</div>
</div>
{{end}}

View File

@@ -0,0 +1,178 @@
{{ define "content" }}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Créer un covoitureur solidaire</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
first_name: null,
last_name: null,
email: null,
phone_number: null,
birthdate: null,
},
rules: {
first_name: ['required'],
last_name: ['required'],
email: ['required', 'email'],
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
birthdate: ['required'],
},
formValidation: {
valid: false,
fields: {
first_name: {valid: null},
last_name: {valid: null},
email: {valid: null},
phone_number: {valid: null},
birthdate: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations personnelles obligatoires pour créer le conducteur</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="block text-sm font-medium text-gray-700">Prénom</label>
<input type="text" name="first_name" id="first_name" autocomplete="given-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.first_name" @blur="validateField('first_name')"
:class="formValidation.fields.first_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="last_name" id="last_name" autocomplete="family-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.last_name" @blur="validateField('last_name')"
:class="formValidation.fields.last_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="text" name="email" id="email" autocomplete="email"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.email" @blur="validateField('email')"
:class="formValidation.fields.email.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="phone_number" class="block text-sm font-medium text-gray-700">Numéro de
téléphone</label>
<input type="text" name="phone_number" id="phone_number" autocomplete="phone" placeholder="+33612345678"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.phone_number" @blur="validateField('phone_number')"
:class="formValidation.fields.phone_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="birthdate" class="block text-sm font-medium text-gray-700">Date de
naissance</label>
<input type="date" name="birthdate" id="birthdate" autocomplete="birthdate" placeholder="JJ/MM/AAAA"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.birthdate" @blur="validateField('birthdate')"
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
{{ $fieldName := "address" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName "FieldLabel" "Adresse principale" }}
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations optionnelles</h3>
<p class="mt-1 text-sm text-gray-500">Autres informations de profil optionnelles</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
{{ $fieldName2 := "address_destination" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName2 "FieldLabel" "Adresse destination" }}
<div class="col-span-6 sm:col-span-3">
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="gender" name="gender" autocomplete="gender" x-model="gender"
class="p-2 max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="0">Inconnu</option>
<option value="1">Masculin</option>
<option value="2">Féminin</option>
<option value="9">Sans objet</option>
</select>
</div>
</div>
<!-- <div class="col-span-3 sm:col-span-3">
<label class="block text-sm font-medium text-gray-700"> Photo </label>
<div class="mt-1 flex items-center space-x-5">
<span class="inline-block h-12 w-12 rounded-co overflow-hidden bg-gray-100">
{{.IconSet.Icon "img:profile-picture-placeholder" "h-full w-full"}}
</span>
<button type="button"
class="bg-white py-2 px-3 border border-gray-300 rounded-2xl shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Charger la photo
</button>
</div>
</div> -->
</div>
</div>
</div>
</div>
<!--<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Paramètres</h3>
<p class="mt-1 text-sm text-gray-500">Paramètres liés au conducteur, utiles pour exploiter les fonctionnalités de PARCOURSMOB</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
{{ $fieldName := "address" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName }}
</div>
</div>
</div>-->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/organized-carpool/">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Créer le covoitureur solidaire</button>
</div>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,144 @@
{{define "content"}}
<main class="py-10">
<!-- Page header -->
<div class="max-w-3xl mx-auto px-4 sm:px-6 md:flex md:items-center md:justify-between md:space-x-5 lg:max-w-7xl lg:px-8">
<div class="flex items-center space-x-5">
<div class="flex-shrink-0">
<div class="relative">
<img class="h-16 w-16 rounded-co" src="/app/beneficiaries/{{.ViewState.driver.ID}}/picture" alt="">
<span class="absolute inset-0 shadow-inner rounded-full" aria-hidden="true"></span>
</div>
</div>
<div>
<h1 class="text-2xl font-bold text-gray-900">{{.ViewState.driver.Data.first_name}}
{{.ViewState.driver.Data.last_name}}</h1>
<p class="text-sm font-bold text-co-red">{{if .ViewState.driver.Data.archived}}Conducteur archivé{{end}}
<p class="text-sm font-medium text-gray-500">{{if .ViewState.driver.Metadata.created}}Ajouté le <time
datetime="2022-07-25">{{.ViewState.driver.Metadata.created}}</time> par
<a href="#" class="text-gray-900">Conseiller 1</a>{{end}}
</p>
</div>
</div>
<div
class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
{{if not .ViewState.driver.Data.archived}}<a href="/app/organized-carpool/drivers/{{ .ViewState.driver.ID }}/archive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Archiver</button></a>{{end}}
{{if .ViewState.driver.Data.archived}}<a href="/app/organized-carpool/drivers/{{ .ViewState.driver.ID }}/unarchive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Désarchiver</button></a>{{end}}
<!-- <button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Supprimer</button> -->
<!-- <a href="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a>-->
</div>
</div>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
<div class="space-y-6 lg:col-start-1 lg:col-span-2">
<section aria-labelledby="driver-information-title">
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h2 id="driver-information-title" class="text-lg leading-6 font-medium text-gray-900">
Informations personnelles</h2>
<p class="mt-1 max-w-2xl text-sm text-gray-500">Informations générales sur le conducteur solidaire.</p>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
{{if .ViewState.driver.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.email}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.phone_number}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .ViewState.driver.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .ViewState.driver.Data.gender (ne .ViewState.driver.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .ViewState.driver.Data.gender}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse principale</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.address_destination}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse destination</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.address_destination.properties.label}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
</section>
<section aria-labelledby="functionalities-title" x-data="{
tab: 'documents',
to(event) {
this.tab = event.target.value
}
}">
<div class="bg-white shadow sm:rounded-lg sm:overflow-hidden">
<div class="divide-y divide-gray-200">
<div>
<div class="sm:hidden">
<label for="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" @change="to"
class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<!--<option value="notes">Notes</option>-->
<!-- <option value="journeys">Déplacements</option>
<option value="vehicles">Véhicules</option>
<option value="events">Dispositifs</option> -->
<option value="documents">Documents</option>
</select>
</div>
<div class="hidden sm:block">
<div class="border-b border-gray-200 pl-4">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<!-- Current: "border-indigo-500 text-indigo-600", Default: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" -->
<!--<a href="#" @click="tab = 'notes'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'notes' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Notes </a>-->
<a href="#" @click="tab = 'documents'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'documents' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Documents </a>
</nav>
</div>
</div>
</div>
<div x-show="tab == 'documents'">{{template "driver_files" .}}</div>
</div>
</div>
</section>
</div>
<section class="lg:col-start-3 lg:col-span-1">
{{template "driver_availabilities" .}}
</section>
</div>
</main>
{{end}}

View File

@@ -0,0 +1,17 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Organiser le covoiturage</h1>
</div>
<div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 md:px-8">
{{template "journey_preview" (dict "journey" .ViewState.journey "driver" .ViewState.driver "passenger" .ViewState.passenger "beneficiaries" .ViewState.beneficiaries)}}
{{if ne .ViewState.passenger.ID ""}}
<div class="max-w-7xl m-auto px-4 sm:px-6 md:px-8 flex justify-items-center">
<form method="POST">
<button class="w-100 bg-co-blue border-gray-300 border px-4 py-2 text-white items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Envoyer la mise en relation
</button>
</form>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,48 @@
{{ define "content" }}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Covoiturage solidaire</h1>
<div class="bg-white shadow sm:rounded-lg sm:overflow-hidden my-4" x-data="{
tab: 'drivers',
to(event) {
this.tab = event.target.value
}
}">
<div class="divide-y divide-gray-200">
<div>
<div class="hidden sm:block">
<div class="border-b border-gray-200 pl-4">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<a href="#" @click="tab = 'drivers'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'drivers' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Covoitureurs solidaires </a>
<!--<a href="#" @click="tab = 'beneficiaries'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'beneficiaries' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Bénéficiaires </a>-->
<a href="#" @click="tab = 'trips'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'solidarityService' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Trajets </a>
</nav>
</div>
</div>
</div>
<div x-show="tab == 'drivers'">{{template "carpool_drivers_list" .}}</div>
<div x-show="tab == 'beneficiaries'">1</div>
<div x-show="tab == 'trips'">{{template "carpool_bookings_list" .}}</div>
</div>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,82 @@
{{ define "solidarity_bookings_history" }}
{{if eq (len .ViewState.bookings_history) 0}}
<div class="m-10 text-center text-gray-600">Aucun trajet dans le passé</div>
{{else}}
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Passager
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Départ
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Destination
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Date
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Statut
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.bookings_history}}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/solidarity-transport/drivers/{{.DriverId}}">
{{ (index $.ViewState.drivers_map .DriverId).Data.first_name }}
{{ (index $.ViewState.drivers_map .DriverId).Data.last_name }}
</a>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/beneficiaries/{{.PassengerId}}">
{{ (index $.ViewState.passengers_map .PassengerId).Data.first_name }}
{{ (index $.ViewState.passengers_map .PassengerId).Data.last_name }}
</a>
</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerPickup.Properties.label }}
</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerDrop.Properties.label }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerPickupDate.Format "02/01/2006 15:04" }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ if eq .Status "WAITING_CONFIRMATION"}}
<span class="p-1 text-xs bg-gray-300 rounded-xl">Attente confirmation</span>
{{ else if eq .Status "VALIDATED"}}
<span class="p-1 text-xs bg-co-green rounded-xl">Validé</span>
{{ else if eq .Status "CANCELLED"}}
<span class="p-1 text-xs bg-co-red text-white rounded-xl">Annulé</span>
{{ end }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/solidarity-transport/bookings/{{.Id}}">
Voir
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}
{{end}}

View File

@@ -0,0 +1,83 @@
{{ define "solidarity_bookings_list" }}
{{if eq (len .ViewState.bookings) 0}}
<div class="m-10 text-center text-gray-600">Aucun trajet déclaré</div>
{{else}}
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Conducteur
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Passager
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Départ
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Destination
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Date
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Statut
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{range .ViewState.bookings}}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/solidarity-transport/drivers/{{.DriverId}}">
{{ (index $.ViewState.drivers_map .DriverId).Data.first_name }}
{{ (index $.ViewState.drivers_map .DriverId).Data.last_name }}
</a>
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/beneficiaries/{{.PassengerId}}">
{{ (index $.ViewState.passengers_map .PassengerId).Data.first_name }}
{{ (index $.ViewState.passengers_map .PassengerId).Data.last_name }}
</a>
</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerPickup.Properties.label }}
</td>
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ .Journey.PassengerDrop.Properties.label }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ timeFormat .Journey.PassengerPickupDate "02/01/2006 15:04" }}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{ if eq .Status "WAITING_CONFIRMATION"}}
<span class="p-1 text-xs bg-gray-300 rounded-xl">Attente confirmation</span>
{{ else if eq .Status "VALIDATED"}}
<span class="p-1 text-xs bg-co-green rounded-xl">Validé</span>
{{ else if eq .Status "CANCELLED"}}
<span class="p-1 text-xs bg-co-red text-white rounded-xl">Annulé</span>
{{ end }}
{{if and .Data.motivation (or (or (eq .Data.motivation "Santé") (eq .Data.motivation "Insertion")) (eq .Data.motivation "Administratif"))}}<div class="mt-4"><span class="text-xs p-2 bg-co-green text-white rounded-2xl">Trajet garanti : {{.Data.motivation}}</span></div>{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue" href="/app/solidarity-transport/bookings/{{.Id}}">
Voir
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}
{{end}}

View File

@@ -0,0 +1,90 @@
{{define "driver_availabilities"}}
<div class="bg-white shadow sm:rounded-lg"
x-data="{
availabilitiesdialog: false
}">
<div class="px-4 py-5 sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Disponibilités</h2>
</div>
<div class="border-t border-gray-200 px-4">
<div class="py-4">
{{ range .ViewState.availabilities }}
<div class="flex flex-row">
<div class="flex-none">
{{ if eq .Day 0}}Dimanche
{{ else if eq .Day 1}}Lundi
{{ else if eq .Day 2}}Mardi
{{ else if eq .Day 3}}Mercredi
{{ else if eq .Day 4}}Jeudi
{{ else if eq .Day 5}}Vendredi
{{ else if eq .Day 6}}Samedi
{{ end }}
{{.StartTime}} - {{ .EndTime }}
</div>
<div class="flex-auto">&nbsp;</div>
<div class="flex-none text-sm"><a class="text-co-blue" href="/app/solidarity-transport/drivers/{{$.ViewState.driver.ID}}/availabilities/{{.Id}}/delete">Supprimer</a></div>
</div>
{{ end }}
</div>
<button type="button" @click="availabilitiesdialog = !availabilitiesdialog"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue my-4 px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Ajouter une disponibilité
</button>
</div>
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true"
x-show="availabilitiesdialog">
<div class="fixed inset-0 bg-gray-900 opacity-30 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Ajouter une disponibilité</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Paramétrer la nouvelle disponibilité du conducteur</p>
</div>
</div>
</div>
<form method="POST" action="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}/availabilities" class="my-4">
<div class="my-8">
{{ $fieldName := "address" }}
{{ template "address_autocomplete" (dict "FieldName" $fieldName "Address" .ViewState.driver.Data.address) }}
</div>
<div class="my-4">
<input name="days.monday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Lundi &nbsp;&nbsp;
<input name="days.tuesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mardi &nbsp;&nbsp;
<input name="days.wednesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mercredi &nbsp;&nbsp;
<input name="days.thursday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Jeudi &nbsp;&nbsp;
<input name="days.friday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Vendredi &nbsp;&nbsp;
<input name="days.saturday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Samedi &nbsp;&nbsp;
<input name="days.sunday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Dimanche &nbsp;&nbsp;
</div>
<div class="my-4 inline-flex justify-items-center">
<div class="p-1">De</div>
<input type="time" id="starttime" name="starttime" value="06:00"
class="shadow-sm focus:ring-co-blue focus:border-co-blue p-1 sm:text-sm border-gray-300 rounded-l-2xl border-l-0">
<div class="p-1 px-4">à</div>
<input type="time" id="endtime" name="endtime" value="20:00"
class="shadow-sm focus:ring-co-blue focus:border-co-blue p-1 sm:text-sm border-gray-300 rounded-r-2xl border-l-0">
</div>
<div class="mt-5 sm:mt-6">
<button type="submit" class="inline-flex w-full justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:text-sm">Ajouter</button>
</div>
<div class="mt-5 sm:mt-6">
<button @click="availabilitiesdialog=false" type="button" class="inline-flex w-full justify-center max-w-xs bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}

View File

@@ -0,0 +1,138 @@
{{define "driver_files"}}
<div class="px-4 py-6 sm:px-6"
x-data="{
fields: {
name: null,
type: null,
file: null,
},
rules: {
name: ['required'],
type: ['required'],
file: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
type: {valid: null},
file: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
{{if eq (len .ViewState.documents) 0}}
<p class="p-12 text-gray-500 text-center text-md">Aucun document</p>
{{end}}
{{if gt (len .ViewState.documents) 0}}
<div class="-mx-4 mb-10 ring-1 ring-gray-300 sm:-mx-6 md:mx-0 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">Type</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Nom du document</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">Ajouté le</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
<span class="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody>
{{range .ViewState.documents}}
<tr>
<td class="relative py-4 pl-4 sm:pl-6 pr-3 text-sm">
<div class="font-medium text-gray-900">
<span class="bg-co-blue text-xs text-white rounded-xl p-1 mr-2">{{index $.ViewState.file_types_map .Metadata.Type}}</span>
</div>
</td>
<td class="px-3 py-3.5 text-sm text-gray-900 lg:table-cell">{{.Metadata.Name}}</td>
<td class="px-3 py-3.5 text-sm text-gray-500 lg:table-cell">{{.LastModified.Format "02/01/2006"}}</td>
<td class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right text-sm font-medium">
<a href="/app/solidarity-transport/drivers/{{$.ViewState.driver.ID}}/documents/{{.FileName}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le document</span></button>
</a>
</td>
</tr>
{{end}}
<!-- More plans... -->
</tbody>
</table>
</div>
{{end}}
<h3 class="text-lg">Ajouter un document</h3>
<form method="POST" action="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}/documents" @submit="submit" enctype="multipart/form-data">
<div class="md:grid md:grid-cols-6 p-2">
<div class="sm:col-span-2">
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
<select id="type" name="type" class="mt-1 block w-full rounded-l-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
x-model="fields.type" @blur="validateField('type')"
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option value="" selected>Sélectionner un type</option>
{{range .ViewState.drivers_file_types}}
<option value="{{.}}">{{index $.ViewState.file_types_map .}}</option>
{{end}}
</select>
</div>
<div class="sm:col-span-4">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name"
placeholder="Nom du fichier"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-r-2xl p-2"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'" />
</div>
<div class="sm:col-span-6 mt-4">
<label for="cover-photo" class="block text-sm font-medium text-gray-700">Téléchargement</label>
<div class="mt-1 flex justify-center rounded-md border-2 border-dashed px-6 pt-5 pb-6"
x-on:drop="console.log('toto')"
:class="formValidation.fields.file.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<div class="space-y-1 text-center">
{{.IconSet.Icon "hero:outline/folder-plus" "mx-auto h-12 w-12 text-gray-400"}}
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer rounded-md bg-white font-medium text-co-blue focus-within:outline-none focus-within:ring-2 focus-within:ring-co-blue focus-within:ring-offset-2 hover:text-co-blue">
<span>Sélectionnez un fichier </span>
<input id="file-upload" name="file-upload" type="file" class="sr-only"
x-model="fields.file" @blur="validateField('file')">
</label>
<!-- <p class="pl-1">ou glissez-déposez</p> -->
</div>
<p class="text-xs text-gray-500">Jusqu'à 10MB</p>
<template x-if="fields.file">
<p class="text-co-blue p-2" x-text="fields.file"></p>
</template>
</div>
</div>
</div>
</div>
<button type="submit"
class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 my-4 mt-8 w-full text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
Ajouter le document
</button>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,7 @@
{{define "driver_history"}}
<div class="px-4 py-6 sm:px-6 text-center">
<p class="text-center text-lg">Trajets réalisés : {{ .ViewState.stats.bookings.count }}</p>
<p class="text-center text-lg">Kilomètres réalisés : {{ .ViewState.stats.bookings.km}} km</p>
</div>
{{end}}

View File

@@ -0,0 +1,78 @@
{{ define "solidarity_drivers_list" }}
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="m-4 sm:ml-16 sm:flex-none">
<!--<a href="/api/cache/{{.ViewState.CacheId}}/export">
<button type="button"
class="inline-flex items-center justify-center bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/document-arrow-down" "h-5 w-5 mr-3"}}
Exporter
</button>
</a>-->
<a href="/app/solidarity-transport/?archived=true">
<button type="button"
class="inline-flex items-center justify-center bg-white hover:bg-gray-50 border-gray-300 border px-4 py-2 text-gray-700 flex items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
{{$.IconSet.Icon "hero:outline/archive-box" "h-5 w-5 mr-3"}}
Conducteurs archivés
</button>
</a>
<a href="/app/solidarity-transport/drivers/create">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Ajouter un conducteur solidaire
</button>
</a>
</div>
</div>
<table class="min-w-full divide-y divide-gray-300 border-gray-300 border-t-1">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Nom
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Adresse
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Téléphone
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Profil validé
</th>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
&nbsp;
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{{ range .ViewState.drivers }}
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Data.first_name }} {{ .Data.last_name }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{if .Data.address}}{{.Data.address.properties.label}}{{end}}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">{{ .Data.phone_number }}</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
{{if solidarityDriverValidatedProfile . (solidarityDocuments .ID) }}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
<a class="text-co-blue hover:text-co-blue"
href="/app/solidarity-transport/drivers/{{.ID}}">
Voir
</a>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}

View File

@@ -0,0 +1,89 @@
{{ define "journey_map" }}
<div id="map" class="w-full h-full"></div>
<script>
let protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles",protocol.tile);
var map = new maplibregl.Map({
container: 'map', // container id
style: "/public/maps/protomaps-light/style.json",
});
const geojsonPoints = {
"type": "FeatureCollection",
"features": [
{{ json .driver_journey.DriverDeparture }},
{{ json .driver_journey.PassengerPickup }},
{{ json .driver_journey.PassengerDrop }},
{{ json .driver_journey.DriverArrival }}
]
}
const driverGeojsonPoints = {
"type": "FeatureCollection",
"features": [
{{ json .driver_journey.DriverDeparture }},
{{ json .driver_journey.DriverArrival }}
]
}
const passengerGeojsonPoints = {
"type": "FeatureCollection",
"features": [
{{ json .driver_journey.PassengerPickup }},
{{ json .driver_journey.PassengerDrop }},
]
}
map.on('load', async () => {
{{if .driver_journey.JourneyPolyline}}
var linestring = polyline.toGeoJSON('{{.driver_journey.JourneyPolyline}}');
map.addSource('trip', {
type: "geojson",
data: linestring
})
map.addLayer({
'id': 'trip',
'type': 'line',
'source': 'trip',
'layout': {
'line-join': 'round',
'line-cap': 'round',
},
'paint': {
'line-color': '#243887',
'line-width': 3
},
});
{{end}}
map.addSource('driver_points', {
type: "geojson",
data: driverGeojsonPoints
})
map.addLayer({
'id': 'driver_points',
'type': 'circle',
'source': 'driver_points',
'paint': {
'circle-color': '#000'
},
});
map.addSource('passenger_points', {
type: "geojson",
data: passengerGeojsonPoints
})
map.addLayer({
'id': 'passenger_points',
'type': 'circle',
'source': 'passenger_points',
'paint': {
'circle-color': '#243887',
'circle-radius': 8
},
});
var bbox=turf.bbox(geojsonPoints)
map.fitBounds(bbox, { padding: 50 })
})
</script>
{{ end }}

View File

@@ -0,0 +1,261 @@
{{define "journey_preview"}}
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2 grid-cols-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Le trajet</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">Informations sur le trajet</h2>
</div>
<div>
{{if eq .driver_journey.Noreturn true}}<span class="text-sm p-2 bg-co-red text-white rounded-xl">Aller simple</span>{{else}}<span class="text-sm p-2 bg-co-red text-white rounded-xl">Aller-retour</span>{{end}}
</div>
</div>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div class="p-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Départ passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.PassengerPickup.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Destination passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.PassengerDrop.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Départ conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.DriverDeparture.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Destination conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.DriverArrival.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Kilomètres passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.PassengerDistance}} km</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Kilomètres conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver_journey.DriverDistance}} km</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Horaire départ conducteur</dt>
<dd class="mt-1 text-sm text-gray-900">{{ .driver_journey.DriverDepartureDate.Format "02/01/2006 15:04"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Horaire rendez vous passager</dt>
<dd class="mt-1 text-sm text-gray-900">{{ timeFormat .driver_journey.PassengerPickupDate "02/01/2006 15:04"}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Temps de trajet (passager)</dt>
<dd class="mt-1 text-sm text-gray-900">{{ printf "%.0f" .driver_journey.Duration.Minutes }} min</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Prix (passager)</dt>
<dd class="mt-1 text-sm text-gray-900">{{ .driver_journey.Price.Amount }} {{ .driver_journey.Price.Currency}}</dd>
</div>
</dl>
</div>
<div>
{{ template "journey_map" . }}
</div>
</div>
</div>
<div class="py-4 grid grid-cols-1 gap-6 sm:grid-cols-2">
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2 grid-cols-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Conducteur</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">{{.driver.Data.first_name}} {{.driver.Data.last_name}}</h2>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Profil validé</dt>
<dd class="mt-1 text-sm text-gray-900">
{{if solidarityDriverValidatedProfile .driver (solidarityDocuments .driver.ID)}}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</dd>
</div>
{{if .driver.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.email}}</dd>
</div>
{{end}}
{{if .driver.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.phone_number}}</dd>
</div>
{{end}}
{{if .driver.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .driver.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .driver.Data.gender (ne .driver.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .driver.Data.gender}}</dd>
</div>
{{end}}
{{if .driver.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .driver.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dd class="mt-1 text-sm text-gray-900">{{.driver.Data.file_number}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
<div class="col-span-1 bg-white rounded-2xl shadow divide-y divide-gray-200 flex flex-col">
<div class="px-4 py-2 flex items-center justify-between flex-wrap sm:flex-nowrap">
<div class="ml-4 mt-2">
<h3 class="text-lg leading-6 font-medium text-gray-900">Passager</h3>
<h2 class="text-sm leading-6 font-medium text-gray-600">{{if ne .passenger.ID "" }}{{.passenger.Data.first_name}} {{.passenger.Data.last_name}}{{end}}</h2>
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
{{if eq .passenger.ID ""}}
<form method="GET">
<div x-data="{
text: '',
beneficiariesListOpen: false,
beneficiaries: {{json .beneficiaries}},
filteredBeneficiaries: (beneficiaries, text) => {
if(text=='') {
return beneficiaries
}
return beneficiaries.filter(b => b['data']['first_name'].toLowerCase().includes(text.toLowerCase()) || b['data']['last_name'].toLowerCase().includes(text.toLowerCase()))
},
fields: {
beneficiaryid: {{if .search}}'{{.search.beneficiary.ID}}'{{else}}null{{end}},
},
selectbeneficiary(beneficiary) {
this.fields.beneficiaryid = beneficiary.id
this.text = beneficiary.data.first_name + ' ' + beneficiary.data.last_name
this.beneficiariesListOpen = false
},
}">
<input type="hidden" name="passengerid" x-model="fields.beneficiaryid">
<label for="combobox" class="block text-sm font-medium text-gray-700">Selectionner un bénéficiaire</label>
<div class="relative mt-1 mb-4">
<input autocomplete="off" @focus="beneficiariesListOpen = true" x-model="text" id="combobox" type="text" class="w-full rounded-2xl border border-gray-300 bg-white py-2 pl-3 pr-12 shadow-sm focus:border-co-blue focus:outline-none focus:ring-1 focus:ring-co-blue sm:text-sm" role="combobox" aria-controls="options" aria-expanded="false">
<button @click="beneficiariesListOpen = ! beneficiariesListOpen" type="button" class="absolute inset-y-0 right-0 flex items-center rounded-r-2xl px-2 focus:outline-none">
<!-- Heroicon name: solid/selector -->
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<ul x-show="beneficiariesListOpen" class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-xl bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" id="options" role="listbox">
<!--
Combobox option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
Active: "text-white bg-indigo-600", Not Active: "text-gray-900"
-->
<template x-for="beneficiary in filteredBeneficiaries(beneficiaries, text)">
<li @click="selectbeneficiary(beneficiary)" class="relative cursor-default hover:bg-gray-100 select-none py-2 pl-3 pr-9 text-gray-900" id="option-0" role="option" tabindex="-1">
<!-- Selected: "font-semibold" -->
<span class="truncate" x-text="beneficiary.data.first_name"></span> <span class="truncate" x-text="beneficiary.data.last_name"></span>
<!--
Checkmark, only display for selected option.
Active: "text-white", Not Active: "text-indigo-600"
-->
<span class="absolute inset-y-0 right-0 flex items-center pr-4 text-co-blue">
<!-- Heroicon name: solid/check -->
<!-- <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg> -->
</span>
</li>
</template>
<!-- More items... -->
</ul>
</div>
</div>
<button type="submit" class="inline-flex w-full justify-center max-w-xs bg-co-blue border-gray-300 border px-4 py-2 text-white items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Valider
</button>
</div>
</form>
{{ else }}
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Profil validé</dt>
<dd class="mt-1 text-sm text-gray-900">
{{if beneficiaryValidatedProfile .passenger (beneficiaryDocuments .passenger.ID)}}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</dd>
</div>
{{if .passenger.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.email}}</dd>
</div>
{{end}}
{{if .passenger.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.phone_number}}</dd>
</div>
{{end}}
{{if .passenger.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .passenger.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .passenger.Data.gender (ne .passenger.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .passenger.Data.gender}}</dd>
</div>
{{end}}
{{if .passenger.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .passenger.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dd class="mt-1 text-sm text-gray-900">{{.passenger.Data.file_number}}</dd>
</div>
{{end}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Compte mobilié (solde)</dt>
<dd class="mt-1 text-sm text-gray-900">{{if .passenger.Data.wallet}}{{ .passenger.Data.wallet }}{{else}}0{{end}} EUR</dd>
</div>
</dl>
</div>
{{end}}
</div>
</div>
{{end}}

View File

@@ -0,0 +1,124 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">
Transport solidaire
</h1>
{{if and .ViewState.booking.Data.motivation (or (or (eq .ViewState.booking.Data.motivation "Santé") (eq .ViewState.booking.Data.motivation "Insertion")) (eq .ViewState.booking.Data.motivation "Administratif"))}}<div class="mt-4"><span class="text-sm p-2 bg-co-green text-white rounded-2xl">Trajet garanti : {{.ViewState.booking.Data.motivation}}</span></div>{{end}}
{{ if eq .ViewState.booking.Status "WAITING_CONFIRMATION"}}
<div class="mt-4"><span class="p-2 text-sm bg-gray-300 rounded-2xl px-4">Attente confirmation</span></div>
{{ else if eq .ViewState.booking.Status "VALIDATED"}}
<div class="mt-4"><span class="p-1 text-sm bg-co-green rounded-2xl px-4">Validé</span></div>
{{ else if eq .ViewState.booking.Status "CANCELLED"}}
<div class="mt-4"><span class="p-1 text-sm bg-co-red text-white rounded-2xl px-4">Annulé : {{.ViewState.booking.Data.reason}}</span></div>
{{ end }}
<div
class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
{{$dialog := "modif"}}
<div x-data="{ {{ $dialog }}: false}">
<button @click="{{ $dialog }} = !{{ $dialog }}" type="button" class="bg-white border-gray-300 border px-4 py-2 text-gray-700 items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Modifier
</button>
<div x-show="{{$dialog}}" class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full max-w-lg sm:p-6">
<form method="POST">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-co bg-co-blue text-white">
{{$.IconSet.Icon "hero:outline/information-circle" "h-6 w-6"}}
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Modifier</h3>
<div class="mt-2">
<p class="text-sm text-gray-500"></p>
</div>
<div>
<label for="driver_distance" class="block text-sm font-medium text-gray-700">Kilomètres conducteur</label>
<div class="mt-1">
<input type="text" name="driver_distance" id="driver_distance" class="block w-full rounded-2xl border-gray-300 shadow-sm focus:border-co-blue focus:ring-co-blue sm:text-sm" value="{{.ViewState.booking.Journey.DriverDistance}}" />
</div>
</div>
</div>
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
<button @click="{{$dialog}} = !{{$dialog}}" type="button" class="mt-3 inline-flex w-full justify-center rounded-l-2xl border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-1 sm:mt-0 sm:text-sm">Annuler</button>
<button type="submit" class="inline-flex w-full justify-center rounded-r-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-2 sm:text-sm">Valider</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- <button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Supprimer</button> -->
{{if eq .ViewState.booking.Status "CANCELLED" }}
<a href="/app/journeys/?departure={{json .ViewState.booking.Journey.PassengerPickup}}&destination={{ json .ViewState.booking.Journey.PassengerDrop }}&departuredate={{ .ViewState.booking.Journey.PassengerPickupDate.Format "2006-01-02"}}&departuretime={{ .ViewState.booking.Journey.PassengerPickupDate.Format "15:04"}}" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm bg-white text-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Solliciter un autre conducteur</button></a>
{{end}}
{{if eq .ViewState.booking.Status "WAITING_CONFIRMATION" }}
<a href="/app/solidarity-transport/bookings/{{.ViewState.booking.Id}}/confirm" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Confirmer</button></a>
{{end}}
{{if ne .ViewState.booking.Status "WAITING_CONFIRMATION" }}
<a href="/app/solidarity-transport/bookings/{{.ViewState.booking.Id}}/waitconfirmation" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Remettre en attente</button></a>
{{end}}
{{if ne .ViewState.booking.Status "CANCELLED" }}
<!--<a href="/app/solidarity-transport/bookings/{{.ViewState.booking.Id}}/cancel" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-red hover:bg-co-red focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Annuler</button></a>-->
{{$dialog := "cancel"}}
<div x-data="{ {{ $dialog }}: false}">
<button @click="{{ $dialog }} = !{{ $dialog }}" type="button" class="bg-co-red border-gray-300 border px-4 py-2 text-white items-center text-sm rounded-2xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Annuler
</button>
<div x-show="{{$dialog}}" class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed inset-0 z-10 overflow-y-auto">
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full max-w-lg sm:p-6">
<form method="POST" action="/app/solidarity-transport/bookings/{{.ViewState.booking.Id}}/cancel">
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-co bg-co-blue text-white">
{{$.IconSet.Icon "hero:outline/information-circle" "h-6 w-6"}}
</div>
<div class="mt-3 text-center sm:mt-5">
<h3 class="text-lg font-medium leading-6 text-gray-900" id="modal-title">Annuler</h3>
<div class="mt-2">
<p class="text-sm text-gray-500"></p>
</div>
<div>
<label for="reason" class="block text-sm font-medium text-gray-700">Raison de l'annulation</label>
<div class="mt-1">
<select name="reason" id="reason" class="block w-full rounded-2xl border-gray-300 shadow-sm focus:border-co-blue focus:ring-co-blue sm:text-sm">
<option value="Trajet annulé par le passager">Trajet annulé par le passager</option>
<option value="Trajet non réalisé">Trajet non réalisé</option>
<option value="Refusé par le bénévole">Refusé par le bénévole</option>
<option value="Autre" selected="selected">Autre</option>
</select>
</div>
</div>
</div>
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
<button @click="{{$dialog}} = !{{$dialog}}" type="button" class="mt-3 inline-flex w-full justify-center rounded-l-2xl border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-1 sm:mt-0 sm:text-sm">Annuler</button>
<button type="submit" class="inline-flex w-full justify-center rounded-r-2xl border border-transparent bg-co-blue px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:col-start-2 sm:text-sm">Valider</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{{end}}
</div>
</div>
<div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 md:px-8">
{{template "journey_preview" (dict "driver_journey" .ViewState.booking.Journey "driver" .ViewState.driver "passenger" .ViewState.passenger "beneficiaries" .ViewState.beneficiaries)}}
</div>
{{ end }}

View File

@@ -0,0 +1,190 @@
{{ define "content" }}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Créer un conducteur solidaire</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
first_name: null,
last_name: null,
email: null,
phone_number: null,
birthdate: null,
},
other_properties: {},
other_properties_serialized: null,
rules: {
first_name: ['required'],
last_name: ['required'],
email: ['required', 'email'],
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
birthdate: ['required'],
},
formValidation: {
valid: false,
fields: {
first_name: {valid: null},
last_name: {valid: null},
email: {valid: null},
phone_number: {valid: null},
birthdate: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.other_properties_serialized = JSON.stringify(this.other_properties)
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<input type="hidden" name="other_properties" x-model="other_properties_serialized" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations personnelles obligatoires pour créer le conducteur</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="block text-sm font-medium text-gray-700">Prénom</label>
<input type="text" name="first_name" id="first_name" autocomplete="given-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.first_name" @blur="validateField('first_name')"
:class="formValidation.fields.first_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="last_name" id="last_name" autocomplete="family-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.last_name" @blur="validateField('last_name')"
:class="formValidation.fields.last_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="text" name="email" id="email" autocomplete="email"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.email" @blur="validateField('email')"
:class="formValidation.fields.email.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="phone_number" class="block text-sm font-medium text-gray-700">Numéro de
téléphone</label>
<input type="text" name="phone_number" id="phone_number" autocomplete="phone" placeholder="+33612345678"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.phone_number" @blur="validateField('phone_number')"
:class="formValidation.fields.phone_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="birthdate" class="block text-sm font-medium text-gray-700">Date de
naissance</label>
<input type="date" name="birthdate" id="birthdate" autocomplete="birthdate" placeholder="JJ/MM/AAAA"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.birthdate" @blur="validateField('birthdate')"
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
{{ $fieldName := "address" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName }}
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations optionnelles</h3>
<p class="mt-1 text-sm text-gray-500">Autres informations de profil optionnelles</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="gender" name="gender" autocomplete="gender" x-model="gender"
class="p-2 max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="0">Inconnu</option>
<option value="1">Masculin</option>
<option value="2">Féminin</option>
<option value="9">Sans objet</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_subscription_date" class="block text-sm font-medium text-gray-700">Date de dernière adhésion</label>
<input type="date" name="last_subscription_date" id="last_subscription_date" autocomplete="last_subscription_date" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.last_subscription_date">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="comment" class="block text-sm font-medium text-gray-700">Commentaire</label>
<textarea name="comment" id="comment"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.comment"></textarea>
</div>
<!-- <div class="col-span-3 sm:col-span-3">
<label class="block text-sm font-medium text-gray-700"> Photo </label>
<div class="mt-1 flex items-center space-x-5">
<span class="inline-block h-12 w-12 rounded-co overflow-hidden bg-gray-100">
{{.IconSet.Icon "img:profile-picture-placeholder" "h-full w-full"}}
</span>
<button type="button"
class="bg-white py-2 px-3 border border-gray-300 rounded-2xl shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Charger la photo
</button>
</div>
</div> -->
</div>
</div>
</div>
</div>
<!--<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Paramètres</h3>
<p class="mt-1 text-sm text-gray-500">Paramètres liés au conducteur, utiles pour exploiter les fonctionnalités de PARCOURSMOB</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
{{ $fieldName := "address" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName }}
</div>
</div>
</div>-->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/solidarity-transport/">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Créer le conducteur solidaire</button>
</div>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,172 @@
{{define "content"}}
<main class="py-10">
<!-- Page header -->
<div class="max-w-3xl mx-auto px-4 sm:px-6 md:flex md:items-center md:justify-between md:space-x-5 lg:max-w-7xl lg:px-8">
<div class="flex items-center space-x-5">
<div class="flex-shrink-0">
<div class="relative">
<img class="h-16 w-16 rounded-co" src="/app/beneficiaries/{{.ViewState.driver.ID}}/picture" alt="">
<span class="absolute inset-0 shadow-inner rounded-full" aria-hidden="true"></span>
</div>
</div>
<div>
<h1 class="text-2xl font-bold text-gray-900">{{.ViewState.driver.Data.first_name}}
{{.ViewState.driver.Data.last_name}}</h1>
<p class="text-sm font-medium text-gray-500">{{if .ViewState.driver.Metadata.created}}Ajouté le <time
datetime="2022-07-25">{{.ViewState.driver.Metadata.created}}</time> par
<a href="#" class="text-gray-900">Conseiller 1</a>{{end}}
</p>
</div>
</div>
<div
class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
{{if not .ViewState.driver.Data.archived}}<a href="/app/solidarity-transport/drivers/{{ .ViewState.driver.ID }}/archive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Archiver</button></a>{{end}}
{{if .ViewState.driver.Data.archived}}<a href="/app/solidarity-transport/drivers/{{ .ViewState.driver.ID }}/unarchive"><button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Désarchiver</button></a>{{end}}
<!-- <button type="button"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Supprimer</button> -->
<a href="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a>
</div>
</div>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
<div class="space-y-6 lg:col-start-1 lg:col-span-2">
<section aria-labelledby="driver-information-title">
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h2 id="driver-information-title" class="text-lg leading-6 font-medium text-gray-900">
Informations personnelles</h2>
<p class="mt-1 max-w-2xl text-sm text-gray-500">Informations générales sur le conducteur solidaire.</p>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Profil validé</dt>
<dd class="mt-1 text-sm text-gray-900">
{{if solidarityDriverValidatedProfile .ViewState.driver .ViewState.documents}}
<span class="p-1 px-2 text-xs bg-co-green rounded-2xl">Oui</span>
{{else}}
<span class="p-1 px-2 text-xs bg-co-red text-white rounded-2xl">Non</span>
{{end}}
</dd>
</div>
{{if .ViewState.driver.Data.email}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Email</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.email}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.phone_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Téléphone</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.phone_number}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.birthdate}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date de naissance</dt>
<dd class="mt-1 text-sm text-gray-900">{{(timeFrom .ViewState.driver.Data.birthdate).Format
"02/01/2006"}}</dd>
</div>
{{end}}
{{if and .ViewState.driver.Data.gender (ne .ViewState.driver.Data.gender "0")}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Genre</dt>
<dd class="mt-1 text-sm text-gray-900">{{genderISO5218 .ViewState.driver.Data.gender}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Adresse</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.address.properties.label}}</dd>
</div>
{{end}}
{{if .ViewState.driver.Data.file_number}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Numéro de dossier (CAF / Pole emploi)</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.driver.Data.file_number}}</dd>
</div>
{{end}}
{{if and .ViewState.driver.Data.other_properties .ViewState.driver.Data.other_properties.last_subscription_date}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Dernière adhésion</dt>
<dd class="mt-1 text-sm text-gray-900">{{ .ViewState.driver.Data.other_properties.last_subscription_date}}</dd>
</div>
{{end}}
{{if and .ViewState.driver.Data.other_properties .ViewState.driver.Data.other_properties.comment}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Commentaire</dt>
<dd class="mt-1 text-sm text-gray-900">{{ .ViewState.driver.Data.other_properties.comment}}</dd>
</div>
{{end}}
</dl>
</div>
</div>
</section>
<section aria-labelledby="functionalities-title" x-data="{
tab: 'documents',
to(event) {
this.tab = event.target.value
}
}">
<div class="bg-white shadow sm:rounded-lg sm:overflow-hidden">
<div class="divide-y divide-gray-200">
<div>
<div class="sm:hidden">
<label for="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" @change="to"
class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
<!--<option value="notes">Notes</option>-->
<!-- <option value="journeys">Déplacements</option>
<option value="vehicles">Véhicules</option>
<option value="events">Dispositifs</option> -->
<option value="documents">Documents</option>
<option value="history">Historique</option>
</select>
</div>
<div class="hidden sm:block">
<div class="border-b border-gray-200 pl-4">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<!-- Current: "border-indigo-500 text-indigo-600", Default: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" -->
<!--<a href="#" @click="tab = 'notes'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'notes' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Notes </a>-->
<a href="#" @click="tab = 'documents'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'documents' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Documents </a>
<a href="#" @click="tab = 'history'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'history' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Transport solidaires </a>
</nav>
</div>
</div>
</div>
<div x-show="tab == 'documents'">{{template "driver_files" .}}</div>
<div x-show="tab == 'history'">{{template "driver_history" .}}</div>
</div>
</div>
</section>
</div>
<section class="lg:col-start-3 lg:col-span-1">
{{template "driver_availabilities" .}}
</section>
</div>
</main>
{{end}}

View File

@@ -0,0 +1,72 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Organiser le transport solidaire</h1>
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<p class="mt-2 text-sm text-gray-700"></p>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<form method="POST" action="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}/journeys/{{.ViewState.driver_journey.Id}}/noreturn">
{{if .ViewState.driver_journey.Noreturn}}
<button type="submit"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/arrow-path-rounded-square" "h-5 w-5 mr-3"}}
Changer en aller-retour
</button>
{{else}}
<button type="submit"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/arrow-long-right" "h-5 w-5 mr-3"}}
Changer en aller simple
</button>
{{end}}
</form>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 md:px-8">
{{template "journey_preview" (dict "driver_journey" .ViewState.driver_journey "driver" .ViewState.driver "passenger" .ViewState.passenger "beneficiaries" .ViewState.beneficiaries)}}
{{if ne .ViewState.passenger.ID ""}}
<div class="max-w-7xl m-auto px-4 sm:px-6 md:px-8 flex flex-col justify-items-center">
{{if .ViewState.passenger}}
{{$wallet := (or .ViewState.passenger.Data.wallet 0.0)}}
{{if lt $wallet .ViewState.driver_journey.Price.Amount}}
<p class="text-xl text-co-red text-center p-4">Le solde du compte mobilité est insuffisant.</p>
{{end}}
<form method="POST">
{{if not .ViewState.driver_journey.Noreturn}}
<div class="m-auto text-center">
<label for="return_waiting_time" class="block text-sm font-medium text-gray-700">Durée d'attente à destination (minutes)</label>
<input type="number" name="return_waiting_time" value="30"
class="p-2 mt-1 mb-4 focus:ring-co-blue focus:border-co-blue block shadow-sm sm:text-sm rounded-2xl bg-white m-auto" />
</div>
{{end}}
<div class="m-auto text-center">
<label for="motivation" class="block text-sm font-medium text-gray-700">Motif du trajet</label>
<select name="motivation" id="motivation"
class="p-2 mt-1 mb-4 focus:ring-co-blue focus:border-co-blue block shadow-sm sm:text-sm rounded-2xl bg-white m-auto">
<option value="Administratif">Administratif (trajet garanti)</option>
<option value="Commerce">Commerce</option>
<option value="Courses">Courses</option>
<option value="Insertion">Insertion (trajet garanti)</option>
<option value="Loisirs">Loisirs</option>
<option value="Visite à un proche">Visite à un proche</option>
<option value="Santé">Santé (trajet garanti)</option>
<option selected="selected" value="">Autre&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>
</select>
</div>
{{template "submit_with_sms"
dict "IconSet" .IconSet
"Viewstate" .ViewState
"ComponentState" (dict "submitText" "Envoyer la mise en relation"
"doNotSendOption" true)
"SMSState" (dict "name" (.ViewState.config.GetString "service_name")
"datetime" (timeFormat .ViewState.driver_journey.PassengerPickupDate "02/01/2006 15:04")
"baseUrl" (.ViewState.config.GetString "base_url") )}}
</form>
{{end}}
</div>
{{end}}
{{ end }}

View File

@@ -0,0 +1,195 @@
{{ define "content" }}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Modifier le conducteur</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
first_name: '{{.ViewState.driver.Data.first_name}}',
last_name: '{{.ViewState.driver.Data.last_name}}',
email: '{{.ViewState.driver.Data.email}}' ,
phone_number: '{{.ViewState.driver.Data.phone_number}}',
birthdate: {{if .ViewState.driver.Data.birthdate}}'{{ (timeFrom .ViewState.driver.Data.birthdate).Format "2006-01-02" }}'{{else}}null{{end}},
},
other_properties: {{if .ViewState.driver.Data.other_properties}}{{ json .ViewState.driver.Data.other_properties }}{{else}}{}{{end}},
other_properties_serialized: '{{ json .ViewState.driver.Data.other_properties }}',
rules: {
first_name: ['required'],
last_name: ['required'],
email: ['required', 'email'],
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
birthdate: ['required'],
},
formValidation: {
valid: false,
fields: {
first_name: {valid: null},
last_name: {valid: null},
email: {valid: null},
phone_number: {valid: null},
birthdate: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.other_properties_serialized = JSON.stringify(this.other_properties)
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<input type="hidden" name="other_properties" x-model="other_properties_serialized" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations personnelles obligatoires pour créer le conducteur</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="first_name" class="block text-sm font-medium text-gray-700">Prénom</label>
<input type="text" name="first_name" id="first_name" autocomplete="given-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.first_name" @blur="validateField('first_name')"
:class="formValidation.fields.first_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="last_name" id="last_name" autocomplete="family-name"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.last_name" @blur="validateField('last_name')"
:class="formValidation.fields.last_name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="text" name="email" id="email" autocomplete="email"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.email" @blur="validateField('email')"
:class="formValidation.fields.email.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="phone_number" class="block text-sm font-medium text-gray-700">Numéro de
téléphone</label>
<input type="text" name="phone_number" id="phone_number" autocomplete="phone" placeholder="+33612345678"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.phone_number" @blur="validateField('phone_number')"
:class="formValidation.fields.phone_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="birthdate" class="block text-sm font-medium text-gray-700">Date de
naissance</label>
<input type="date" name="birthdate" id="birthdate" autocomplete="birthdate" placeholder="JJ/MM/AAAA"
class="p-2 mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.birthdate" @blur="validateField('birthdate')"
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
{{ $fieldName := "address" }}
{{if .ViewState.driver.Data.address}}
{{$default := .ViewState.driver.Data.address}}
{{ template "address" dict "FieldName" $fieldName "Default" $default}}
{{else}}
{{ template "address_autocomplete" dict "FieldName" $fieldName}}
{{end}}
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations optionnelles</h3>
<p class="mt-1 text-sm text-gray-500">Autres informations de profil optionnelles</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
<div class="sm:mt-0 sm:col-span-2">
<select id="gender" name="gender" autocomplete="gender" x-model="gender"
class="p-2 max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-2xl">
<option value="0">Inconnu</option>
<option value="1">Masculin</option>
<option value="2">Féminin</option>
<option value="9">Sans objet</option>
</select>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<label for="last_subscription_date" class="block text-sm font-medium text-gray-700">Date de dernière adhésion</label>
<input type="date" name="last_subscription_date" id="last_subscription_date" autocomplete="last_subscription_date" placeholder="JJ/MM/AAAA"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.last_subscription_date">
</div>
<div class="col-span-6 sm:col-span-3">
<label for="comment" class="block text-sm font-medium text-gray-700">Commentaire</label>
<textarea name="comment" id="comment"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="other_properties.comment"></textarea>
</div>
<!-- <div class="col-span-3 sm:col-span-3">
<label class="block text-sm font-medium text-gray-700"> Photo </label>
<div class="mt-1 flex items-center space-x-5">
<span class="inline-block h-12 w-12 rounded-co overflow-hidden bg-gray-100">
{{.IconSet.Icon "img:profile-picture-placeholder" "h-full w-full"}}
</span>
<button type="button"
class="bg-white py-2 px-3 border border-gray-300 rounded-2xl shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">
Charger la photo
</button>
</div>
</div> -->
</div>
</div>
</div>
</div>
<!--<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Paramètres</h3>
<p class="mt-1 text-sm text-gray-500">Paramètres liés au conducteur, utiles pour exploiter les fonctionnalités de PARCOURSMOB</p>
</div>
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
{{ $fieldName := "address" }}
{{ template "address_autocomplete" dict "FieldName" $fieldName }}
</div>
</div>
</div>-->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/solidarity-transport/drivers/{{.ViewState.driver.ID}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Modifier le conducteur solidaire</button>
</div>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,99 @@
{{define "main"}}
<html class="h-full bg-gray-50">
<head>
<title>PARCOURSMOB</title>
<link rel="stylesheet" href="/public/css/main.css" />
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/maplibre-gl@^5.2.0/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@kingshott/iodine@8.1.0/dist/iodine.min.umd.js" defer></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@^5.2.0/dist/maplibre-gl.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pmtiles@^4.3.0/dist/pmtiles.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@protomaps/basemaps@5/dist/basemaps.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/polyline@0.2.0/src/polyline.js"></script>
<!--<script defer type="text/javascript" src="/public/js/main.js" defer></script>-->
</head>
<body class="h-full">
<div class="flex flex-col justify-center py-12">
<div class="">
<h2 class="m-4 mt-6 text-center text-3xl font-extrabold text-gray-900">Demande de trajet solidaire</h2>
<p class="m-4 text-center">
{{if eq .ViewState.booking.Status "WAITING_CONFIRMATION"}}
Vous avez une nouvelle demande de trajet
{{else if eq .ViewState.booking.Status "VALIDATED"}}
Demande déjà validée
{{else if eq .ViewState.booking.Status "CANCELLED"}}
Trajet annulé
{{end}}
</p>
</div>
<div class="h-50 w-full">
{{template "journey_map" (dict "driver_journey" .ViewState.booking.Journey "driver" .ViewState.driver "passenger" .ViewState.passenger "beneficiaries" .ViewState.beneficiaries)}}
</div>
<div class="grid grid-cols-1">
<div class="p-4 py-5 sm:px-6">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Type de de trajet</dt>
{{if .ViewState.booking.Journey.Noreturn}}<dd class="mt-1 text-gray-900">Aller simple (pas de retour)</dd>
{{else}}<dd class="mt-1 text-gray-900">Aller-retour ({{.ViewState.booking.ReturnWaitingDuration | shortDuration}} d'attente estimée sur place)</dd>{{end}}
</div>
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Passager</dt>
<dd class="mt-1 text-gray-900">{{.ViewState.passenger.Data.first_name}} {{.ViewState.passenger.Data.last_name}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Lieu de récupération du passager</dt>
<dd class="mt-1 text-gray-900">{{.ViewState.booking.Journey.PassengerPickup.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Lieu de destination</dt>
<dd class="mt-1 text-gray-900">{{.ViewState.booking.Journey.PassengerDrop.Properties.label}}</dd>
</div>
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Date et heure du rendez-vous</dt>
<dd class="mt-1 text-gray-900">{{ timeFormat .ViewState.booking.Journey.PassengerPickupDate "02/01/2006 15:04"}}</dd>
</div>
{{if eq .ViewState.booking.Status "VALIDATED"}}
<div class="sm:col-span-1">
<dt class="font-medium text-gray-500">Numéro de téléphone du passager</dt>
<dd class="mt-1 text-co-blue"><a href="tel:{{.ViewState.passenger.Data.phone_number}}">{{ .ViewState.passenger.Data.phone_number}}</a></dd>
</div>
{{end}}
</dl>
</div>
<div>
</div>
<div class="flex flex-col justify-center m-4 sm:flex-row">
{{if eq .ViewState.booking.Status "WAITING_CONFIRMATION"}}
<form method="POST">
<input type="hidden" name="action" value="confirm" />
{{template "submit_with_sms"
dict "IconSet" .IconSet
"Viewstate" .ViewState
"ComponentState" (dict "submitText" "Valider"
"headerText" "Validez le trajet"
"infoText" "Le message suivant sera envoyé à votre passager. Vous pouvez le personnaliser.")
"SMSState" (dict "name" (.ViewState.config.GetString "service_name")
"driver_first_name" .ViewState.driver.Data.first_name
"driver_last_name" .ViewState.driver.Data.last_name
"address" .ViewState.booking.Journey.PassengerDrop.Properties.label
"date" (timeFormat .ViewState.booking.Journey.PassengerPickupDate "02/01/2006 15:04")
"phone_number" .ViewState.driver.Data.phone_number)}}
</form>
<form method="POST">
<input type="hidden" name="action" value="cancel" />
<button type="submit"
class="m-4 inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-red px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-red focus:ring-offset-2 sm:w-auto">
Refuser
</button>
</form>
{{end}}
</div>
</div>
</body>
</html>
{{end}}

View File

@@ -0,0 +1,54 @@
{{ define "content" }}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Transport solidaire</h1>
<div class="bg-white shadow sm:rounded-lg sm:overflow-hidden my-4" x-data="{
tab: 'drivers',
to(event) {
this.tab = event.target.value
}
}">
<div class="divide-y divide-gray-200">
<div>
<div class="hidden sm:block">
<div class="border-b border-gray-200 pl-4">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<a href="#" @click="tab = 'drivers'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'drivers' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Conducteurs </a>
<!--<a href="#" @click="tab = 'beneficiaries'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'beneficiaries' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Bénéficiaires </a>-->
<a href="#" @click="tab = 'solidarityService'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'solidarityService' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Trajets </a>
<a href="#" @click="tab = 'solidarityHistory'"
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
:class="tab == 'solidarityHistory' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
Trajets passés </a>
</nav>
</div>
</div>
</div>
<div x-show="tab == 'drivers'">{{template "solidarity_drivers_list" .}}</div>
<div x-show="tab == 'beneficiaries'">1</div>
<div x-show="tab == 'solidarityService'">{{template "solidarity_bookings_list" .}}</div>
<div x-show="tab == 'solidarityHistory'">{{template "solidarity_bookings_history" .}}</div>
</div>
</div>
</div>
{{ end }}

View File

@@ -1,6 +1,17 @@
{{define "content"}}
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 space-y-6">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 space-y-6" x-data="{
comment: '',
init() {
let quill = new Quill(this.$refs.quill, { theme: 'snow', modules: { toolbar: [[{ 'header': [1, 2, 3, 4, 5, 6, false] }], ['bold', 'italic', 'underline'], ['link', 'image']] } })
quill.root.innerHTML = this.comment
quill.on('text-change', () => {
this.comment = quill.root.innerHTML
})
},
}">
<h1 class="text-2xl font-semibold text-gray-900">Demande de support technique</h1>
<div class="bg-white py-2 px-4 shadow sm:rounded-lg sm:px-10">
<p class="text-sm text-gray-600 p-4">
@@ -10,9 +21,11 @@
<div class="mb-4 w-full bg-gray-50 rounded-3xl border border-gray-200 dark:bg-gray-700 dark:border-gray-600">
<div class="py-2 px-4 rounded-3xl bg-white dark:bg-gray-800">
<label class="sr-only">Votre message</label>
<label class="sr-only">Votre message</label>
<textarea name="comment" rows="4" class="block w-full resize-none border-0 border-b border-transparent p-0 pb-2 focus:border-co-blue focus:ring-0 sm:text-sm" placeholder="Votre message..." required></textarea>
<div x-ref="quill" class="block w-full resize-none border-0 border-b border-transparent p-0 pb-2 focus:border-co-blue focus:ring-0 sm:text-sm"></div>
<input type="hidden" name="comment" x-model="comment" />
</div>
<div class="flex justify-center items-center py-2 px-3 border-t dark:border-gray-600">
<button type="submit" value="send message" class="px-2 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">

View File

@@ -198,4 +198,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,110 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Ajouter un diagnostic</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
name: null,
namespace: 'parcoursmob_bookings',
},
rules: {
name: ['required'],
namespace: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations obligatoires
pour créer un diagnostic dans PARCOURSMOB</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name" autocomplete="given-name"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
</div>
</div>
<input type="hidden" name="namespace" value="parcoursmob_bookings" />
<!-- <div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma JSON</h3>
<p class="mt-1 text-sm text-gray-500">Schéma JSON pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="json_schema" class="block text-sm font-medium text-gray-700">Schéma JSON</label>
<textarea name="json_schema" id="json_schema" autocomplete="json_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.json_schema" @blur="validateField('json_schema')"
:class="formValidation.fields.json_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma UI</h3>
<p class="mt-1 text-sm text-gray-500">Schéma UI pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="ui_schema" class="block text-sm font-medium text-gray-700">Schéma UI</label>
<textarea name="ui_schema" id="ui_schema" autocomplete="ui_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.ui_schema" @blur="validateField('ui_schema')"
:class="formValidation.fields.ui_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div> -->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/vehicles/bookings/{{.ViewState.booking}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Créer le diagnostique</button>
</div>
</form>
</div>
{{end}}

View File

@@ -147,6 +147,20 @@
<div class="lg:col-start-2 lg:col-span-2">
{{if .ViewState.searched}}
<div class="bg-white px-4 py-5 shadow sm:rounded-2xl sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Documents demandés</h2>
<table>
<tbody>
{{range .ViewState.search.mandatory_documents}}
<tr>
<td class="whitespace-nowrap py-4 px-3 text-sm text-gray-500">
{{index $.ViewState.search.file_types_map .}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="bg-white mt-8 px-4 py-5 shadow sm:rounded-2xl sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Véhicules disponibles</h2>
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">

View File

@@ -0,0 +1,56 @@
{{define "booking_diags"}}
{{ $calendarIcon := .IconSet.Icon "hero:outline/calendar" "h-6 w-6" }}
{{ $carIcon := .IconSet.Icon "tabler-icons:car" "h-6 w-6"}}
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 flex items-center space-x-5 sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Diagnostics réalisés</h2>
<a href="{{.ViewState.booking.ID}}/create-diag">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Créer un diagnostic
</button>
</a>
</div>
<div class="border-t border-gray-200">
{{ $diagCount := len .ViewState.diags }}
<ul role="list" class="divide-y divide-gray-200 flex-1">
{{if eq $diagCount 0}}
<li class="py-2 px-4">
<p class="py-5 mt-1 max-w-2xl text-sm text-gray-500">Aucun diagnostique effectué pour le moment.</p>
</li>
{{else}}
{{range .ViewState.diags}}
{{ $diags := .ID }}
{{if eq .Deleted false}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<a href="/app/diags/{{$diags}}" class="mt-1 text-sm text-gray-900">{{.Name}}</a>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
<a href="/app/diags/{{$diags}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le diagnostic</span></button>
</a>
</li>
{{end}}
{{if eq .Deleted true}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{.Name}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">Ce diagnostique a été retiré</p>
</div>
</li>
{{end}}
{{end}}
{{end}}
</ul>
</div>
</div>
{{end}}

View File

@@ -3,7 +3,7 @@
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<div class="overflow-hidden shadow md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
@@ -71,8 +71,11 @@
{{if .Data.administrator_unavailability}}
<div class="text-gray-900" ></div>
{{else}}
<div class="text-gray-900" ><img class="h-6 w-6 rounded-co"
src="/app/beneficiaries/{{.Driver}}/picture" alt=""></div>
<div class="text-gray-900" >
<!--<img class="h-6 w-6 rounded-co" src="/app/beneficiaries/{{.Driver}}/picture" alt="">-->
{{ (index $.ViewState.drivers_map .Driver).Data.first_name }}
{{ (index $.ViewState.drivers_map .Driver).Data.last_name }}
</div>
{{end}}
</td>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
@@ -98,4 +101,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,56 @@
{{define "vehicle_diags"}}
{{ $calendarIcon := .IconSet.Icon "hero:outline/calendar" "h-6 w-6" }}
{{ $carIcon := .IconSet.Icon "tabler-icons:car" "h-6 w-6"}}
<div class="bg-white shadow sm:rounded-lg">
<div class="px-4 py-5 flex items-center space-x-5 sm:px-6">
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Diagnostics réalisés</h2>
<a href="{{.ViewState.vehicle.ID}}/create-diag">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-blue px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-ci-blue focus:ring-offset-2 sm:w-auto">
{{$.IconSet.Icon "hero:outline/plus-circle" "h-5 w-5 mr-3"}}
Créer un diagnostic
</button>
</a>
</div>
<div class="border-t border-gray-200">
{{ $diagCount := len .ViewState.diags }}
<ul role="list" class="divide-y divide-gray-200 flex-1">
{{if eq $diagCount 0}}
<li class="py-2 px-4">
<p class="py-5 mt-1 max-w-2xl text-sm text-gray-500">Aucun diagnostique effectué pour le moment.</p>
</li>
{{else}}
{{range .ViewState.diags}}
{{ $diags := .ID }}
{{if eq .Deleted false}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<a href="app/diags/{{$diags}}" class="mt-1 text-sm text-gray-900">{{.Name}}</a>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
</div>
<a href="/app/diags/{{$diags}}" target="_blank">
<button type="button" class="inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-co-blue focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-30">Voir<span class="sr-only"> le diagnostic</span></button>
</a>
</li>
{{end}}
{{if eq .Deleted true}}
<li class="py-5 px-4 flex">
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{.Name}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">{{(timeFrom .Diagdate).Format "02/01/2006"}}</p>
</div>
<div class="flex-1 ml-3">
<p class="mt-1 text-sm text-gray-900">Ce diagnostique a été retiré</p>
</div>
</li>
{{end}}
{{end}}
{{end}}
</ul>
</div>
</div>
{{end}}

View File

@@ -3,7 +3,7 @@
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<div class="overflow-hidden shadow md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
@@ -121,4 +121,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -22,14 +22,13 @@
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">
Changer de véhicule
</button>
<a href="/app/vehicles-management/bookings/{{.ViewState.booking.ID}}/delete">
<a href="/app/vehicles-management/bookings/{{.ViewState.booking.ID}}/delete">
<button type="button"
class="inline-flex items-center justify-center rounded-2xl border border-transparent bg-co-red px-4 py-2 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:ring-co-red focus:ring-offset-2 sm:w-auto">
Annuler
</button>
</a>
{{end}}
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true" x-show="changeVehicle">
<div class="fixed inset-0 bg-gray-900 bg-opacity-30 transition-opacity"></div>
@@ -127,6 +126,9 @@
</div>
</div>
</div>
<!--<div>
{{template "booking_diags" .}}
</div>-->
</div>
<div class="lg:col-start-2 lg:col-span-2">
<div class="bg-white shadow sm:rounded-2xl sm:px-6">
@@ -187,6 +189,11 @@
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
{{.ViewState.vehicle.Data.licence_plate}}</dd>
</div>
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium text-gray-500">Kilométrage</dt>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
{{.ViewState.vehicle.Data.kilometers}} km</dd>
</div>
<div class="py-4 sm:py-5 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="text-sm font-medium text-gray-500">Type</dt>
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
@@ -342,4 +349,4 @@
</div>
</div>
</div>
{{end}}
{{end}}

View File

@@ -0,0 +1,116 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Ajouter un diagnostic</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
name: null,
namespace: 'parcoursmob_bookings',
json_schema: null,
ui_schema: null,
},
rules: {
name: ['required']
namespace: ['required']
json_schema: ['required']
ui_schema: ['required']
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
json_schema: {valid: null},
ui_schema: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations obligatoires
pour créer un diagnostic dans PARCOURSMOB</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name" autocomplete="given-name"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
</div>
</div>
<input type="hidden" name="namespace" value="parcoursmob_bookings" />
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma JSON</h3>
<p class="mt-1 text-sm text-gray-500">Schéma JSON pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="json_schema" class="block text-sm font-medium text-gray-700">Schéma JSON</label>
<textarea name="json_schema" id="json_schema" autocomplete="json_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.json_schema" @blur="validateField('json_schema')"
:class="formValidation.fields.json_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma UI</h3>
<p class="mt-1 text-sm text-gray-500">Schéma UI pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="ui_schema" class="block text-sm font-medium text-gray-700">Schéma UI</label>
<textarea name="ui_schema" id="ui_schema" autocomplete="ui_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.ui_schema" @blur="validateField('ui_schema')"
:class="formValidation.fields.ui_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/vehicles-management/bookings/{{.ViewState.booking}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Créer le diagnostique</button>
</div>
</form>
</div>
{{end}}

View File

@@ -0,0 +1,110 @@
{{define "content"}}
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 class="text-2xl font-semibold text-gray-900">Ajouter un diagnostic</h1>
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
x-data="{
fields: {
name: null,
namespace: 'parcoursmob_vehicles',
},
rules: {
name: ['required'],
namespace: ['required'],
},
formValidation: {
valid: false,
fields: {
name: {valid: null},
}
},
isFormValid: true,
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
validateField(field) {
this.formValidation.fields[field] = Iodine.assert(this.fields[field], this.rules[field])
},
submit(event) {
this.validate()
if(!this.formValidation.valid) {
this.isFormValid = false
event.preventDefault()
}
return this.formValidation.valid
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations obligatoires</h3>
<p class="mt-1 text-sm text-gray-500">Informations obligatoires
pour créer un diagnostic dans PARCOURSMOB</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="name" class="block text-sm font-medium text-gray-700">Nom</label>
<input type="text" name="name" id="name" autocomplete="given-name"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.name" @blur="validateField('name')"
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
</div>
</div>
</div>
</div>
<input type="hidden" name="namespace" value="parcoursmob_vehicles" />
<!-- <div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma JSON</h3>
<p class="mt-1 text-sm text-gray-500">Schéma JSON pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="json_schema" class="block text-sm font-medium text-gray-700">Schéma JSON</label>
<textarea name="json_schema" id="json_schema" autocomplete="json_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.json_schema" @blur="validateField('json_schema')"
:class="formValidation.fields.json_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6 mt-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Schéma UI</h3>
<p class="mt-1 text-sm text-gray-500">Schéma UI pour le diagnostic</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="ui_schema" class="block text-sm font-medium text-gray-700">Schéma UI</label>
<textarea name="ui_schema" id="ui_schema" autocomplete="ui_schema"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.ui_schema" @blur="validateField('ui_schema')"
:class="formValidation.fields.ui_schema.valid == false ? 'border-co-red border-2' : 'border-gray-300'"></textarea>
</div>
</div>
</div>
</div>
</div> -->
<div class="flex justify-end">
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
<a href="/app/vehicles-management/fleet/{{.ViewState.vehicle}}">
<button type="button"
class="bg-white py-2 px-4 border border-gray-300 rounded-2xl shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Annuler</button>
</a>
<button type="submit"
class="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-2xl text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-co-blue">Créer le diagnostique</button>
</div>
</form>
</div>
{{end}}

View File

@@ -10,11 +10,13 @@
licence_plate: null,
name: null,
type: null,
informations: '',
},
rules: {
licence_plate: ['required'], // 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'
name: ['required'],
type: ['required'],
informations: ['optional']
},
formValidation: {
valid: false,
@@ -22,9 +24,19 @@
name: {valid: null},
licence_plate: {valid: null},
type: {valid: null},
informations: {valid: null},
}
},
isFormValid: true,
init() {
let quill = new Quill(this.$refs.quill, { theme: 'snow', modules: { toolbar: [[{ 'header': [1, 2, 3, 4, 5, 6, false] }], ['bold', 'italic', 'underline'], ['link', 'image']] } })
quill.root.innerHTML = this.fields.informations
quill.on('text-change', () => {
this.fields.informations = quill.root.innerHTML
})
},
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
@@ -94,6 +106,13 @@
@blur="fields.licence_plate = fields.licence_plate.toUpperCase(); validateField('licence_plate')"
:class="formValidation.fields.licence_plate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<!--<div class="col-span-3">
<label for="kilometers"
class="block text-sm font-medium text-gray-700">Kilométrage</label>
<input type="text" name="kilometers" id="kilometers" placeholder="1 km"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.kilometers">
</div>-->
</div>
</div>
</div>
@@ -111,8 +130,8 @@
<div class="mt-5">
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques</label>
<div class="mt-1">
<textarea rows="4" name="informations" id="informations"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>
<div x-ref="quill" class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-b-2xl"></div>
<input type="hidden" name="informations" x-model="fields.informations" />
</div>
</div>
</div>
@@ -131,4 +150,4 @@
</div>
</form>
</div>
{{end}}
{{end}}

View File

@@ -16,8 +16,8 @@
<div class="mt-6 flex flex-col-reverse justify-stretch space-y-4 space-y-reverse sm:flex-row-reverse sm:justify-end sm:space-x-reverse sm:space-y-0 sm:space-x-3 md:mt-0 md:flex-row md:space-x-3">
<button type="button" @click="dialog = !dialog"
class="inline-flex items-center justify-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-offset-gray-100 focus:ring-co-blue">Retirer de la flotte</button>
<!-- <a href="/app/vehicles-management/fleet/{{.ViewState.vehicle.ID}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a> -->
<a href="/app/vehicles-management/fleet/{{.ViewState.vehicle.ID}}/update" class="inline-flex"><button type="button"
class="w-full px-4 py-2 border border-transparent text-sm font-medium rounded-2xl shadow-sm text-white bg-co-blue hover:bg-co-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-co-blue">Modifier</button></a>
</div>
</div>
<div class="mt-8 max-w-3xl mx-auto grid grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-3">
@@ -54,6 +54,12 @@
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.vehicle.Data.licence_plate}}</dd>
</div>
{{end}}
{{if .ViewState.vehicle.Data.kilometers}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Kilométrage</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.vehicle.Data.kilometers}} km</dd>
</div>
{{end}}
{{if .ViewState.vehicle.Data.address}}
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Lieu</dt>
@@ -63,7 +69,7 @@
{{if .ViewState.vehicle.Data.informations}}
<div class="sm:col-span-2">
<dt class="text-sm font-medium text-gray-500">Informations pratiques pour le bénéficiaire</dt>
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.vehicle.Data.informations}}</dd>
<dd class="mt-1 text-sm text-gray-900">{{unescapeHTML .ViewState.vehicle.Data.informations}}</dd>
</div>
{{end}}
@@ -132,8 +138,10 @@
{{else}}
<div class="text-gray-900" >
<a href="/app/beneficiaries/{{.Driver}}">
<img class="h-6 w-6 rounded-co"
src="/app/beneficiaries/{{.Driver}}/picture" alt="">
<!--<img class="h-6 w-6 rounded-co"
src="/app/beneficiaries/{{.Driver}}/picture" alt="">-->
{{(index $.ViewState.beneficiaries .Driver).Data.first_name}}
{{(index $.ViewState.beneficiaries .Driver).Data.last_name}}
</a>
</div>
{{end}}
@@ -238,4 +246,4 @@
</form>
</div>
</div>
{{end}}
{{end}}

View File

@@ -9,6 +9,8 @@
fields: {
licence_plate: '{{ .ViewState.vehicle.Data.licence_plate }}',
name: '{{ .ViewState.vehicle.Data.name }}',
kilometers: '{{ .ViewState.vehicle.Data.kilometers }}',
informations: '{{ .ViewState.vahicle.Data.informations}}',
},
rules: {
licence_plate: ['required', 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'],
@@ -22,6 +24,15 @@
}
},
isFormValid: true,
init() {
let quill = new Quill(this.$refs.quill, { theme: 'snow', modules: { toolbar: [[{ 'header': [1, 2, 3, 4, 5, 6, false] }], ['bold', 'italic', 'underline'], ['link', 'image']] } })
quill.root.innerHTML = this.fields.informations
quill.on('text-change', () => {
this.fields.informations = quill.root.innerHTML
})
},
validate() {
this.formValidation = Iodine.assert(this.fields, this.rules)
},
@@ -35,7 +46,12 @@
event.preventDefault()
}
return this.formValidation.valid
},
displayAutomatic(type) {
return type == 'Voiture'
}
}">
<form class="space-y-6" method="POST" @submit="submit">
<div class="bg-white shadow px-4 py-5 sm:rounded-lg sm:p-6">
@@ -55,7 +71,30 @@
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<div class="col-span-3">
{{template "vehicle_type_select" .}}
<div class="col-span-3">
<label for="type" class="block text-sm font-medium text-gray-700">Type de véhicule</label>
<select id="type" name="type"
x-model="fields.type" @blur="validateField('type')"
class="max-w-lg mt-1 block focus:ring-co-blue focus:border-co-blue w-full shadow-sm sm:max-w-xs sm:text-sm rounded-2xl"
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
<option value="" selected></option>
{{range .ViewState.vehicle_types}}
<option value="{{.}}">{{.}}</option>
{{end}}
</select>
<div class="m-4" x-show="displayAutomatic(fields.type)">
<legend class="sr-only">Automatique</legend>
<div class="relative flex items-start">
<div class="flex h-5 items-center">
<input id="automatic" aria-describedby="automatic-description" name="automatic" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-co-blue focus:ring-co-blue">
</div>
<div class="ml-3 text-sm">
<label for="automatic" class="font-medium text-gray-700">Automatique</label>
<p id="automatic-description" class="text-gray-500">Ce véhicule a une boite automatique.</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-span-3">
<label for="licence_plate"
@@ -66,6 +105,15 @@
@blur="fields.licence_plate = fields.licence_plate.toUpperCase(); validateField('licence_plate')"
:class="formValidation.fields.licence_plate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>
<!--<div class="col-span-3">
<label for="kilometers"
class="block text-sm font-medium text-gray-700">Kilométrage</label>
<input type="text" name="kilometers" id="kilometers" placeholder="1200 km"
class="mt-1 focus:ring-co-blue focus:border-co-blue block w-full shadow-sm sm:text-sm rounded-2xl"
x-model="fields.kilometers"
@blur="fields.kilometers; validateField('kilometers')"
:class="formValidation.fields.kilometers.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
</div>-->
</div>
</div>
</div>
@@ -88,8 +136,8 @@
<div class="mt-5">
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques pour le bénéficiaire</label>
<div class="mt-1">
<textarea rows="4" name="informations" id="informations"
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>
<div x-ref="quill" class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-b-2xl"></div>
<input type="hidden" name="informations" x-model="fields.informations" />
</div>
</div>
</div>
@@ -108,4 +156,4 @@
</div>
</form>
</div>
{{end}}
{{end}}

1
web/node_modules/.bin/detect-libc generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../detect-libc/bin/detect-libc.js

1
web/node_modules/.bin/jiti generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../jiti/lib/jiti-cli.mjs

1
web/node_modules/.bin/tailwindcss generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../@tailwindcss/cli/dist/index.mjs

401
web/node_modules/.package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,401 @@
{
"name": "web",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@tailwindcss/cli": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.0.9.tgz",
"integrity": "sha512-obJvIxu4SCA3PLQYDB7tz9Biv3LFB6+YM/DXNNqwjEMRBNr7Y7LLBk3Cl6xwM+/TxJlA2rEV/t+XwkbldcxeXA==",
"license": "MIT",
"dependencies": {
"@parcel/watcher": "^2.5.1",
"@tailwindcss/node": "4.0.9",
"@tailwindcss/oxide": "4.0.9",
"enhanced-resolve": "^5.18.1",
"lightningcss": "^1.29.1",
"mri": "^1.2.0",
"picocolors": "^1.1.1",
"tailwindcss": "4.0.9"
},
"bin": {
"tailwindcss": "dist/index.mjs"
}
},
"node_modules/@tailwindcss/node": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.9.tgz",
"integrity": "sha512-tOJvdI7XfJbARYhxX+0RArAhmuDcczTC46DGCEziqxzzbIaPnfYaIyRT31n4u8lROrsO7Q6u/K9bmQHL2uL1bQ==",
"license": "MIT",
"dependencies": {
"enhanced-resolve": "^5.18.1",
"jiti": "^2.4.2",
"tailwindcss": "4.0.9"
}
},
"node_modules/@tailwindcss/oxide": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.9.tgz",
"integrity": "sha512-eLizHmXFqHswJONwfqi/WZjtmWZpIalpvMlNhTM99/bkHtUs6IqgI1XQ0/W5eO2HiRQcIlXUogI2ycvKhVLNcA==",
"license": "MIT",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@tailwindcss/oxide-android-arm64": "4.0.9",
"@tailwindcss/oxide-darwin-arm64": "4.0.9",
"@tailwindcss/oxide-darwin-x64": "4.0.9",
"@tailwindcss/oxide-freebsd-x64": "4.0.9",
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.9",
"@tailwindcss/oxide-linux-arm64-gnu": "4.0.9",
"@tailwindcss/oxide-linux-arm64-musl": "4.0.9",
"@tailwindcss/oxide-linux-x64-gnu": "4.0.9",
"@tailwindcss/oxide-linux-x64-musl": "4.0.9",
"@tailwindcss/oxide-win32-arm64-msvc": "4.0.9",
"@tailwindcss/oxide-win32-x64-msvc": "4.0.9"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.9.tgz",
"integrity": "sha512-v0D8WqI/c3WpWH1kq/HP0J899ATLdGZmENa2/emmNjubT0sWtEke9W9+wXeEoACuGAhF9i3PO5MeyditpDCiWQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.9.tgz",
"integrity": "sha512-Kvp0TCkfeXyeehqLJr7otsc4hd/BUPfcIGrQiwsTVCfaMfjQZCG7DjI+9/QqPZha8YapLA9UoIcUILRYO7NE1Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"license": "Apache-2.0",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
"integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/jiti": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/lightningcss": {
"version": "1.29.1",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz",
"integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==",
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^1.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-darwin-arm64": "1.29.1",
"lightningcss-darwin-x64": "1.29.1",
"lightningcss-freebsd-x64": "1.29.1",
"lightningcss-linux-arm-gnueabihf": "1.29.1",
"lightningcss-linux-arm64-gnu": "1.29.1",
"lightningcss-linux-arm64-musl": "1.29.1",
"lightningcss-linux-x64-gnu": "1.29.1",
"lightningcss-linux-x64-musl": "1.29.1",
"lightningcss-win32-arm64-msvc": "1.29.1",
"lightningcss-win32-x64-msvc": "1.29.1"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.29.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz",
"integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.29.1",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz",
"integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tailwindcss": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.9.tgz",
"integrity": "sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==",
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
}
}
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1 @@
This is the linux-x64-glibc build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.

View File

@@ -0,0 +1,33 @@
{
"name": "@parcel/watcher-linux-x64-glibc",
"version": "2.5.1",
"main": "watcher.node",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/watcher.git"
},
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"files": [
"watcher.node"
],
"engines": {
"node": ">= 10.0.0"
},
"os": [
"linux"
],
"cpu": [
"x64"
],
"libc": [
"glibc"
]
}

Binary file not shown.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1 @@
This is the linux-x64-musl build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.

View File

@@ -0,0 +1,33 @@
{
"name": "@parcel/watcher-linux-x64-musl",
"version": "2.5.1",
"main": "watcher.node",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/watcher.git"
},
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"files": [
"watcher.node"
],
"engines": {
"node": ">= 10.0.0"
},
"os": [
"linux"
],
"cpu": [
"x64"
],
"libc": [
"musl"
]
}

Binary file not shown.

21
web/node_modules/@parcel/watcher/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

135
web/node_modules/@parcel/watcher/README.md generated vendored Normal file
View File

@@ -0,0 +1,135 @@
# @parcel/watcher
A native C++ Node module for querying and subscribing to filesystem events. Used by [Parcel 2](https://github.com/parcel-bundler/parcel).
## Features
- **Watch** - subscribe to realtime recursive directory change notifications when files or directories are created, updated, or deleted.
- **Query** - performantly query for historical change events in a directory, even when your program is not running.
- **Native** - implemented in C++ for performance and low-level integration with the operating system.
- **Cross platform** - includes backends for macOS, Linux, Windows, FreeBSD, and Watchman.
- **Performant** - events are throttled in C++ so the JavaScript thread is not overwhelmed during large filesystem changes (e.g. `git checkout` or `npm install`).
- **Scalable** - tens of thousands of files can be watched or queried at once with good performance.
## Example
```javascript
const watcher = require('@parcel/watcher');
const path = require('path');
// Subscribe to events
let subscription = await watcher.subscribe(process.cwd(), (err, events) => {
console.log(events);
});
// later on...
await subscription.unsubscribe();
// Get events since some saved snapshot in the past
let snapshotPath = path.join(process.cwd(), 'snapshot.txt');
let events = await watcher.getEventsSince(process.cwd(), snapshotPath);
// Save a snapshot for later
await watcher.writeSnapshot(process.cwd(), snapshotPath);
```
## Watching
`@parcel/watcher` supports subscribing to realtime notifications of changes in a directory. It works recursively, so changes in sub-directories will also be emitted.
Events are throttled and coalesced for performance during large changes like `git checkout` or `npm install`, and a single notification will be emitted with all of the events at the end.
Only one notification will be emitted per file. For example, if a file was both created and updated since the last event, you'll get only a `create` event. If a file is both created and deleted, you will not be notifed of that file. Renames cause two events: a `delete` for the old name, and a `create` for the new name.
```javascript
let subscription = await watcher.subscribe(process.cwd(), (err, events) => {
console.log(events);
});
```
Events have two properties:
- `type` - the event type: `create`, `update`, or `delete`.
- `path` - the absolute path to the file or directory.
To unsubscribe from change notifications, call the `unsubscribe` method on the returned subscription object.
```javascript
await subscription.unsubscribe();
```
`@parcel/watcher` has the following watcher backends, listed in priority order:
- [FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS
- [Watchman](https://facebook.github.io/watchman/) if installed
- [inotify](http://man7.org/linux/man-pages/man7/inotify.7.html) on Linux
- [ReadDirectoryChangesW](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v%3Dvs.85%29.aspx) on Windows
- [kqueue](https://man.freebsd.org/cgi/man.cgi?kqueue) on FreeBSD, or as an alternative to FSEvents on macOS
You can specify the exact backend you wish to use by passing the `backend` option. If that backend is not available on the current platform, the default backend will be used instead. See below for the list of backend names that can be passed to the options.
## Querying
`@parcel/watcher` also supports querying for historical changes made in a directory, even when your program is not running. This makes it easy to invalidate a cache and re-build only the files that have changed, for example. It can be **significantly** faster than traversing the entire filesystem to determine what files changed, depending on the platform.
In order to query for historical changes, you first need a previous snapshot to compare to. This can be saved to a file with the `writeSnapshot` function, e.g. just before your program exits.
```javascript
await watcher.writeSnapshot(dirPath, snapshotPath);
```
When your program starts up, you can query for changes that have occurred since that snapshot using the `getEventsSince` function.
```javascript
let events = await watcher.getEventsSince(dirPath, snapshotPath);
```
The events returned are exactly the same as the events that would be passed to the `subscribe` callback (see above).
`@parcel/watcher` has the following watcher backends, listed in priority order:
- [FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS
- [Watchman](https://facebook.github.io/watchman/) if installed
- [fts](http://man7.org/linux/man-pages/man3/fts.3.html) (brute force) on Linux and FreeBSD
- [FindFirstFile](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilea) (brute force) on Windows
The FSEvents (macOS) and Watchman backends are significantly more performant than the brute force backends used by default on Linux and Windows, for example returning results in miliseconds instead of seconds for large directory trees. This is because a background daemon monitoring filesystem changes on those platforms allows us to query cached data rather than traversing the filesystem manually (brute force).
macOS has good performance with FSEvents by default. For the best performance on other platforms, install [Watchman](https://facebook.github.io/watchman/) and it will be used by `@parcel/watcher` automatically.
You can specify the exact backend you wish to use by passing the `backend` option. If that backend is not available on the current platform, the default backend will be used instead. See below for the list of backend names that can be passed to the options.
## Options
All of the APIs in `@parcel/watcher` support the following options, which are passed as an object as the last function argument.
- `ignore` - an array of paths or glob patterns to ignore. uses [`is-glob`](https://github.com/micromatch/is-glob) to distinguish paths from globs. glob patterns are parsed with [`micromatch`](https://github.com/micromatch/micromatch) (see [features](https://github.com/micromatch/micromatch#matching-features)).
- paths can be relative or absolute and can either be files or directories. No events will be emitted about these files or directories or their children.
- glob patterns match on relative paths from the root that is watched. No events will be emitted for matching paths.
- `backend` - the name of an explicitly chosen backend to use. Allowed options are `"fs-events"`, `"watchman"`, `"inotify"`, `"kqueue"`, `"windows"`, or `"brute-force"` (only for querying). If the specified backend is not available on the current platform, the default backend will be used instead.
## WASM
The `@parcel/watcher-wasm` package can be used in place of `@parcel/watcher` on unsupported platforms. It relies on the Node `fs` module, so in non-Node environments such as browsers, an `fs` polyfill will be needed.
**Note**: the WASM implementation is significantly less efficient than the native implementations because it must crawl the file system to watch each directory individually. Use the native `@parcel/watcher` package wherever possible.
```js
import {subscribe} from '@parcel/watcher-wasm';
// Use the module as documented above.
subscribe(/* ... */);
```
## Who is using this?
- [Parcel 2](https://parceljs.org/)
- [VSCode](https://code.visualstudio.com/updates/v1_62#_file-watching-changes)
- [Tailwind CSS Intellisense](https://github.com/tailwindlabs/tailwindcss-intellisense)
- [Gatsby Cloud](https://twitter.com/chatsidhartha/status/1435647412828196867)
- [Nx](https://nx.dev)
- [Nuxt](https://nuxt.com)
## License
MIT

93
web/node_modules/@parcel/watcher/binding.gyp generated vendored Normal file
View File

@@ -0,0 +1,93 @@
{
"targets": [
{
"target_name": "watcher",
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
"sources": [ "src/binding.cc", "src/Watcher.cc", "src/Backend.cc", "src/DirTree.cc", "src/Glob.cc", "src/Debounce.cc" ],
"include_dirs" : ["<!(node -p \"require('node-addon-api').include_dir\")"],
'cflags!': [ '-fno-exceptions', '-std=c++17' ],
'cflags_cc!': [ '-fno-exceptions', '-std=c++17' ],
"conditions": [
['OS=="mac"', {
"sources": [
"src/watchman/BSER.cc",
"src/watchman/WatchmanBackend.cc",
"src/shared/BruteForceBackend.cc",
"src/unix/fts.cc",
"src/macos/FSEventsBackend.cc",
"src/kqueue/KqueueBackend.cc"
],
"link_settings": {
"libraries": ["CoreServices.framework"]
},
"defines": [
"WATCHMAN",
"BRUTE_FORCE",
"FS_EVENTS",
"KQUEUE"
],
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES"
}
}],
['OS=="mac" and target_arch=="arm64"', {
"xcode_settings": {
"ARCHS": ["arm64"]
}
}],
['OS=="linux" or OS=="android"', {
"sources": [
"src/watchman/BSER.cc",
"src/watchman/WatchmanBackend.cc",
"src/shared/BruteForceBackend.cc",
"src/linux/InotifyBackend.cc",
"src/unix/legacy.cc"
],
"defines": [
"WATCHMAN",
"INOTIFY",
"BRUTE_FORCE"
]
}],
['OS=="win"', {
"sources": [
"src/watchman/BSER.cc",
"src/watchman/WatchmanBackend.cc",
"src/shared/BruteForceBackend.cc",
"src/windows/WindowsBackend.cc",
"src/windows/win_utils.cc"
],
"defines": [
"WATCHMAN",
"WINDOWS",
"BRUTE_FORCE"
],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1, # /EHsc
"AdditionalOptions": ['-std:c++17']
}
}
}],
['OS=="freebsd"', {
"sources": [
"src/watchman/BSER.cc",
"src/watchman/WatchmanBackend.cc",
"src/shared/BruteForceBackend.cc",
"src/unix/fts.cc",
"src/kqueue/KqueueBackend.cc"
],
"defines": [
"WATCHMAN",
"BRUTE_FORCE",
"KQUEUE"
]
}]
]
}
],
"variables": {
"openssl_fips": "",
"node_use_dtrace": "false"
}
}

49
web/node_modules/@parcel/watcher/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,49 @@
declare type FilePath = string;
declare type GlobPattern = string;
declare namespace ParcelWatcher {
export type BackendType =
| 'fs-events'
| 'watchman'
| 'inotify'
| 'windows'
| 'brute-force';
export type EventType = 'create' | 'update' | 'delete';
export interface Options {
ignore?: (FilePath|GlobPattern)[];
backend?: BackendType;
}
export type SubscribeCallback = (
err: Error | null,
events: Event[]
) => unknown;
export interface AsyncSubscription {
unsubscribe(): Promise<void>;
}
export interface Event {
path: FilePath;
type: EventType;
}
export function getEventsSince(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<Event[]>;
export function subscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<AsyncSubscription>;
export function unsubscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<void>;
export function writeSnapshot(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<FilePath>;
}
export = ParcelWatcher;

41
web/node_modules/@parcel/watcher/index.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
const {createWrapper} = require('./wrapper');
let name = `@parcel/watcher-${process.platform}-${process.arch}`;
if (process.platform === 'linux') {
const { MUSL, family } = require('detect-libc');
if (family === MUSL) {
name += '-musl';
} else {
name += '-glibc';
}
}
let binding;
try {
binding = require(name);
} catch (err) {
handleError(err);
try {
binding = require('./build/Release/watcher.node');
} catch (err) {
handleError(err);
try {
binding = require('./build/Debug/watcher.node');
} catch (err) {
handleError(err);
throw new Error(`No prebuild or local build of @parcel/watcher found. Tried ${name}. Please ensure it is installed (don't use --no-optional when installing with npm). Otherwise it is possible we don't support your platform yet. If this is the case, please report an issue to https://github.com/parcel-bundler/watcher.`);
}
}
}
function handleError(err) {
if (err?.code !== 'MODULE_NOT_FOUND') {
throw err;
}
}
const wrapper = createWrapper(binding);
exports.writeSnapshot = wrapper.writeSnapshot;
exports.getEventsSince = wrapper.getEventsSince;
exports.subscribe = wrapper.subscribe;
exports.unsubscribe = wrapper.unsubscribe;

48
web/node_modules/@parcel/watcher/index.js.flow generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// @flow
declare type FilePath = string;
declare type GlobPattern = string;
export type BackendType =
| 'fs-events'
| 'watchman'
| 'inotify'
| 'windows'
| 'brute-force';
export type EventType = 'create' | 'update' | 'delete';
export interface Options {
ignore?: Array<FilePath | GlobPattern>,
backend?: BackendType
}
export type SubscribeCallback = (
err: ?Error,
events: Array<Event>
) => mixed;
export interface AsyncSubscription {
unsubscribe(): Promise<void>
}
export interface Event {
path: FilePath,
type: EventType
}
declare module.exports: {
getEventsSince(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<Array<Event>>,
subscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<AsyncSubscription>,
unsubscribe(
dir: FilePath,
fn: SubscribeCallback,
opts?: Options
): Promise<void>,
writeSnapshot(
dir: FilePath,
snapshot: FilePath,
opts?: Options
): Promise<FilePath>
}

88
web/node_modules/@parcel/watcher/package.json generated vendored Normal file
View File

@@ -0,0 +1,88 @@
{
"name": "@parcel/watcher",
"version": "2.5.1",
"main": "index.js",
"types": "index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/watcher.git"
},
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"files": [
"index.js",
"index.js.flow",
"index.d.ts",
"wrapper.js",
"package.json",
"README.md",
"LICENSE",
"src",
"scripts/build-from-source.js",
"binding.gyp"
],
"scripts": {
"prebuild": "prebuildify --napi --strip --tag-libc",
"format": "prettier --write \"./**/*.{js,json,md}\"",
"build": "node-gyp rebuild",
"install": "node scripts/build-from-source.js",
"test": "mocha"
},
"engines": {
"node": ">= 10.0.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,json,md}": [
"prettier --write",
"git add"
]
},
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"devDependencies": {
"esbuild": "^0.19.8",
"fs-extra": "^10.0.0",
"husky": "^7.0.2",
"lint-staged": "^11.1.2",
"mocha": "^9.1.1",
"napi-wasm": "^1.1.0",
"prebuildify": "^6.0.1",
"prettier": "^2.3.2"
},
"binary": {
"napi_versions": [
3
]
},
"optionalDependencies": {
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1"
}
}

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env node
const {spawn} = require('child_process');
if (process.env.npm_config_build_from_source === 'true') {
build();
}
function build() {
spawn('node-gyp', ['rebuild'], { stdio: 'inherit', shell: true }).on('exit', function (code) {
process.exit(code);
});
}

182
web/node_modules/@parcel/watcher/src/Backend.cc generated vendored Normal file
View File

@@ -0,0 +1,182 @@
#ifdef FS_EVENTS
#include "macos/FSEventsBackend.hh"
#endif
#ifdef WATCHMAN
#include "watchman/WatchmanBackend.hh"
#endif
#ifdef WINDOWS
#include "windows/WindowsBackend.hh"
#endif
#ifdef INOTIFY
#include "linux/InotifyBackend.hh"
#endif
#ifdef KQUEUE
#include "kqueue/KqueueBackend.hh"
#endif
#ifdef __wasm32__
#include "wasm/WasmBackend.hh"
#endif
#include "shared/BruteForceBackend.hh"
#include "Backend.hh"
#include <unordered_map>
static std::unordered_map<std::string, std::shared_ptr<Backend>> sharedBackends;
std::shared_ptr<Backend> getBackend(std::string backend) {
// Use FSEvents on macOS by default.
// Use watchman by default if available on other platforms.
// Fall back to brute force.
#ifdef FS_EVENTS
if (backend == "fs-events" || backend == "default") {
return std::make_shared<FSEventsBackend>();
}
#endif
#ifdef WATCHMAN
if ((backend == "watchman" || backend == "default") && WatchmanBackend::checkAvailable()) {
return std::make_shared<WatchmanBackend>();
}
#endif
#ifdef WINDOWS
if (backend == "windows" || backend == "default") {
return std::make_shared<WindowsBackend>();
}
#endif
#ifdef INOTIFY
if (backend == "inotify" || backend == "default") {
return std::make_shared<InotifyBackend>();
}
#endif
#ifdef KQUEUE
if (backend == "kqueue" || backend == "default") {
return std::make_shared<KqueueBackend>();
}
#endif
#ifdef __wasm32__
if (backend == "wasm" || backend == "default") {
return std::make_shared<WasmBackend>();
}
#endif
if (backend == "brute-force" || backend == "default") {
return std::make_shared<BruteForceBackend>();
}
return nullptr;
}
std::shared_ptr<Backend> Backend::getShared(std::string backend) {
auto found = sharedBackends.find(backend);
if (found != sharedBackends.end()) {
return found->second;
}
auto result = getBackend(backend);
if (!result) {
return getShared("default");
}
result->run();
sharedBackends.emplace(backend, result);
return result;
}
void removeShared(Backend *backend) {
for (auto it = sharedBackends.begin(); it != sharedBackends.end(); it++) {
if (it->second.get() == backend) {
sharedBackends.erase(it);
break;
}
}
// Free up memory.
if (sharedBackends.size() == 0) {
sharedBackends.rehash(0);
}
}
void Backend::run() {
#ifndef __wasm32__
mThread = std::thread([this] () {
try {
start();
} catch (std::exception &err) {
handleError(err);
}
});
if (mThread.joinable()) {
mStartedSignal.wait();
}
#else
try {
start();
} catch (std::exception &err) {
handleError(err);
}
#endif
}
void Backend::notifyStarted() {
mStartedSignal.notify();
}
void Backend::start() {
notifyStarted();
}
Backend::~Backend() {
#ifndef __wasm32__
// Wait for thread to stop
if (mThread.joinable()) {
// If the backend is being destroyed from the thread itself, detach, otherwise join.
if (mThread.get_id() == std::this_thread::get_id()) {
mThread.detach();
} else {
mThread.join();
}
}
#endif
}
void Backend::watch(WatcherRef watcher) {
std::unique_lock<std::mutex> lock(mMutex);
auto res = mSubscriptions.find(watcher);
if (res == mSubscriptions.end()) {
try {
this->subscribe(watcher);
mSubscriptions.insert(watcher);
} catch (std::exception &err) {
unref();
throw;
}
}
}
void Backend::unwatch(WatcherRef watcher) {
std::unique_lock<std::mutex> lock(mMutex);
size_t deleted = mSubscriptions.erase(watcher);
if (deleted > 0) {
this->unsubscribe(watcher);
unref();
}
}
void Backend::unref() {
if (mSubscriptions.size() == 0) {
removeShared(this);
}
}
void Backend::handleWatcherError(WatcherError &err) {
unwatch(err.mWatcher);
err.mWatcher->notifyError(err);
}
void Backend::handleError(std::exception &err) {
std::unique_lock<std::mutex> lock(mMutex);
for (auto it = mSubscriptions.begin(); it != mSubscriptions.end(); it++) {
(*it)->notifyError(err);
}
removeShared(this);
}

37
web/node_modules/@parcel/watcher/src/Backend.hh generated vendored Normal file
View File

@@ -0,0 +1,37 @@
#ifndef BACKEND_H
#define BACKEND_H
#include "Event.hh"
#include "Watcher.hh"
#include "Signal.hh"
#include <thread>
class Backend {
public:
virtual ~Backend();
void run();
void notifyStarted();
virtual void start();
virtual void writeSnapshot(WatcherRef watcher, std::string *snapshotPath) = 0;
virtual void getEventsSince(WatcherRef watcher, std::string *snapshotPath) = 0;
virtual void subscribe(WatcherRef watcher) = 0;
virtual void unsubscribe(WatcherRef watcher) = 0;
static std::shared_ptr<Backend> getShared(std::string backend);
void watch(WatcherRef watcher);
void unwatch(WatcherRef watcher);
void unref();
void handleWatcherError(WatcherError &err);
std::mutex mMutex;
std::thread mThread;
private:
std::unordered_set<WatcherRef> mSubscriptions;
Signal mStartedSignal;
void handleError(std::exception &err);
};
#endif

113
web/node_modules/@parcel/watcher/src/Debounce.cc generated vendored Normal file
View File

@@ -0,0 +1,113 @@
#include "Debounce.hh"
#ifdef __wasm32__
extern "C" void on_timeout(void *ctx) {
Debounce *debounce = (Debounce *)ctx;
debounce->notify();
}
#endif
std::shared_ptr<Debounce> Debounce::getShared() {
static std::weak_ptr<Debounce> sharedInstance;
std::shared_ptr<Debounce> shared = sharedInstance.lock();
if (!shared) {
shared = std::make_shared<Debounce>();
sharedInstance = shared;
}
return shared;
}
Debounce::Debounce() {
mRunning = true;
#ifndef __wasm32__
mThread = std::thread([this] () {
loop();
});
#endif
}
Debounce::~Debounce() {
mRunning = false;
#ifndef __wasm32__
mWaitSignal.notify();
mThread.join();
#endif
}
void Debounce::add(void *key, std::function<void()> cb) {
std::unique_lock<std::mutex> lock(mMutex);
mCallbacks.emplace(key, cb);
}
void Debounce::remove(void *key) {
std::unique_lock<std::mutex> lock(mMutex);
mCallbacks.erase(key);
}
void Debounce::trigger() {
std::unique_lock<std::mutex> lock(mMutex);
#ifdef __wasm32__
notifyIfReady();
#else
mWaitSignal.notify();
#endif
}
#ifndef __wasm32__
void Debounce::loop() {
while (mRunning) {
mWaitSignal.wait();
if (!mRunning) {
break;
}
notifyIfReady();
}
}
#endif
void Debounce::notifyIfReady() {
if (!mRunning) {
return;
}
// If we haven't seen an event in more than the maximum wait time, notify callbacks immediately
// to ensure that we don't wait forever. Otherwise, wait for the minimum wait time and batch
// subsequent fast changes. This also means the first file change in a batch is notified immediately,
// separately from the rest of the batch. This seems like an acceptable tradeoff if the common case
// is that only a single file was updated at a time.
auto time = std::chrono::steady_clock::now();
if ((time - mLastTime) > std::chrono::milliseconds(MAX_WAIT_TIME)) {
mLastTime = time;
notify();
} else {
wait();
}
}
void Debounce::wait() {
#ifdef __wasm32__
clear_timeout(mTimeout);
mTimeout = set_timeout(MIN_WAIT_TIME, this);
#else
auto status = mWaitSignal.waitFor(std::chrono::milliseconds(MIN_WAIT_TIME));
if (mRunning && (status == std::cv_status::timeout)) {
notify();
}
#endif
}
void Debounce::notify() {
std::unique_lock<std::mutex> lock(mMutex);
mLastTime = std::chrono::steady_clock::now();
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
auto cb = it->second;
cb();
}
#ifndef __wasm32__
mWaitSignal.reset();
#endif
}

Some files were not shown because too many files have changed in this diff Show More