Compare commits
88 Commits
vehicleLis
...
0cc1c1451c
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cc1c1451c | |||
| 82773f090b | |||
| 84592d2ed4 | |||
| ddcd8b64af | |||
| 1f7515cf92 | |||
| 89ffff594e | |||
| 038c256fc5 | |||
| 65c2f52438 | |||
| 42ba42f3a6 | |||
| 07b91da6bc | |||
| d269edcfe4 | |||
| 9d5534d8ef | |||
| c7d0f24ca1 | |||
| 8613a7e614 | |||
| 95afa20373 | |||
| eddd57219c | |||
| 153e832649 | |||
| ef66fa9876 | |||
| 34faa8c215 | |||
| d9bb47bc98 | |||
| b9692a09f2 | |||
| ea6ed41608 | |||
| 676eea1238 | |||
| 395160db6e | |||
|
|
bc948e0141 | ||
| 1d86d44aa8 | |||
| 03f389825e | |||
| fc0739ab57 | |||
| 9b76cb143d | |||
| c6f40f2aa2 | |||
| 3526ee8700 | |||
| d8369bad3d | |||
| 1903cd27db | |||
| 5eccdcf89b | |||
| 56f0700999 | |||
| c8ab3f22ef | |||
| 10c4fb22d1 | |||
| 698ccfc9d6 | |||
| 8cf9fcdb0a | |||
| d6b8a48d1c | |||
|
|
cbc77f1615 | ||
|
|
fc8b325865 | ||
| 89adcc69a2 | |||
| da3c37699e | |||
| 019569155a | |||
|
|
bd71a36cd3 | ||
|
|
8778f88fbd | ||
|
|
3aba9e0071 | ||
|
|
560af60f32 | ||
|
|
4789c5a49e | ||
|
|
b480747b86 | ||
|
|
5617646bdb | ||
|
|
4c05d570f7 | ||
|
|
11e377ddb3 | ||
| 18e2517805 | |||
| bad991c3bc | |||
| cf7b78e64e | |||
|
|
eec5afdf39 | ||
|
|
83ae4665f0 | ||
|
|
5950599bdb | ||
|
|
e5dae2d18f | ||
|
|
e5fc2bcfb5 | ||
|
|
6c5c25a992 | ||
|
|
89af48389a | ||
|
|
669d7e4e7b | ||
|
|
0bf315596b | ||
| 61e9285a2b | |||
| eb8859ca67 | |||
| b89b8064c5 | |||
|
|
0f4d0e1e5b | ||
|
|
583027422c | ||
|
|
a36827a6ff | ||
|
|
511baa6a8e | ||
|
|
031ef7b924 | ||
|
|
fc60854e21 | ||
|
|
afc403bded | ||
|
|
e0fd3aa510 | ||
|
|
33c182574c | ||
|
|
274f6c1616 | ||
|
|
675974a9e7 | ||
|
|
612f74202d | ||
|
|
6ccb72e566 | ||
|
|
ea4eea03da | ||
|
|
5ffb2bcf85 | ||
|
|
18f126e4eb | ||
|
|
1c29f00720 | ||
|
|
70186d1357 | ||
|
|
9ba3480507 |
132
config.yaml
132
config.yaml
@@ -1,5 +1,43 @@
|
|||||||
name: PARCOURSMOB
|
name: PARCOURSMOB
|
||||||
|
|
||||||
|
menu_items:
|
||||||
|
- name: dashboard
|
||||||
|
title: Tableau de bord
|
||||||
|
link: /app/
|
||||||
|
icon: hero:outline/home
|
||||||
|
- name: beneficiaries
|
||||||
|
title: Bénéficiaires
|
||||||
|
link: /app/beneficiaries/
|
||||||
|
icon: hero:outline/user-group
|
||||||
|
- name: journeys
|
||||||
|
title: Déplacements
|
||||||
|
link: /app/journeys/
|
||||||
|
icon: hero:outline/map
|
||||||
|
- name: solidarity_transport
|
||||||
|
title: Transport solidaire
|
||||||
|
link: /app/solidarity-transport/
|
||||||
|
icon: tabler-icons:car
|
||||||
|
- name: vehicles
|
||||||
|
title: Véhicules partagés
|
||||||
|
link: /app/vehicles/
|
||||||
|
icon: tabler-icons:car
|
||||||
|
- name: vehicles_management
|
||||||
|
title: Gestion des véhicules
|
||||||
|
link: /app/vehicles-management/
|
||||||
|
icon: hero:outline/briefcase
|
||||||
|
- name: agenda
|
||||||
|
title: Agenda dispositifs
|
||||||
|
link: /app/agenda/
|
||||||
|
icon: hero:outline/calendar
|
||||||
|
- name: directory
|
||||||
|
title: Répertoire solutions
|
||||||
|
link: /app/directory/
|
||||||
|
icon: hero:outline/document-text
|
||||||
|
- name: support
|
||||||
|
title: Support
|
||||||
|
link: /app/support/
|
||||||
|
icon: hero:outline/support
|
||||||
|
|
||||||
views:
|
views:
|
||||||
generic:
|
generic:
|
||||||
files:
|
files:
|
||||||
@@ -9,6 +47,7 @@ views:
|
|||||||
files:
|
files:
|
||||||
- web/layouts/dashboard/_partials/agenda-widget.html
|
- web/layouts/dashboard/_partials/agenda-widget.html
|
||||||
- web/layouts/dashboard/_partials/beneficiaries-widget.html
|
- web/layouts/dashboard/_partials/beneficiaries-widget.html
|
||||||
|
- web/layouts/dashboard/_partials/bookings-widget.html
|
||||||
- web/layouts/dashboard/dashboard.html
|
- web/layouts/dashboard/dashboard.html
|
||||||
beneficiaries:
|
beneficiaries:
|
||||||
list:
|
list:
|
||||||
@@ -28,7 +67,12 @@ views:
|
|||||||
- web/layouts/beneficiaries/_partials/beneficiary-events.html
|
- web/layouts/beneficiaries/_partials/beneficiary-events.html
|
||||||
- web/layouts/beneficiaries/_partials/beneficiary-files.html
|
- web/layouts/beneficiaries/_partials/beneficiary-files.html
|
||||||
- web/layouts/beneficiaries/_partials/beneficiary-organizations.html
|
- web/layouts/beneficiaries/_partials/beneficiary-organizations.html
|
||||||
|
- web/layouts/beneficiaries/_partials/beneficiary-diags.html
|
||||||
|
- web/layouts/beneficiaries/_partials/beneficiary-wallet.html
|
||||||
- web/layouts/beneficiaries/display.html
|
- web/layouts/beneficiaries/display.html
|
||||||
|
create_diag:
|
||||||
|
files:
|
||||||
|
- web/layouts/beneficiaries/create-diag.html
|
||||||
update:
|
update:
|
||||||
files:
|
files:
|
||||||
- web/layouts/_partials/address_autocomplete.html
|
- web/layouts/_partials/address_autocomplete.html
|
||||||
@@ -40,6 +84,15 @@ views:
|
|||||||
update:
|
update:
|
||||||
files:
|
files:
|
||||||
- web/layouts/members/update.html
|
- web/layouts/members/update.html
|
||||||
|
list:
|
||||||
|
files:
|
||||||
|
- web/layouts/members/membersList.html
|
||||||
|
group:
|
||||||
|
settings:
|
||||||
|
files:
|
||||||
|
- web/layouts/administration/_partials/groups_admins.html
|
||||||
|
- web/layouts/administration/_partials/group_members.html
|
||||||
|
- web/layouts/group/settings.html
|
||||||
vehicles:
|
vehicles:
|
||||||
search:
|
search:
|
||||||
files:
|
files:
|
||||||
@@ -49,10 +102,14 @@ views:
|
|||||||
booking_display:
|
booking_display:
|
||||||
files:
|
files:
|
||||||
- web/layouts/vehicles/booking-display.html
|
- web/layouts/vehicles/booking-display.html
|
||||||
|
- web/layouts/vehicles_management/_partials/booking-diags.html
|
||||||
bookings_list:
|
bookings_list:
|
||||||
files:
|
files:
|
||||||
- web/layouts/vehicles_management/_partials/bookings-list.html
|
- web/layouts/vehicles_management/_partials/bookings-list.html
|
||||||
- web/layouts/vehicles/bookings-list.html
|
- web/layouts/vehicles/bookings-list.html
|
||||||
|
create_booking_diag:
|
||||||
|
files:
|
||||||
|
- web/layouts/vehicles/create-booking-diag.html
|
||||||
vehicles_management:
|
vehicles_management:
|
||||||
overview:
|
overview:
|
||||||
files:
|
files:
|
||||||
@@ -72,6 +129,7 @@ views:
|
|||||||
files:
|
files:
|
||||||
- web/layouts/vehicles_management/_partials/calendar.html
|
- web/layouts/vehicles_management/_partials/calendar.html
|
||||||
- web/layouts/vehicles_management/fleet-display.html
|
- web/layouts/vehicles_management/fleet-display.html
|
||||||
|
- web/layouts/vehicles_management/_partials/vehicle-diags.html
|
||||||
fleet_update:
|
fleet_update:
|
||||||
files:
|
files:
|
||||||
- web/layouts/_partials/address_autocomplete.html
|
- web/layouts/_partials/address_autocomplete.html
|
||||||
@@ -80,18 +138,33 @@ views:
|
|||||||
booking_display:
|
booking_display:
|
||||||
files:
|
files:
|
||||||
- web/layouts/vehicles_management/booking-display.html
|
- web/layouts/vehicles_management/booking-display.html
|
||||||
|
- web/layouts/vehicles_management/_partials/booking-diags.html
|
||||||
|
delete_booking:
|
||||||
|
files:
|
||||||
|
- web/layouts/vehicles_management/delete-booking.html
|
||||||
|
create_vehicle_diag:
|
||||||
|
files:
|
||||||
|
- web/layouts/vehicles_management/create-vehicle-diag.html
|
||||||
|
create_booking_diag:
|
||||||
|
files:
|
||||||
|
- web/layouts/vehicles_management/create-booking-diag.html
|
||||||
agenda:
|
agenda:
|
||||||
list:
|
list:
|
||||||
files:
|
files:
|
||||||
- web/layouts/agenda/home.html
|
- web/layouts/agenda/home.html
|
||||||
|
history:
|
||||||
|
files:
|
||||||
|
- web/layouts/agenda/history.html
|
||||||
display_event:
|
display_event:
|
||||||
files:
|
files:
|
||||||
- web/layouts/agenda/_partials/subscribers-table.html
|
- web/layouts/agenda/_partials/subscribers-table.html
|
||||||
- web/layouts/agenda/display-event.html
|
- web/layouts/agenda/display-event.html
|
||||||
|
- web/layouts/agenda/_partials/event-files.html
|
||||||
create_event:
|
create_event:
|
||||||
files:
|
files:
|
||||||
- web/layouts/_partials/address_autocomplete.html
|
- web/layouts/_partials/address_autocomplete.html
|
||||||
- web/layouts/agenda/create-event.html
|
- web/layouts/agenda/create-event.html
|
||||||
|
- web/layouts/agenda/_partials/event-files.html
|
||||||
delete_subscriber:
|
delete_subscriber:
|
||||||
files:
|
files:
|
||||||
- web/layouts/agenda/delete-subscriber.html
|
- web/layouts/agenda/delete-subscriber.html
|
||||||
@@ -113,11 +186,12 @@ views:
|
|||||||
journeys:
|
journeys:
|
||||||
search:
|
search:
|
||||||
files:
|
files:
|
||||||
- web/layouts/_partials/address_autocomplete.html
|
- web/layouts/_partials/orb_address_autocomplete.html
|
||||||
- web/layouts/journeys/_partials/journeys-all.html
|
- web/layouts/journeys/_partials/journeys-all.html
|
||||||
- web/layouts/journeys/_partials/journeys-others.html
|
- web/layouts/journeys/_partials/journeys-others.html
|
||||||
- web/layouts/journeys/_partials/journeys-carpool.html
|
- web/layouts/journeys/_partials/journeys-carpool.html
|
||||||
- web/layouts/journeys/_partials/journeys-public-transit.html
|
- web/layouts/journeys/_partials/journeys-public-transit.html
|
||||||
|
- web/layouts/journeys/_partials/journeys-solidarity-transport.html
|
||||||
- web/layouts/journeys/search.html
|
- web/layouts/journeys/search.html
|
||||||
list:
|
list:
|
||||||
files:
|
files:
|
||||||
@@ -150,6 +224,48 @@ views:
|
|||||||
display_group:
|
display_group:
|
||||||
files:
|
files:
|
||||||
- web/layouts/group_module/display_group.html
|
- web/layouts/group_module/display_group.html
|
||||||
|
diags:
|
||||||
|
list:
|
||||||
|
files:
|
||||||
|
- web/layouts/diags/home.html
|
||||||
|
display_diag:
|
||||||
|
files:
|
||||||
|
- web/layouts/diags/display-diag.html
|
||||||
|
- web/layouts/diags/_partials/diags-files.html
|
||||||
|
delete:
|
||||||
|
files:
|
||||||
|
- web/layouts/diags/delete-diag.html
|
||||||
|
update:
|
||||||
|
files:
|
||||||
|
- web/layouts/diags/update-diag.html
|
||||||
|
history:
|
||||||
|
files:
|
||||||
|
- web/layouts/diags/history-diags.html
|
||||||
|
|
||||||
|
solidarity_transport:
|
||||||
|
overview:
|
||||||
|
files:
|
||||||
|
- web/layouts/solidarity_transport/_partials/drivers_list.html
|
||||||
|
- web/layouts/solidarity_transport/_partials/bookings_list.html
|
||||||
|
- web/layouts/solidarity_transport/overview.html
|
||||||
|
driver_create:
|
||||||
|
files:
|
||||||
|
- web/layouts/_partials/address_autocomplete.html
|
||||||
|
- web/layouts/solidarity_transport/driver_create.html
|
||||||
|
driver_display:
|
||||||
|
files:
|
||||||
|
- web/layouts/_partials/address_autocomplete.html
|
||||||
|
- web/layouts/solidarity_transport/_partials/driver_availabilities.html
|
||||||
|
- web/layouts/solidarity_transport/_partials/driver_documents.html
|
||||||
|
- web/layouts/solidarity_transport/driver_display.html
|
||||||
|
driver_journey:
|
||||||
|
files:
|
||||||
|
- web/layouts/solidarity_transport/_partials/journey_preview.html
|
||||||
|
- web/layouts/solidarity_transport/driver_journey.html
|
||||||
|
booking_display:
|
||||||
|
files:
|
||||||
|
- web/layouts/solidarity_transport/_partials/journey_preview.html
|
||||||
|
- web/layouts/solidarity_transport/booking_display.html
|
||||||
|
|
||||||
administration:
|
administration:
|
||||||
home:
|
home:
|
||||||
@@ -163,7 +279,19 @@ views:
|
|||||||
- web/layouts/administration/_partials/groups_admins.html
|
- web/layouts/administration/_partials/groups_admins.html
|
||||||
- web/layouts/administration/_partials/group_members.html
|
- web/layouts/administration/_partials/group_members.html
|
||||||
- web/layouts/administration/display_group.html
|
- web/layouts/administration/display_group.html
|
||||||
group:
|
beneficaires_list:
|
||||||
|
files:
|
||||||
|
- web/layouts/administration/beneficaires_stats.html
|
||||||
|
events_list:
|
||||||
|
files:
|
||||||
|
- web/layouts/administration/events_stats.html
|
||||||
|
vehicles_list:
|
||||||
|
files:
|
||||||
|
- web/layouts/administration/vehicles_stats.html
|
||||||
|
bookings_list:
|
||||||
|
files:
|
||||||
|
- web/layouts/administration/_partials/bookings_list.html
|
||||||
|
- web/layouts/administration/bookings_stats.html
|
||||||
settings:
|
settings:
|
||||||
files:
|
files:
|
||||||
- web/layouts/administration/_partials/groups_admins.html
|
- web/layouts/administration/_partials/groups_admins.html
|
||||||
|
|||||||
0
emails/layout.html
Normal file → Executable file
0
emails/layout.html
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<p>Vous avez reçu un commentaire sur PARCOURSMOB de la part de <b>{{.user}}</b></p>
|
<p>Vous avez reçu un commentaire sur PARCOURSMOB de la part de <b>{{.user}}</b></p>
|
||||||
<p>{{.key}}</p>
|
<p>{{unescapeHTML .key}}</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
@theme {
|
||||||
|
--color-co-blue: #243887;
|
||||||
|
--color-co-lightblue: #907eff;
|
||||||
|
--color-co-red: #ff1300;
|
||||||
|
--color-co-green: #6cc11f;
|
||||||
|
--color-co-yellow: #ffdd00;
|
||||||
|
--color-co-orange: #ff5300;
|
||||||
|
|
||||||
|
--radius-co: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Manometer";
|
font-family: "Manometer";
|
||||||
@@ -16,4 +25,7 @@
|
|||||||
html {
|
html {
|
||||||
font-family: Bitter, serif;
|
font-family: Bitter, serif;
|
||||||
}
|
}
|
||||||
|
input {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import '@kingshott/iodine';
|
import '@kingshott/iodine';
|
||||||
import Alpine from 'alpinejs'
|
import Alpine from 'alpinejs'
|
||||||
|
import { Protocol } from "pmtiles";
|
||||||
|
|
||||||
window.Alpine = Alpine
|
window.Alpine = Alpine
|
||||||
|
|
||||||
Alpine.start()
|
Alpine.start()
|
||||||
|
|
||||||
|
let protocol = new Protocol();
|
||||||
|
maplibregl.addProtocol("pmtiles",protocol.tile);
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
{{ define "address_autocomplete" }}
|
{{ define "address_autocomplete" }}
|
||||||
|
|
||||||
|
<div class="col-span-6 relative" x-data='{
|
||||||
<div class="col-span-6 relative" x-data="{
|
input: {{if .Address}}"{{.Address.properties.label}}"{{else}}null{{end}},
|
||||||
input: {{if .Address}}'{{.Address.Properties.label}}'{{else}}null{{end}},
|
address: {{if .Address}}JSON.stringify({{template "geojson" .Address}}){{else}}null{{end}},
|
||||||
address: {{if .Address}}JSON.stringify({{printf "%s" .Address.MarshalJSON}}){{else}}null{{end}},
|
addressObject: {{if .Address}}{{template "geojson" .Address }}{{else}}null{{end}},
|
||||||
addressObject: {{if .Address}}{{printf "%s" .Address.MarshalJSON}}{{else}}null{{end}},
|
|
||||||
responselength: 0,
|
responselength: 0,
|
||||||
async autocomplete() {
|
async autocomplete() {
|
||||||
if(this.input == null || this.input == '') {
|
if(this.input == null || this.input == "") {
|
||||||
this.responselength = 0
|
this.responselength = 0
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -15,26 +14,26 @@
|
|||||||
this.responselength = 0
|
this.responselength = 0
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
result = await fetch('https://geocode.ridygo.fr/v1/autocomplete/\?text=' + this.input)
|
result = await fetch("https://api.geocode.earth/v1/autocomplete/\?api_key=ge-4949288cd450ad31&text=" + this.input)
|
||||||
json = await result.json()
|
json = await result.json()
|
||||||
|
|
||||||
this.responselength = json['features'].length
|
this.responselength = json["features"].length
|
||||||
return json['features']
|
return json["features"]
|
||||||
},
|
},
|
||||||
select(a) {
|
select(a) {
|
||||||
this.address = JSON.stringify(a)
|
this.address = JSON.stringify(a)
|
||||||
this.addressObject = a
|
this.addressObject = a
|
||||||
this.input = a.properties.label
|
this.input = a.properties.label
|
||||||
}
|
}
|
||||||
}">
|
}'>
|
||||||
<input type="hidden" name="{{ .FieldName }}" x-model="address">
|
<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>
|
<label for="address" class="block text-sm font-medium text-gray-700">{{ if .FieldLabel }}{{.FieldLabel}}{{else}}Adresse{{end}}</label>
|
||||||
<input type="text"
|
<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">
|
x-model="input">
|
||||||
|
|
||||||
<ul x-show="responselength > 0"
|
<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">
|
<template x-for="item in autocomplete">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<li class="text-gray-900 hover:bg-gray-200 cursor-default select-none relative py-2 pl-3 pr-9"
|
<li class="text-gray-900 hover:bg-gray-200 cursor-default select-none relative py-2 pl-3 pr-9"
|
||||||
@@ -46,3 +45,14 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{define "geojson"}}{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [{{index .geometry.coordinates 0}}, {{index .geometry.coordinates 1}}],
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"label": "{{.properties.label}}"
|
||||||
|
}
|
||||||
|
}{{end}}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
{{define "mainmenu"}}
|
{{define "mainmenu"}}
|
||||||
|
|
||||||
{{range .LayoutState.MenuItems}}
|
{{range .LayoutState.Menu}}
|
||||||
|
{{if moduleAvailable .name }}
|
||||||
<nav class="px-2 space-y-1">
|
<nav class="px-2 space-y-1">
|
||||||
<a href="{{.Link}}"
|
<a href="{{.link}}"
|
||||||
{{ if .Active }}
|
{{ 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">
|
class="bg-white text-co-blue group flex items-center px-2 py-2 text-base font-medium rounded-2xl">
|
||||||
{{ else}}
|
{{ 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}}
|
{{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>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
59
web/layouts/_partials/orb_address_autocomplete.html
Normal file
59
web/layouts/_partials/orb_address_autocomplete.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
{{ 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 []
|
||||||
|
}
|
||||||
|
result = await fetch("https://api.geocode.earth/v1/autocomplete/\?api_key=ge-4949288cd450ad31&text=" + this.input)
|
||||||
|
json = await result.json()
|
||||||
|
|
||||||
|
this.responselength = json["features"].length
|
||||||
|
return json["features"]
|
||||||
|
},
|
||||||
|
select(a) {
|
||||||
|
this.address = JSON.stringify(a)
|
||||||
|
this.addressObject = a
|
||||||
|
this.input = a.properties.label
|
||||||
|
}
|
||||||
|
}'>
|
||||||
|
<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}}
|
||||||
115
web/layouts/administration/_partials/bookings_list.html
Normal file
115
web/layouts/administration/_partials/bookings_list.html
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{{define "bookings_list"}}
|
||||||
|
<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="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 text-center">
|
||||||
|
Statut
|
||||||
|
</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">
|
||||||
|
Numéro
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Gestionnaire véhicule
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Prescripteur
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Beneficiaire
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Dates
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Commentaire
|
||||||
|
</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 class="divide-y divide-gray-200 bg-white">
|
||||||
|
{{range .ViewState.bookings}}
|
||||||
|
<tr>
|
||||||
|
<td class="flex-col py-4 pl-4 pr-3 text-sm sm:pl-6 text-center">
|
||||||
|
{{if .Data.administrator_unavailability}}
|
||||||
|
<span class="p-1 bg-black text-white text-xs font-bold rounded-xl" >
|
||||||
|
Retiré
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
{{if eq .Status 1 }}
|
||||||
|
<span class="p-1 bg-co-blue text-white text-xs font-bold rounded-xl" >
|
||||||
|
A venir
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{if eq .Status 0 }}
|
||||||
|
<span class="p-1 bg-co-green text-white text-xs font-bold rounded-xl" >
|
||||||
|
En cours
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{if eq .Status -1 }}
|
||||||
|
<span class="p-1 bg-co-red text-white text-xs font-bold rounded-xl" >
|
||||||
|
Terminé
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{ (index $.ViewState.vehicles_map .Vehicleid).Type }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{ (index $.ViewState.vehicles_map .Vehicleid).Data.licence_plate }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{ (index $.ViewState.admingroups (index (index $.ViewState.vehicles_map .Vehicleid).Administrators 0)).Data.name }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{ .Data.booked_by.group.name }}<br />({{ .Data.booked_by.user.display_name }})</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
{{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=""> -->
|
||||||
|
{{$b := (index $.ViewState.beneficiaries_map .Driver)}}
|
||||||
|
{{$b.Data.first_name}} {{$b.Data.last_name}} <br />
|
||||||
|
{{$b.Data.file_number}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
{{if .Data.administrator_unavailability}}
|
||||||
|
<div class="text-gray-900" >Retiré du {{(timeFrom .Unavailablefrom).Format "02/01/2006"}} <br />
|
||||||
|
au {{(timeFrom .Unavailableto).Format "02/01/2006"}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="text-gray-900" >Du {{(timeFrom .Startdate).Format "02/01/2006"}} <br />
|
||||||
|
au {{(timeFrom .Enddate).Format "02/01/2006"}}</div>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{ .Data.comment }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<a href="/app/vehicles-management/bookings/{{.ID}}"
|
||||||
|
class="text-co-blue hover:text-co-blue">Voir</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
156
web/layouts/administration/beneficaires_stats.html
Normal file
156
web/layouts/administration/beneficaires_stats.html
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">La liste des bénéficaires</h1>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/app/administration/">
|
||||||
|
<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">
|
||||||
|
Retour
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8" x-data="{
|
||||||
|
state: {{.ViewState.JSONWithLimits 0 10}},
|
||||||
|
current: 0,
|
||||||
|
nb_pages() {
|
||||||
|
let nbEl = this.state.count
|
||||||
|
return Math.ceil(nbEl/10)
|
||||||
|
},
|
||||||
|
async paginate(page) {
|
||||||
|
let start = (page-1)*10
|
||||||
|
if(start < 0|| start > this.state.count) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let resp = await fetch('/api/cache/' + this.state.cache_id + '?limits.min=' + start + '&limits.max=' + (start+10))
|
||||||
|
let data = await resp.json()
|
||||||
|
this.state.beneficiaries = data
|
||||||
|
this.current=page-1
|
||||||
|
}
|
||||||
|
}">
|
||||||
|
<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="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="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Téléphone
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Adresse
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Labels
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
||||||
|
<span class="sr-only">Modifier</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200 bg-white">
|
||||||
|
<template x-for="beneficiary in state.beneficiaries">
|
||||||
|
<tr>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="h-10 w-10 flex-shrink-0">
|
||||||
|
<img class="h-10 w-10 rounded-co"
|
||||||
|
:src="'/app/beneficiaries/' + beneficiary.id + '/picture'" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="font-medium text-gray-900"><span
|
||||||
|
x-text="beneficiary.data.first_name"></span> <span
|
||||||
|
x-text="beneficiary.data.last_name"></span></div>
|
||||||
|
<div class="text-gray-500" x-text="beneficiary.data.email"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||||
|
<div class="text-gray-900" x-text="beneficiary.data.phone_number"></div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"
|
||||||
|
x-text="beneficiary.data.address ? beneficiary.data.address.properties.label : ''">
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||||
|
<template x-for="tag in beneficiary.data.tags">
|
||||||
|
<span
|
||||||
|
class="inline-flex rounded-full bg-green-100 px-2 text-xs font-semibold leading-5 text-green-800"
|
||||||
|
x-text="tag"></span>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
||||||
|
<a :href="'/app/beneficiaries/' + beneficiary.id"
|
||||||
|
class="text-co-blue hover:text-co-blue">Voir<span class="sr-only">, <span
|
||||||
|
x-text="beneficiary.data.first_name"></span> <span
|
||||||
|
x-text="beneficiary.data.last_name"></span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- More people... -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||||
|
<div class="flex-1 flex justify-between sm:hidden">
|
||||||
|
<a href="#" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
@click="paginate(current)"> Previous </a>
|
||||||
|
<a href="#" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
@click="paginate(current+2)"> Next </a>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-700">
|
||||||
|
Résultats
|
||||||
|
<span class="font-medium" x-text="Math.min((current * 10)+1, state.count)"></span>
|
||||||
|
Ă
|
||||||
|
<span class="font-medium" x-text="Math.min((current * 10)+10, state.count)"></span>
|
||||||
|
sur
|
||||||
|
<span class="font-medium" x-text="state.count"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
|
||||||
|
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
|
||||||
|
@click="paginate(current)">
|
||||||
|
<span class="sr-only">Previous</span>
|
||||||
|
<!-- Heroicon name: solid/chevron-left -->
|
||||||
|
<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="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<template x-for="i in nb_pages">
|
||||||
|
<a href="#" @click="paginate(i)"
|
||||||
|
class="relative inline-flex items-center px-4 py-2 border text-sm font-medium"
|
||||||
|
:class="i == current+1 ? 'z-10 bg-indigo-50 border-co-blue text-co-blue' : 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'"
|
||||||
|
x-text="i"></a>
|
||||||
|
</template>
|
||||||
|
<!-- Current: "z-10 bg-indigo-50 border-indigo-500 text-indigo-600", Default: "bg-white border-gray-300 text-gray-500 hover:bg-gray-50" -->
|
||||||
|
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
|
||||||
|
@click="paginate(current+2)">
|
||||||
|
<span class="sr-only">Next</span>
|
||||||
|
<!-- Heroicon name: solid/chevron-right -->
|
||||||
|
<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="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
37
web/layouts/administration/bookings_stats.html
Normal file
37
web/layouts/administration/bookings_stats.html
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{{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">Historique des réservations de véhicules</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/exports/fleets/bookings">
|
||||||
|
<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">
|
||||||
|
Export
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="/app/administration/stats/vehicles">
|
||||||
|
<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-left" "h-5 w-5 mr-3"}}
|
||||||
|
Liste des véhicules
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<!-- <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> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "bookings_list" .}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
111
web/layouts/administration/events_stats.html
Normal file
111
web/layouts/administration/events_stats.html
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">La liste des événements</h1>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/exports/agenda/subscriptions">
|
||||||
|
<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">
|
||||||
|
Export
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="/app/administration/">
|
||||||
|
<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">
|
||||||
|
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">
|
||||||
|
Dispositif
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Structure
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Lieu
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Dates
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Places disponibles
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Bénéficiaires positionnés
|
||||||
|
</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.events}}
|
||||||
|
<a href="/app/agenda/{{.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>
|
||||||
|
<div class="text-gray-500">{{.Type}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
|
||||||
|
<div class="text-gray-900" >
|
||||||
|
{{range .Owners}}
|
||||||
|
{{if (index $.ViewState.groups .)}}
|
||||||
|
{{(index $.ViewState.groups .).Data.name}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{if .Data.address}}{{.Data.address.properties.label}}{{end}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >
|
||||||
|
{{if eq .Startdate .Enddate}}
|
||||||
|
Le {{(timeFrom .Startdate).Format "02/01/2006"}}
|
||||||
|
{{else}}
|
||||||
|
Du {{(timeFrom .Startdate).Format "02/01/2006"}} <br />Au {{(timeFrom .Enddate).Format "02/01/2006"}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
{{if ne .RemainingSubscriptions 999}}
|
||||||
|
<div class="text-gray-900" >{{.RemainingSubscriptions}}</div>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="flex -space-x-1 overflow-hidden" >
|
||||||
|
{{range .Subscriptions}}
|
||||||
|
<img class="inline-block h-6 w-6 rounded-co ring-2 ring-white" src="/app/beneficiaries/{{.Subscriber}}/picture" >
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<a href="/app/agenda/{{.ID}}" class="text-co-blue hover:text-co-blue">Voir</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<div
|
<div
|
||||||
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
||||||
<div class="flex-1 px-4 py-2 text-sm truncate">
|
<div class="flex-1 px-4 py-2 text-sm truncate">
|
||||||
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">Bénéficiaires</a>
|
<a href="/app/administration/stats/beneficaires" class="text-gray-900 font-medium hover:text-gray-600">Bénéficiaires</a>
|
||||||
<p class="text-gray-500">{{len .ViewState.beneficiaries }} bénéficiaires</p>
|
<p class="text-gray-500">{{len .ViewState.beneficiaries }} bénéficiaires</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,13 +23,13 @@
|
|||||||
<li class="col-span-1 flex shadow-sm rounded-3xl">
|
<li class="col-span-1 flex shadow-sm rounded-3xl">
|
||||||
<div
|
<div
|
||||||
class="flex-shrink-0 flex items-center justify-center w-16 bg-co-green text-white text-sm font-medium rounded-l-3xl">
|
class="flex-shrink-0 flex items-center justify-center w-16 bg-co-green text-white text-sm font-medium rounded-l-3xl">
|
||||||
{{.IconSet.Icon "hero:outline/office-building" "h-6 w-6"}}
|
{{.IconSet.Icon "hero:outline/calendar" "h-6 w-6"}}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
||||||
<div class="flex-1 px-4 py-2 text-sm truncate">
|
<div class="flex-1 px-4 py-2 text-sm truncate">
|
||||||
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">Organisations</a>
|
<a href="/app/administration/stats/events" class="text-gray-900 font-medium hover:text-gray-600">Évenements</a>
|
||||||
<p class="text-gray-500">{{len .ViewState.groups}} organisations</p>
|
<p class="text-gray-500">{{len .ViewState.events}} évenements</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<div
|
<div
|
||||||
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-3xl truncate">
|
||||||
<div class="flex-1 px-4 py-2 text-sm truncate">
|
<div class="flex-1 px-4 py-2 text-sm truncate">
|
||||||
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">Référents</a>
|
<a href="/app/members/" class="text-gray-900 font-medium hover:text-gray-600">Référents</a>
|
||||||
<p class="text-gray-500">{{len .ViewState.accounts}} membres</p>
|
<p class="text-gray-500">{{len .ViewState.accounts}} membres</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,8 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mt-10 mx-auto px-4 sm:px-6 md:px-8">
|
<div class="max-w-7xl mt-10 mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<!-- <h2 class="text-xl font-semibold text-gray-500">Gestion des organisations</h2> -->
|
||||||
|
<div class="flex-1 px-4 py-2 text-sm truncate">
|
||||||
<h2 class="text-xl font-semibold text-gray-500">Gestion des organisations</h2>
|
<h2 class="text-xl font-semibold text-gray-500">Gestion des organisations</h2>
|
||||||
|
<p class="text-gray-500">( {{len .ViewState.groups}} organisations )</p>
|
||||||
|
</div>
|
||||||
<div class="sm:flex sm:items-center">
|
<div class="sm:flex sm:items-center">
|
||||||
<div class="sm:flex-auto">
|
<div class="sm:flex-auto">
|
||||||
<p class="mt-2 text-sm text-gray-700"></p>
|
<p class="mt-2 text-sm text-gray-700"></p>
|
||||||
|
|||||||
155
web/layouts/administration/vehicles_stats.html
Normal file
155
web/layouts/administration/vehicles_stats.html
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">Liste des véhicules</h1>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/app/administration/">
|
||||||
|
<a href="/app/administration/stats/bookings">
|
||||||
|
<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-left" "h-5 w-5 mr-3"}}
|
||||||
|
Liste des réservations
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</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="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Statut Réservation
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Organisation
|
||||||
|
</th>
|
||||||
|
<th scope="col"
|
||||||
|
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Numéro (Immat / Bicycode)
|
||||||
|
</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">
|
||||||
|
Modèle
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Lieu
|
||||||
|
</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 class="divide-y divide-gray-200 bg-white">
|
||||||
|
{{range .ViewState.vehicles}}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6 text-center">
|
||||||
|
<div class="flex flex-col px-6">
|
||||||
|
{{if not .Bookings}}
|
||||||
|
<span class="mt-1 p-1 bg-gray-500 text-white text-xs font-bold rounded-xl" >
|
||||||
|
Disponible
|
||||||
|
</span>
|
||||||
|
{{else if len .Bookings | eq 1}}
|
||||||
|
{{range .Bookings}}
|
||||||
|
{{if .Data.administrator_unavailability}}
|
||||||
|
<span class="mt-1 p-1 bg-black text-white text-xs font-bold rounded-xl" >
|
||||||
|
Retiré
|
||||||
|
</span>
|
||||||
|
{{else if eq .Status -1 }}
|
||||||
|
<span class="mt-1 p-1 bg-gray-500 text-white text-xs font-bold rounded-xl" >
|
||||||
|
Disponible
|
||||||
|
</span>
|
||||||
|
{{else if eq .Status 1 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-blue text-white text-xs font-bold rounded-xl" >
|
||||||
|
A venir
|
||||||
|
</span>
|
||||||
|
{{else if eq .Status 0 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-green text-white text-xs font-bold rounded-xl" >
|
||||||
|
En cours
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{else if gt (len .Bookings) 1}}
|
||||||
|
{{range .Bookings}}
|
||||||
|
{{if .Data.administrator_unavailability}}
|
||||||
|
<span class="mt-1 p-1 bg-black text-white text-xs font-bold rounded-xl" >
|
||||||
|
Retiré
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
{{if eq .Status 1 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-blue text-white text-xs font-bold rounded-xl" >
|
||||||
|
A venir
|
||||||
|
</span>
|
||||||
|
{{else if eq .Status 0 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-green text-white text-xs font-bold rounded-xl" >
|
||||||
|
En cours
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{$allAvailable := true}}
|
||||||
|
{{range .Bookings}}
|
||||||
|
{{if ne .Status -1}}
|
||||||
|
{{ $allAvailable = false }}
|
||||||
|
{{break}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if $allAvailable}}
|
||||||
|
<span class="mt-1 p-1 bg-gray-500 text-white text-xs font-bold rounded-xl" >
|
||||||
|
Disponible
|
||||||
|
</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{(index $.ViewState.admingroups (index .Administrators 0)).Data.name}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{.Data.licence_plate}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >
|
||||||
|
{{.Type}}
|
||||||
|
{{if eq .Type "Voiture"}}
|
||||||
|
{{ if .Data.automatic}} (boite auto){{ end }}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{.Data.name}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{if .Data.address}}{{.Data.address.properties.label}}{{end}}</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<a href="/app/vehicles-management/fleet/{{.ID}}"
|
||||||
|
class="text-co-blue hover:text-co-blue">Voir</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{{end}}
|
||||||
134
web/layouts/agenda/_partials/event-files.html
Normal file
134
web/layouts/agenda/_partials/event-files.html
Normal 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}}
|
||||||
@@ -4,18 +4,20 @@
|
|||||||
<h1 class="text-2xl font-semibold text-gray-900">Ajouter Ă l'agenda</h1>
|
<h1 class="text-2xl font-semibold text-gray-900">Ajouter Ă l'agenda</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8"
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 mt-8" x-data="{
|
||||||
x-data="{
|
|
||||||
fields: {
|
fields: {
|
||||||
name: null,
|
name: null,
|
||||||
type: null,
|
type: null,
|
||||||
description: null,
|
description: '',
|
||||||
allday: false,
|
allday: false,
|
||||||
startdate: null,
|
startdate: null,
|
||||||
enddate: null,
|
enddate: null,
|
||||||
starttime: null,
|
starttime: null,
|
||||||
endtime: null,
|
endtime: null,
|
||||||
max_subscribers: 0,
|
max_subscribers: 0,
|
||||||
|
file_name: null,
|
||||||
|
file_type: null,
|
||||||
|
file: null,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
name: ['required'],
|
name: ['required'],
|
||||||
@@ -25,7 +27,10 @@
|
|||||||
starttime: ['optional'],
|
starttime: ['optional'],
|
||||||
endtime: ['optional'],
|
endtime: ['optional'],
|
||||||
description: ['optional'],
|
description: ['optional'],
|
||||||
max_subscribers: ['required', 'min:0']
|
max_subscribers: ['required', 'min:0'],
|
||||||
|
file_name: ['optional'],
|
||||||
|
file_type: ['optional'],
|
||||||
|
file: ['optional'],
|
||||||
},
|
},
|
||||||
formValidation: {
|
formValidation: {
|
||||||
valid: false,
|
valid: false,
|
||||||
@@ -39,9 +44,22 @@
|
|||||||
endtime: {valid: null},
|
endtime: {valid: null},
|
||||||
allday: {valid: null},
|
allday: {valid: null},
|
||||||
max_subscribers: {valid: null},
|
max_subscribers: {valid: null},
|
||||||
|
file_name: {valid: null},
|
||||||
|
file_type: {valid: null},
|
||||||
|
file: {valid: null},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isFormValid: true,
|
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() {
|
validate() {
|
||||||
this.formValidation = Iodine.assert(this.fields, this.rules)
|
this.formValidation = Iodine.assert(this.fields, this.rules)
|
||||||
|
|
||||||
@@ -62,12 +80,14 @@
|
|||||||
return this.formValidation.valid
|
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="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:grid md:grid-cols-3 md:gap-6">
|
||||||
<div class="md:col-span-1">
|
<div class="md:col-span-1">
|
||||||
<h3 class="text-lg font-medium leading-6 text-gray-900">Informations sur le dispositif</h3>
|
<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>
|
||||||
<div class="mt-5 md:mt-0 md:col-span-2">
|
<div class="mt-5 md:mt-0 md:col-span-2">
|
||||||
<div class="grid grid-cols-6 gap-6">
|
<div class="grid grid-cols-6 gap-6">
|
||||||
@@ -80,8 +100,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-3">
|
<div class="sm:col-span-3">
|
||||||
<label for="type" class="block text-sm font-medium text-gray-700">Type de dispositif</label>
|
<label for="type" class="block text-sm font-medium text-gray-700">Type de dispositif</label>
|
||||||
<select id="type" name="type"
|
<select id="type" name="type" x-model="fields.type" @blur="validateField('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="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'">
|
:class="formValidation.fields.type.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
<option></option>
|
<option></option>
|
||||||
@@ -94,10 +113,12 @@
|
|||||||
<div class="col-span-6">
|
<div class="col-span-6">
|
||||||
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
|
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<textarea rows="4" name="description" id="descrpition"
|
<!-- <textarea rows="4" name="description" id="descrpition" x-model="fields.description"
|
||||||
x-model="fields.description" @blur="validateField('description')"
|
@blur="validateField('description')"
|
||||||
:class="formValidation.fields.description.valid == false ? 'border-co-red border-2' : 'border-gray-300'"
|
: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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -119,12 +140,14 @@
|
|||||||
|
|
||||||
<div class="col-span-6">
|
<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"
|
<button type="button"
|
||||||
:class="fields.allday ? 'bg-co-blue' : 'bg-gray-200'"
|
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">
|
@click="fields.allday = ! fields.allday">
|
||||||
<span class="sr-only">Use setting</span>
|
<span class="sr-only">Use setting</span>
|
||||||
<!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" -->
|
<!-- 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>
|
: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>
|
</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">
|
<input type="hidden" name="allday" x-model="fields.allday">
|
||||||
@@ -134,14 +157,16 @@
|
|||||||
<div class="sm:col-span-6">
|
<div class="sm:col-span-6">
|
||||||
<div class="inline-flex w-full">
|
<div class="inline-flex w-full">
|
||||||
<div class="flex-1">
|
<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"
|
<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"
|
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')"
|
x-model="fields.startdate" @blur="validateField('startdate')"
|
||||||
:class="formValidation.fields.startdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
:class="formValidation.fields.startdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<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"
|
<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"
|
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')"
|
x-model="fields.enddate" @blur="validateField('enddate')"
|
||||||
@@ -155,14 +180,16 @@
|
|||||||
<div class="sm:col-span-6" x-show="!fields.allday">
|
<div class="sm:col-span-6" x-show="!fields.allday">
|
||||||
<div class="inline-flex w-full">
|
<div class="inline-flex w-full">
|
||||||
<div class="flex-1">
|
<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"
|
<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"
|
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')"
|
x-model="fields.starttime" @blur="validateField('starttime')"
|
||||||
:class="formValidation.fields.starttime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
:class="formValidation.fields.starttime.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<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"
|
<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"
|
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')"
|
x-model="fields.endtime" @blur="validateField('endtime')"
|
||||||
@@ -184,12 +211,14 @@
|
|||||||
<div class="md:grid md:grid-cols-3 md:gap-6">
|
<div class="md:grid md:grid-cols-3 md:gap-6">
|
||||||
<div class="md:col-span-1">
|
<div class="md:col-span-1">
|
||||||
<h3 class="text-lg font-medium leading-6 text-gray-900">Paramètres</h3>
|
<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>
|
||||||
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2 align-middle">
|
<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="grid grid-cols-6 gap-6">
|
||||||
<div class="col-span-6 sm:col-span-3">
|
<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"
|
<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"
|
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')"
|
x-model="fields.max_subscribers" @blur="validateField('max_subscribers')"
|
||||||
@@ -199,8 +228,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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">
|
<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>
|
<p x-show="! isFormValid" class="px-4 py-2 text-sm text-co-red">Certains champs de sont pas valides.</p>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
<a href="/app/agenda/{{$event}}/delete" class="inline-flex"><button type="button"
|
<a href="/app/agenda/{{$event}}/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>
|
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>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -77,24 +78,35 @@
|
|||||||
{{if .ViewState.event.Description}}
|
{{if .ViewState.event.Description}}
|
||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<dt class="text-sm font-medium text-gray-500">Description</dt>
|
<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>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{ if eq (index .ViewState.event.Owners 0) .Group.ID }}
|
{{ if eq (index .ViewState.event.Owners 0) .Group.ID }}
|
||||||
<section aria-labelledby="subscribers-table"></section>
|
<section aria-labelledby="subscribers-table"></section>
|
||||||
<div class="bg-white shadow sm:rounded-lg">
|
<div class="bg-white shadow sm:rounded-lg">
|
||||||
<div class="flex justify-between px-4 py-5 sm:px-6">
|
<div class="flex justify-between px-4 py-5 sm:px-6">
|
||||||
<h2 id="event-information-title" class="text-lg leading-6 font-medium text-gray-900">Tous les inscrits</h2>
|
<h2 id="event-information-title" class="text-lg leading-6 font-medium text-gray-900">Tous les inscrits</h2>
|
||||||
|
<a href="/exports/agenda/{{$event}}">
|
||||||
|
<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/agenda/{{.ViewState.event.ID}}/history">
|
<a href="/app/agenda/{{.ViewState.event.ID}}/history">
|
||||||
|
|
||||||
<button type="submit"class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 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">
|
<button type="submit"class="rounded-2xl border border-transparent bg-co-blue px-4 py-2 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">
|
||||||
Bénéficiaires désinscrits
|
Bénéficiaires désinscrits
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{{template "subscribers_table" .}}
|
{{template "subscribers_table" .}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -194,5 +206,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</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="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>
|
</main>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
105
web/layouts/agenda/history.html
Normal file
105
web/layouts/agenda/history.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
{{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">Anciens dispositifs</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">
|
||||||
|
</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">
|
||||||
|
Dispositif
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Structure
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Lieu
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Dates
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Places disponibles
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">
|
||||||
|
Bénéficiaires positionnés
|
||||||
|
</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.events}}
|
||||||
|
{{if eq .Deleted false}}
|
||||||
|
<a href="/app/agenda/{{.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>
|
||||||
|
<div class="text-gray-500">{{.Type}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
|
||||||
|
<div class="text-gray-900" >
|
||||||
|
{{range .Owners}}
|
||||||
|
{{if (index $.ViewState.groups .)}}
|
||||||
|
{{(index $.ViewState.groups .).Data.name}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >{{if .Data.address}}{{.Data.address.properties.label}}{{end}}</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="text-gray-900" >
|
||||||
|
{{if eq .Startdate .Enddate}}
|
||||||
|
Le {{(timeFrom .Startdate).Format "02/01/2006"}}
|
||||||
|
{{else}}
|
||||||
|
Du {{(timeFrom .Startdate).Format "02/01/2006"}} <br />Au {{(timeFrom .Enddate).Format "02/01/2006"}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
{{if ne .RemainingSubscriptions 999}}
|
||||||
|
<div class="text-gray-900" >{{.RemainingSubscriptions}}</div>
|
||||||
|
{{end}}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="flex -space-x-1 overflow-hidden" >
|
||||||
|
{{range .Subscriptions}}
|
||||||
|
<img class="inline-block h-6 w-6 rounded-co ring-2 ring-white" src="/app/beneficiaries/{{.Subscriber}}/picture" >
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<a href="/app/agenda/{{.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}}
|
||||||
@@ -7,6 +7,13 @@
|
|||||||
<p class="mt-2 text-sm text-gray-700"></p>
|
<p class="mt-2 text-sm text-gray-700"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/app/agenda/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>
|
||||||
<a href="/app/agenda/create-event">
|
<a href="/app/agenda/create-event">
|
||||||
<button type="button"
|
<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">
|
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">
|
||||||
@@ -22,7 +29,7 @@
|
|||||||
<div class="mt-8 flex flex-col">
|
<div class="mt-8 flex flex-col">
|
||||||
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<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="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">
|
<table class="min-w-full divide-y divide-gray-300">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -44,6 +44,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isFormValid: true,
|
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() {
|
validate() {
|
||||||
this.formValidation = Iodine.assert(this.fields, this.rules)
|
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>
|
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
|
||||||
<div class="mt-1">
|
<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')"
|
x-model="fields.description" @blur="validateField('description')"
|
||||||
:class="formValidation.fields.description.valid == false ? 'border-co-red border-2' : 'border-gray-300'"
|
: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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
58
web/layouts/beneficiaries/_partials/beneficiary-diags.html
Normal file
58
web/layouts/beneficiaries/_partials/beneficiary-diags.html
Normal 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}}
|
||||||
@@ -1,4 +1,63 @@
|
|||||||
{{define "beneficiary_events"}}
|
{{define "beneficiary_events"}}
|
||||||
<div class="px-4 py-6 sm:px-6">
|
{{ $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">
|
||||||
|
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Actions réalisées</h2>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200">
|
||||||
|
{{ $eventCount := len .ViewState.event }}
|
||||||
|
<ul role="list" class="divide-y divide-gray-200 flex-1">
|
||||||
|
{{if eq $eventCount 0}}
|
||||||
|
<li class="py-2 px-4">
|
||||||
|
<p class="py-5 mt-1 max-w-2xl text-sm text-gray-500">Aucun dispositif n'est prévu pour le moment.</p>
|
||||||
|
</li>
|
||||||
|
{{else}}
|
||||||
|
{{range .ViewState.event}}
|
||||||
|
{{if eq .Deleted false}}
|
||||||
|
<li class="py-5 px-4 flex">
|
||||||
|
<a href="{{.Db}}{{.ID}}" class="flex w-full">
|
||||||
|
<div class="flex-1 ml-3">
|
||||||
|
<div class="flex items-center">
|
||||||
|
|
||||||
|
{{if eq .Status 2}}
|
||||||
|
{{if eq .Icons "vehicle"}}
|
||||||
|
<span class="bg-co-green text-white rounded-full h-8 w-10 flex items-center justify-center">{{$carIcon}}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="bg-co-green text-white rounded-full h-8 w-10 flex items-center justify-center">{{$calendarIcon}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if eq .Status 1}}
|
||||||
|
{{if eq .Icons "vehicle"}}
|
||||||
|
<span class="bg-co-blue text-white rounded-full h-8 w-10 flex items-center justify-center">{{$carIcon}}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="bg-co-blue text-white rounded-full h-8 w-10 flex items-center justify-center">{{$calendarIcon}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if eq .Status 3}}
|
||||||
|
{{if eq .Icons "vehicle"}}
|
||||||
|
<span class="bg-gray-500 text-white rounded-full h-8 w-10 flex items-center justify-center">{{$carIcon}}</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="bg-gray-500 text-white rounded-full h-8 w-10 flex items-center justify-center">{{$calendarIcon}}</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if eq .Date .DateEnd}}
|
||||||
|
<p class="text-sm font-medium text-gray-500">  le {{(timeFrom .Date).Format "02/01/2006"}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="text-sm font-medium text-gray-500"> du {{(timeFrom .Date).Format "02/01/2006"}} au {{(timeFrom .DateEnd).Format "02/01/2006"}}</p>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<p class="mt-1 text-sm text-gray-900">{{.Type}} - {{.Name}}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -81,7 +81,7 @@ x-data="{
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<h3 class="text-lg">Ajouter un document</h3>
|
<h3 class="text-lg">Ajouter un document</h3>
|
||||||
<form method="POST" action="/app/beneficiaries/e7616eac-4a87-4396-a505-23e0421b9c4c/documents" @submit="submit" enctype="multipart/form-data">
|
<form method="POST" action="/app/beneficiaries/{{.ViewState.beneficiary.ID}}/documents" @submit="submit" enctype="multipart/form-data">
|
||||||
<div class="md:grid md:grid-cols-6 p-2">
|
<div class="md:grid md:grid-cols-6 p-2">
|
||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
|
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
|
||||||
|
|||||||
46
web/layouts/beneficiaries/_partials/beneficiary-wallet.html
Normal file
46
web/layouts/beneficiaries/_partials/beneficiary-wallet.html
Normal 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}}
|
||||||
110
web/layouts/beneficiaries/create-diag.html
Normal file
110
web/layouts/beneficiaries/create-diag.html
Normal 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}}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
email: ['required', 'email'],
|
email: ['required', 'email'],
|
||||||
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
|
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
|
||||||
birthdate: ['required'],
|
birthdate: ['required'],
|
||||||
file_number: ['optional'],
|
file_number: ['required'],
|
||||||
},
|
},
|
||||||
formValidation: {
|
formValidation: {
|
||||||
valid: false,
|
valid: false,
|
||||||
@@ -100,6 +100,13 @@
|
|||||||
x-model="fields.birthdate" @blur="validateField('birthdate')"
|
x-model="fields.birthdate" @blur="validateField('birthdate')"
|
||||||
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</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>
|
||||||
|
<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')"
|
||||||
|
:class="formValidation.fields.file_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,14 +121,6 @@
|
|||||||
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
|
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
|
||||||
<div class="grid grid-cols-6 gap-6">
|
<div class="grid grid-cols-6 gap-6">
|
||||||
|
|
||||||
<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 (allocataire, ...)</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')"
|
|
||||||
:class="formValidation.fields.file_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-6 sm:col-span-3">
|
<div class="col-span-6 sm:col-span-3">
|
||||||
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
|
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
|
||||||
<div class="sm:mt-0 sm:col-span-2">
|
<div class="sm:mt-0 sm:col-span-2">
|
||||||
|
|||||||
@@ -69,6 +69,12 @@
|
|||||||
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.address.properties.label}}</dd>
|
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.address.properties.label}}</dd>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{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>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.beneficiary.Data.file_number}}</dd>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,10 +133,20 @@
|
|||||||
:class="tab == 'documents' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
|
:class="tab == 'documents' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
|
||||||
Documents </a>
|
Documents </a>
|
||||||
|
|
||||||
|
<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 = 'organizations'"
|
<a href="#" @click="tab = 'organizations'"
|
||||||
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
|
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'">
|
:class="tab == 'organizations' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
|
||||||
Gestionnaires </a>
|
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>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,17 +154,16 @@
|
|||||||
|
|
||||||
<div x-show="tab == 'documents'">{{template "beneficiary_files" .}}</div>
|
<div x-show="tab == 'documents'">{{template "beneficiary_files" .}}</div>
|
||||||
<div x-show="tab == 'notes'">{{template "beneficiary_notes" .}}</div>
|
<div x-show="tab == 'notes'">{{template "beneficiary_notes" .}}</div>
|
||||||
|
<div x-show="tab == 'wallet'">{{template "beneficiary_wallet" .}}</div>
|
||||||
<div x-show="tab == 'organizations'">{{template "beneficiary_organizations" .}}</div>
|
<div x-show="tab == 'organizations'">{{template "beneficiary_organizations" .}}</div>
|
||||||
|
<!--<div x-show="tab == 'diags'">{{template "beneficiary_diags" .}}</div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section aria-labelledby="timeline-title" class="lg:col-start-3 lg:col-span-1">
|
<section aria-labelledby="timeline-title" class="lg:col-start-3 lg:col-span-1">
|
||||||
<div class="bg-white px-4 py-5 shadow sm:rounded-lg sm:px-6">
|
{{template "beneficiary_events" .}}
|
||||||
<h2 id="timeline-title" class="text-lg font-medium text-gray-900">Actions réalisées</h2>
|
|
||||||
<p class="p-12 text-gray-500 text-center text-md">Aucune action réalisée pour le moment</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<div class="mt-8 flex flex-col">
|
<div class="mt-8 flex flex-col">
|
||||||
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<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="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">
|
<table class="min-w-full divide-y divide-gray-300">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
email: ['required', 'email'],
|
email: ['required', 'email'],
|
||||||
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
|
phone_number: ['required', 'regexMatch:^((\\+)33|0)[1-9](\\d{2}){4}$'],
|
||||||
birthdate: ['required'],
|
birthdate: ['required'],
|
||||||
file_number: ['optional'],
|
file_number: ['required'],
|
||||||
},
|
},
|
||||||
formValidation: {
|
formValidation: {
|
||||||
valid: false,
|
valid: false,
|
||||||
@@ -101,6 +101,15 @@
|
|||||||
x-model="fields.birthdate" @blur="validateField('birthdate')"
|
x-model="fields.birthdate" @blur="validateField('birthdate')"
|
||||||
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
:class="formValidation.fields.birthdate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</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>
|
||||||
|
<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')"
|
||||||
|
:class="formValidation.fields.file_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,15 +123,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
|
<div class="mt-5 space-y-6 md:mt-0 md:col-span-2">
|
||||||
<div class="grid grid-cols-6 gap-6">
|
<div class="grid grid-cols-6 gap-6">
|
||||||
|
|
||||||
<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 (allocataire, ...)</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')"
|
|
||||||
:class="formValidation.fields.file_number.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-span-6 sm:col-span-3">
|
<div class="col-span-6 sm:col-span-3">
|
||||||
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
|
<label for="gender" class="block text-sm font-medium text-gray-700">Genre</label>
|
||||||
<div class="sm:mt-0 sm:col-span-2">
|
<div class="sm:mt-0 sm:col-span-2">
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul role="list" class="divide-y divide-gray-200 flex-1">
|
<ul role="list" class="divide-y divide-gray-200 flex-1">
|
||||||
{{range .}}
|
{{range .}}
|
||||||
|
{{if eq .Deleted false}}
|
||||||
<li class="py-2 px-4 flex">
|
<li class="py-2 px-4 flex">
|
||||||
<a href="/app/agenda/{{.ID}}" class="flex w-full">
|
<a href="/app/agenda/{{.ID}}" class="flex w-full">
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<a href="/app/agenda/">
|
<a href="/app/agenda/">
|
||||||
|
|||||||
32
web/layouts/dashboard/_partials/bookings-widget.html
Normal file
32
web/layouts/dashboard/_partials/bookings-widget.html
Normal 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}}
|
||||||
@@ -69,8 +69,14 @@
|
|||||||
|
|
||||||
|
|
||||||
{{template "beneficiaries_widget" .ViewState.beneficiaries}}
|
{{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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
132
web/layouts/diags/_partials/diags-files.html
Normal file
132
web/layouts/diags/_partials/diags-files.html
Normal 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}}
|
||||||
32
web/layouts/diags/delete-diag.html
Normal file
32
web/layouts/diags/delete-diag.html
Normal 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}}
|
||||||
80
web/layouts/diags/display-diag.html
Normal file
80
web/layouts/diags/display-diag.html
Normal 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 }}
|
||||||
73
web/layouts/diags/history-diags.html
Normal file
73
web/layouts/diags/history-diags.html
Normal 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}}
|
||||||
84
web/layouts/diags/home.html
Normal file
84
web/layouts/diags/home.html
Normal 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}}
|
||||||
117
web/layouts/diags/update-diag.html
Normal file
117
web/layouts/diags/update-diag.html
Normal 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}}
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ if gt (len .ViewState.journeys.Journeys) 0}}
|
{{ if and .ViewState.journeys (gt (len .ViewState.journeys.Journeys) 0)}}
|
||||||
<div class="px-4 pt-4 flex text-sm text-grey-900 font-bold border-t-2">
|
<div class="px-4 pt-4 flex text-sm text-grey-900 font-bold border-t-2">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
{{.IconSet.Icon "tabler-icons:bus" "h-6 w-6 inline-flex mr-4"}}
|
{{.IconSet.Icon "tabler-icons:bus" "h-6 w-6 inline-flex mr-4"}}
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!--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">
|
<div class="flex-1">
|
||||||
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
|
{{.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
|
<span class=" font-bold">{{len .ViewState.vehicles}} véhicules</span> partagés disponibles ce jour là et la semaine suivante
|
||||||
@@ -100,6 +100,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="p-4 text-center">
|
<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>
|
<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>
|
</div>-->
|
||||||
|
|
||||||
|
<h3 class="p-4 text-lg">Transport solidaire</h3>
|
||||||
|
|
||||||
|
{{template "journeys_solidarity_transport" .}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{{define "journeys_others"}}
|
{{define "journeys_others"}}
|
||||||
|
|
||||||
<!--VEHICLES-->
|
<!--VEHICLES-->
|
||||||
|
{{if moduleAvailable "vehicles"}}
|
||||||
<div class="p-4 flex text-sm text-grey-900">
|
<div class="p-4 flex text-sm text-grey-900">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
|
{{.IconSet.Icon "tabler-icons:car" "h-6 w-6 inline-flex mr-4"}}
|
||||||
@@ -34,5 +35,5 @@
|
|||||||
<div class="p-4 text-center">
|
<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>
|
<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>
|
</div>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{{define "journeys_public_transit"}}
|
{{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>
|
<p class="p-12 text-gray-500 text-center text-md">Aucun transport en commun pour ce trajet.</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{$first := true}}
|
{{$first := true}}
|
||||||
|
{{if .ViewState.journeys}}
|
||||||
{{range .ViewState.journeys.Journeys}}
|
{{range .ViewState.journeys.Journeys}}
|
||||||
{{if $first}}
|
{{if $first}}
|
||||||
{{$first = false}}
|
{{$first = false}}
|
||||||
@@ -79,3 +80,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
{{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}}
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
</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">
|
||||||
|
<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}}
|
||||||
@@ -39,14 +39,14 @@
|
|||||||
<label for="departuredate" class="block text-sm font-medium text-gray-700">Le</label>
|
<label for="departuredate" class="block text-sm font-medium text-gray-700">Le</label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<input type="date" id="departuredate" name="departuredate" value="{{.ViewState.departuredate}}"
|
<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>
|
</div>
|
||||||
<div class="lg:col-span-1">
|
<div class="lg:col-span-1">
|
||||||
<label for="departuretime" class="block text-sm font-medium text-gray-700">A</label>
|
<label for="departuretime" class="block text-sm font-medium text-gray-700">A</label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<input type="time" id="departuretime" name="departuretime" value="{{.ViewState.departuretime}}"
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
<option value="public-transit">Transports</option>
|
<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>
|
<option value="others">Autres</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -105,10 +105,10 @@
|
|||||||
:class="tab == 'public-transit' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
|
: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>
|
Transports </a>
|
||||||
|
|
||||||
<!-- <a href="#" @click="tab = 'active-modes'"
|
<a href="#" @click="tab = 'solidarity-transport'"
|
||||||
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
|
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'">
|
:class="tab == 'solidarity-transport' ? 'border-co-blue text-co-blue' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'">
|
||||||
Modes actifs </a> -->
|
Transport solidaire </a>
|
||||||
|
|
||||||
<a href="#" @click="tab = 'others'"
|
<a href="#" @click="tab = 'others'"
|
||||||
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
|
class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
|
||||||
@@ -122,6 +122,7 @@
|
|||||||
<div x-show="tab == 'all'">{{template "journeys_all" .}}</div>
|
<div x-show="tab == 'all'">{{template "journeys_all" .}}</div>
|
||||||
<div x-show="tab == 'carpool'">{{template "journeys_carpool" .}}</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 == 'public-transit'">{{template "journeys_public_transit" .}}</div>
|
||||||
|
<div x-show="tab == 'solidarity-transport'">{{template "journeys_solidarity_transport" .}}</div>
|
||||||
<div x-show="tab == 'others'">{{template "journeys_others" .}}</div>
|
<div x-show="tab == 'others'">{{template "journeys_others" .}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,9 +5,15 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>PARCOURSMOB</title>
|
<title>PARCOURSMOB</title>
|
||||||
<link rel="stylesheet" href="/public/css/main.css" />
|
<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" />
|
||||||
<!-- <script defer type="text/javascript" src="/public/js/main.js" defer></script> -->
|
<!-- <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 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://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
|
||||||
|
<script src="https://unpkg.com/maplibre-gl@^5.1.1/dist/maplibre-gl.js"></script>
|
||||||
|
<script src="https://unpkg.com/pmtiles@^4.3.0/dist/pmtiles.js"></script>
|
||||||
|
<link href="https://unpkg.com/maplibre-gl@^5.1.1/dist/maplibre-gl.css" rel="stylesheet" />
|
||||||
|
<script src="https://unpkg.com/@protomaps/basemaps@5/dist/basemaps.js" crossorigin="anonymous"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="h-full" x-data="{ offCanvasMenu: false }">
|
<body class="h-full" x-data="{ offCanvasMenu: false }">
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
</div>
|
</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">
|
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 eq .UserID .ViewState.admins.ID}}
|
{{if eq .UserID .ViewState.admins.ID}}
|
||||||
<a href="/app/members/{{.ViewState.admins.ID}}/update" class="inline-flex"><button type="button"
|
<a href="/app/members/{{.ViewState.admins.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>
|
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>
|
||||||
@@ -56,10 +54,17 @@
|
|||||||
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.admins.Data.first_name}}</dd>
|
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.admins.Data.first_name}}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<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 class="mt-1 text-sm text-gray-900">{{.ViewState.admins.Data.last_name}}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sm:col-span-1">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Organisations</dt>
|
||||||
|
{{range .ViewState.groups}}
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{.}}
|
||||||
|
</dd>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
154
web/layouts/members/membersList.html
Normal file
154
web/layouts/members/membersList.html
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">La listes des référents</h1>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<a href="/app/administration/">
|
||||||
|
<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">
|
||||||
|
Retour
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8" x-data="{
|
||||||
|
state: {{.ViewState.list.JSONWithLimits 0 10}},
|
||||||
|
current: 0,
|
||||||
|
nb_pages() {
|
||||||
|
let nbEl = this.state.count
|
||||||
|
return Math.ceil(nbEl/10)
|
||||||
|
},
|
||||||
|
async paginate(page) {
|
||||||
|
let start = (page-1)*10
|
||||||
|
if(start < 0|| start > this.state.count) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let resp = await fetch('/api/cache/' + this.state.cache_id + '?limits.min=' + start + '&limits.max=' + (start+10))
|
||||||
|
let data = await resp.json()
|
||||||
|
this.state.members = data
|
||||||
|
this.current=page-1
|
||||||
|
}
|
||||||
|
}">
|
||||||
|
<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="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="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Téléphone
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
|
||||||
|
Organisations
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6">
|
||||||
|
<span class="sr-only"></span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody class="divide-y divide-gray-200 bg-white">
|
||||||
|
<template x-for="(member, i) in state.members" >
|
||||||
|
<tr>
|
||||||
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="h-10 w-10 flex-shrink-0">
|
||||||
|
<img class="h-10 w-10 rounded-co"
|
||||||
|
:src="'/app/members/' + member.id + '/picture'" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="font-medium text-gray-900"><span
|
||||||
|
x-text="member.data.first_name"></span> <span
|
||||||
|
x-text="member.data.last_name"></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
|
||||||
|
<div class="text-gray-900" x-text="member.data.phone_number"></div>
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"
|
||||||
|
x-text="member.data.email">
|
||||||
|
</td>
|
||||||
|
<template x-for="(group, index) in state.groups" >
|
||||||
|
<template x-if="i === index">
|
||||||
|
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"
|
||||||
|
x-text="group" >
|
||||||
|
</td>
|
||||||
|
</template></template>
|
||||||
|
|
||||||
|
<td
|
||||||
|
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
|
||||||
|
<a :href="'/app/members/' + member.id"
|
||||||
|
class="text-co-blue hover:text-co-blue">Voir<span class="sr-only">, <span
|
||||||
|
x-text="member.data.first_name"></span> <span
|
||||||
|
x-text="member.data.last_name"></span></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||||
|
<div class="flex-1 flex justify-between sm:hidden">
|
||||||
|
<a href="#" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
@click="paginate(current)"> Previous </a>
|
||||||
|
<a href="#" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
@click="paginate(current+2)"> Next </a>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-700">
|
||||||
|
Résultats
|
||||||
|
<span class="font-medium" x-text="Math.min((current * 10)+1, state.count)"></span>
|
||||||
|
Ă
|
||||||
|
<span class="font-medium" x-text="Math.min((current * 10)+10, state.count)"></span>
|
||||||
|
sur
|
||||||
|
<span class="font-medium" x-text="state.count"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
|
||||||
|
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
|
||||||
|
@click="paginate(current)">
|
||||||
|
<span class="sr-only">Previous</span>
|
||||||
|
|
||||||
|
<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="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<template x-for="i in nb_pages">
|
||||||
|
<a href="#" @click="paginate(i)"
|
||||||
|
class="relative inline-flex items-center px-4 py-2 border text-sm font-medium"
|
||||||
|
:class="i == current+1 ? 'z-10 bg-indigo-50 border-co-blue text-co-blue' : 'bg-white border-gray-300 text-gray-500 hover:bg-gray-50'"
|
||||||
|
x-text="i"></a>
|
||||||
|
</template>
|
||||||
|
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
|
||||||
|
@click="paginate(current+2)">
|
||||||
|
<span class="sr-only">Next</span>
|
||||||
|
<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="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
{{ 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">
|
||||||
|
|
||||||
|
</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="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/solidarity-transport/bookings/{{.Id}}">
|
||||||
|
Voir
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
@@ -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"> </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
|
||||||
|
<input name="days.tuesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mardi
|
||||||
|
<input name="days.wednesday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Mercredi
|
||||||
|
<input name="days.thursday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Jeudi
|
||||||
|
<input name="days.friday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Vendredi
|
||||||
|
<input name="days.saturday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Samedi
|
||||||
|
<input name="days.sunday" type="checkbox" class="focus:ring-co-blue h-4 w-4 text-co-blue border-gray-300 rounded"> Dimanche
|
||||||
|
</div>
|
||||||
|
<div class="my-4 inline-flex justify-items-center">
|
||||||
|
<div class="p-1">De</div>
|
||||||
|
<input type="time" id="starttime" name="starttime" value="08: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="18: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}}
|
||||||
136
web/layouts/solidarity_transport/_partials/driver_documents.html
Normal file
136
web/layouts/solidarity_transport/_partials/driver_documents.html
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
|
||||||
|
{{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/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>
|
||||||
|
<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}}
|
||||||
67
web/layouts/solidarity_transport/_partials/drivers_list.html
Normal file
67
web/layouts/solidarity_transport/_partials/drivers_list.html
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{{ 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/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">
|
||||||
|
|
||||||
|
</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">
|
||||||
|
<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/solidarity-transport/drivers/{{.ID}}">
|
||||||
|
Voir
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ end }}
|
||||||
268
web/layouts/solidarity_transport/_partials/journey_preview.html
Normal file
268
web/layouts/solidarity_transport/_partials/journey_preview.html
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
{{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">{{.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">{{ .driver_journey.PassengerPickupDate.Format "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>
|
||||||
|
<div id="map" class="w-full h-full"></div>
|
||||||
|
<script>
|
||||||
|
var map = new maplibregl.Map({
|
||||||
|
container: 'map', // container id
|
||||||
|
style: {
|
||||||
|
version: 8,
|
||||||
|
glyphs:'https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf',
|
||||||
|
sprite: "https://protomaps.github.io/basemaps-assets/sprites/v4/light",
|
||||||
|
sources: {
|
||||||
|
"protomaps": {
|
||||||
|
type: "vector",
|
||||||
|
url: "pmtiles://https://coopgo-gis-data.s3.fr-par.scw.cloud/vector-tiles/pmtiles/europe-latest.pmtiles",
|
||||||
|
// ^-- Remember to prefix the URL with pmtiles://
|
||||||
|
attribution: '<a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>'
|
||||||
|
},
|
||||||
|
"points": {
|
||||||
|
type: "geojson",
|
||||||
|
data: {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{{ json .driver_journey.DriverDeparture }},
|
||||||
|
{{ json .driver_journey.PassengerPickup }},
|
||||||
|
{{ json .driver_journey.PassengerDrop }},
|
||||||
|
{{ json .driver_journey.DriverArrival }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layers: basemaps.layers("protomaps",basemaps.namedFlavor("light"),{lang:"en"})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</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}}
|
||||||
35
web/layouts/solidarity_transport/booking_display.html
Normal file
35
web/layouts/solidarity_transport/booking_display.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{{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 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é</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">
|
||||||
|
<!-- <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 "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>
|
||||||
|
{{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 }}
|
||||||
173
web/layouts/solidarity_transport/driver_create.html
Normal file
173
web/layouts/solidarity_transport/driver_create.html
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
{{ 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,
|
||||||
|
},
|
||||||
|
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 }}
|
||||||
|
</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-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}}
|
||||||
|
|
||||||
138
web/layouts/solidarity_transport/driver_display.html
Normal file
138
web/layouts/solidarity_transport/driver_display.html
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
{{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">
|
||||||
|
<!-- <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</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}}
|
||||||
|
</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}}
|
||||||
18
web/layouts/solidarity_transport/driver_journey.html
Normal file
18
web/layouts/solidarity_transport/driver_journey.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
{{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>
|
||||||
|
<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 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 }}
|
||||||
48
web/layouts/solidarity_transport/overview.html
Normal file
48
web/layouts/solidarity_transport/overview.html
Normal 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">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>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
{{define "content"}}
|
{{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>
|
<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">
|
<div class="bg-white py-2 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
<p class="text-sm text-gray-600 p-4">
|
<p class="text-sm text-gray-600 p-4">
|
||||||
@@ -12,7 +23,9 @@
|
|||||||
<div class="py-2 px-4 rounded-3xl bg-white dark:bg-gray-800">
|
<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>
|
||||||
<div class="flex justify-center items-center py-2 px-3 border-t dark:border-gray-600">
|
<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">
|
<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">
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
|
<div class="sm:flex sm:items-center">
|
||||||
|
<div class="sm:flex-auto">
|
||||||
<h1 class="text-2xl font-semibold text-gray-900">Réservations</h1>
|
<h1 class="text-2xl font-semibold text-gray-900">Réservations</h1>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||||
|
<!-- <a href="/app/administration/stats/vehicles/available">
|
||||||
|
<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">
|
||||||
|
Voitures disponibles
|
||||||
|
</button>
|
||||||
|
</a> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
|
||||||
@@ -42,7 +54,7 @@
|
|||||||
|
|
||||||
{{range .ViewState.bookings}}
|
{{range .ViewState.bookings}}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6 text-center">
|
<td class="flex-col py-4 pl-4 pr-3 text-sm sm:pl-6 text-center">
|
||||||
{{if .Data.administrator_unavailability}}
|
{{if .Data.administrator_unavailability}}
|
||||||
<span class="p-1 bg-black text-white text-xs font-bold rounded-xl" >
|
<span class="p-1 bg-black text-white text-xs font-bold rounded-xl" >
|
||||||
Retiré
|
Retiré
|
||||||
@@ -68,6 +80,7 @@
|
|||||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
<div class="text-gray-900" >{{(index $.ViewState.vehicles_map .Vehicleid).Type}}</div>
|
<div class="text-gray-900" >{{(index $.ViewState.vehicles_map .Vehicleid).Type}}</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
<div class="text-gray-900" >{{(index $.ViewState.vehicles_map .Vehicleid).Data.licence_plate}}</div>
|
<div class="text-gray-900" >{{(index $.ViewState.vehicles_map .Vehicleid).Data.licence_plate}}</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
110
web/layouts/vehicles/create-booking-diag.html
Normal file
110
web/layouts/vehicles/create-booking-diag.html
Normal 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}}
|
||||||
@@ -147,6 +147,20 @@
|
|||||||
<div class="lg:col-start-2 lg:col-span-2">
|
<div class="lg:col-start-2 lg:col-span-2">
|
||||||
{{if .ViewState.searched}}
|
{{if .ViewState.searched}}
|
||||||
<div class="bg-white px-4 py-5 shadow sm:rounded-2xl sm:px-6">
|
<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>
|
<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="mt-8 flex flex-col">
|
||||||
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||||
|
|||||||
56
web/layouts/vehicles_management/_partials/booking-diags.html
Normal file
56
web/layouts/vehicles_management/_partials/booking-diags.html
Normal 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}}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="mt-8 flex flex-col">
|
<div class="mt-8 flex flex-col">
|
||||||
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<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="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">
|
<table class="min-w-full divide-y divide-gray-300">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -67,8 +67,11 @@
|
|||||||
{{if .Data.administrator_unavailability}}
|
{{if .Data.administrator_unavailability}}
|
||||||
<div class="text-gray-900" ></div>
|
<div class="text-gray-900" ></div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="text-gray-900" ><img class="h-6 w-6 rounded-co"
|
<div class="text-gray-900" >
|
||||||
src="/app/beneficiaries/{{.Driver}}/picture" alt=""></div>
|
<!--<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}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm sm:pl-6">
|
||||||
|
|||||||
56
web/layouts/vehicles_management/_partials/vehicle-diags.html
Normal file
56
web/layouts/vehicles_management/_partials/vehicle-diags.html
Normal 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}}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="mt-8 flex flex-col">
|
<div class="mt-8 flex flex-col">
|
||||||
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<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="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">
|
<table class="min-w-full divide-y divide-gray-300">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -48,6 +48,14 @@
|
|||||||
<span class="mt-1 p-1 bg-gray-500 text-white text-xs font-bold rounded-xl" >
|
<span class="mt-1 p-1 bg-gray-500 text-white text-xs font-bold rounded-xl" >
|
||||||
Disponible
|
Disponible
|
||||||
</span>
|
</span>
|
||||||
|
{{else if eq .Status 1 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-blue text-white text-xs font-bold rounded-xl" >
|
||||||
|
A venir
|
||||||
|
</span>
|
||||||
|
{{else if eq .Status 0 }}
|
||||||
|
<span class="mt-1 p-1 bg-co-green text-white text-xs font-bold rounded-xl" >
|
||||||
|
En cours
|
||||||
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if gt (len .Bookings) 1}}
|
{{else if gt (len .Bookings) 1}}
|
||||||
@@ -72,7 +80,7 @@
|
|||||||
{{range .Bookings}}
|
{{range .Bookings}}
|
||||||
{{if ne .Status -1}}
|
{{if ne .Status -1}}
|
||||||
{{ $allAvailable = false }}
|
{{ $allAvailable = false }}
|
||||||
{{ break }}
|
{{break}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if $allAvailable}}
|
{{if $allAvailable}}
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
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">
|
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
|
Changer de véhicule
|
||||||
</button>
|
</button>
|
||||||
<!-- <a href="/app/vehicles-management/bookings/{{.ViewState.booking.ID}}/delete">
|
<a href="/app/vehicles-management/bookings/{{.ViewState.booking.ID}}/delete">
|
||||||
<button type="button"
|
<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">
|
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
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
</a> -->
|
</a>
|
||||||
|
|
||||||
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true" x-show="changeVehicle">
|
<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>
|
<div class="fixed inset-0 bg-gray-900 bg-opacity-30 transition-opacity"></div>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<select x-model="selectedvehicle" id="vehicle" name="vehicle" class="mt-1 block w-full rounded-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-co-blue focus:outline-none focus:ring-co-blue sm:text-sm">
|
<select x-model="selectedvehicle" id="vehicle" name="vehicle" class="mt-1 block w-full rounded-2xl border-gray-300 py-2 pl-3 pr-10 text-base focus:border-co-blue focus:outline-none focus:ring-co-blue sm:text-sm">
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
{{range .ViewState.alternative_vehicles }}
|
{{range .ViewState.alternative_vehicles }}
|
||||||
<option value="{{.ID}}">{{.Data.name}} ({{.Type}})</option>
|
<option value="{{.ID}}">{{.Data.licence_plate}} - {{.Data.name}} ({{.Type}})</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<div class="mt-5 sm:mt-6">
|
<div class="mt-5 sm:mt-6">
|
||||||
@@ -103,6 +103,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--<div>
|
||||||
|
{{template "booking_diags" .}}
|
||||||
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
<div class="lg:col-start-2 lg:col-span-2">
|
<div class="lg:col-start-2 lg:col-span-2">
|
||||||
<div class="bg-white shadow sm:rounded-2xl sm:px-6">
|
<div class="bg-white shadow sm:rounded-2xl sm:px-6">
|
||||||
@@ -158,6 +161,11 @@
|
|||||||
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
{{.ViewState.vehicle.Data.licence_plate}}</dd>
|
{{.ViewState.vehicle.Data.licence_plate}}</dd>
|
||||||
</div>
|
</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">
|
<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>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
|||||||
116
web/layouts/vehicles_management/create-booking-diag.html
Normal file
116
web/layouts/vehicles_management/create-booking-diag.html
Normal 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}}
|
||||||
110
web/layouts/vehicles_management/create-vehicle-diag.html
Normal file
110
web/layouts/vehicles_management/create-vehicle-diag.html
Normal 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}}
|
||||||
39
web/layouts/vehicles_management/delete-booking.html
Normal file
39
web/layouts/vehicles_management/delete-booking.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{{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 d'annulation</h3>
|
||||||
|
<div class="mt-2">
|
||||||
|
<p class="text-sm text-gray-500">Souhaitez-vous vraiment annuler cette réservaton ?</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label for="motif" class="block text-sm font-medium text-gray-700">Motif</label>
|
||||||
|
<div class="mt-1">
|
||||||
|
<textarea rows="4" name="motif" id="motif"
|
||||||
|
class="shadow-sm focus:ring-co-blue focus:border-co-blue block w-full sm:text-sm border-gray-300 rounded-2xl"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2">
|
||||||
|
<a href="/app/vehicles-management/bookings/{{.ViewState.booking.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}}
|
||||||
@@ -10,11 +10,13 @@
|
|||||||
licence_plate: null,
|
licence_plate: null,
|
||||||
name: null,
|
name: null,
|
||||||
type: null,
|
type: null,
|
||||||
|
informations: '',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
licence_plate: ['required'], // 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'
|
licence_plate: ['required'], // 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'
|
||||||
name: ['required'],
|
name: ['required'],
|
||||||
type: ['required'],
|
type: ['required'],
|
||||||
|
informations: ['optional']
|
||||||
},
|
},
|
||||||
formValidation: {
|
formValidation: {
|
||||||
valid: false,
|
valid: false,
|
||||||
@@ -22,9 +24,19 @@
|
|||||||
name: {valid: null},
|
name: {valid: null},
|
||||||
licence_plate: {valid: null},
|
licence_plate: {valid: null},
|
||||||
type: {valid: null},
|
type: {valid: null},
|
||||||
|
informations: {valid: null},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isFormValid: true,
|
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() {
|
validate() {
|
||||||
this.formValidation = Iodine.assert(this.fields, this.rules)
|
this.formValidation = Iodine.assert(this.fields, this.rules)
|
||||||
},
|
},
|
||||||
@@ -94,6 +106,13 @@
|
|||||||
@blur="fields.licence_plate = fields.licence_plate.toUpperCase(); validateField('licence_plate')"
|
@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'">
|
:class="formValidation.fields.licence_plate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,8 +130,8 @@
|
|||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques</label>
|
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques</label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<textarea rows="4" name="informations" id="informations"
|
<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>
|
||||||
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="informations" x-model="fields.informations" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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">
|
<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"
|
<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>
|
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"
|
<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> -->
|
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>
|
</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="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>
|
<dd class="mt-1 text-sm text-gray-900">{{.ViewState.vehicle.Data.licence_plate}}</dd>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{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}}
|
{{if .ViewState.vehicle.Data.address}}
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1">
|
||||||
<dt class="text-sm font-medium text-gray-500">Lieu</dt>
|
<dt class="text-sm font-medium text-gray-500">Lieu</dt>
|
||||||
@@ -63,7 +69,7 @@
|
|||||||
{{if .ViewState.vehicle.Data.informations}}
|
{{if .ViewState.vehicle.Data.informations}}
|
||||||
<div class="sm:col-span-2">
|
<div class="sm:col-span-2">
|
||||||
<dt class="text-sm font-medium text-gray-500">Informations pratiques pour le bénéficiaire</dt>
|
<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>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
@@ -128,8 +134,10 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<div class="text-gray-900" >
|
<div class="text-gray-900" >
|
||||||
<a href="/app/beneficiaries/{{.Driver}}">
|
<a href="/app/beneficiaries/{{.Driver}}">
|
||||||
<img class="h-6 w-6 rounded-co"
|
<!--<img class="h-6 w-6 rounded-co"
|
||||||
src="/app/beneficiaries/{{.Driver}}/picture" alt="">
|
src="/app/beneficiaries/{{.Driver}}/picture" alt="">-->
|
||||||
|
{{(index $.ViewState.beneficiaries .Driver).Data.first_name}}
|
||||||
|
{{(index $.ViewState.beneficiaries .Driver).Data.last_name}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
fields: {
|
fields: {
|
||||||
licence_plate: '{{ .ViewState.vehicle.Data.licence_plate }}',
|
licence_plate: '{{ .ViewState.vehicle.Data.licence_plate }}',
|
||||||
name: '{{ .ViewState.vehicle.Data.name }}',
|
name: '{{ .ViewState.vehicle.Data.name }}',
|
||||||
|
kilometers: '{{ .ViewState.vehicle.Data.kilometers }}',
|
||||||
|
informations: '{{ .ViewState.vahicle.Data.informations}}',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
licence_plate: ['required', 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'],
|
licence_plate: ['required', 'regexMatch:^[A-Z]{1,2}-[0-9]{1,3}-[A-Z]{1,2}$'],
|
||||||
@@ -22,6 +24,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isFormValid: true,
|
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() {
|
validate() {
|
||||||
this.formValidation = Iodine.assert(this.fields, this.rules)
|
this.formValidation = Iodine.assert(this.fields, this.rules)
|
||||||
},
|
},
|
||||||
@@ -35,7 +46,12 @@
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
return this.formValidation.valid
|
return this.formValidation.valid
|
||||||
|
},
|
||||||
|
|
||||||
|
displayAutomatic(type) {
|
||||||
|
return type == 'Voiture'
|
||||||
}
|
}
|
||||||
|
|
||||||
}">
|
}">
|
||||||
<form class="space-y-6" method="POST" @submit="submit">
|
<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="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'">
|
:class="formValidation.fields.name.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3">
|
<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>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
<label for="licence_plate"
|
<label for="licence_plate"
|
||||||
@@ -66,6 +105,15 @@
|
|||||||
@blur="fields.licence_plate = fields.licence_plate.toUpperCase(); validateField('licence_plate')"
|
@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'">
|
:class="formValidation.fields.licence_plate.valid == false ? 'border-co-red border-2' : 'border-gray-300'">
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,8 +131,8 @@
|
|||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques pour le bénéficiaire</label>
|
<label for="informations" class="block text-sm font-medium text-gray-700">Informations pratiques pour le bénéficiaire</label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<textarea rows="4" name="informations" id="informations"
|
<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>
|
||||||
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="informations" x-model="fields.informations" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
web/node_modules/.bin/detect-libc
generated
vendored
Symbolic link
1
web/node_modules/.bin/detect-libc
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../detect-libc/bin/detect-libc.js
|
||||||
1
web/node_modules/.bin/jiti
generated
vendored
Symbolic link
1
web/node_modules/.bin/jiti
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../jiti/lib/jiti-cli.mjs
|
||||||
1
web/node_modules/.bin/tailwindcss
generated
vendored
Symbolic link
1
web/node_modules/.bin/tailwindcss
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../@tailwindcss/cli/dist/index.mjs
|
||||||
345
web/node_modules/.package-lock.json
generated
vendored
Normal file
345
web/node_modules/.package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
{
|
||||||
|
"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/@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/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/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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
web/node_modules/@parcel/watcher-linux-x64-glibc/LICENSE
generated
vendored
Normal file
21
web/node_modules/@parcel/watcher-linux-x64-glibc/LICENSE
generated
vendored
Normal 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.
|
||||||
1
web/node_modules/@parcel/watcher-linux-x64-glibc/README.md
generated
vendored
Normal file
1
web/node_modules/@parcel/watcher-linux-x64-glibc/README.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is the linux-x64-glibc build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.
|
||||||
33
web/node_modules/@parcel/watcher-linux-x64-glibc/package.json
generated
vendored
Normal file
33
web/node_modules/@parcel/watcher-linux-x64-glibc/package.json
generated
vendored
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
web/node_modules/@parcel/watcher-linux-x64-glibc/watcher.node
generated
vendored
Normal file
BIN
web/node_modules/@parcel/watcher-linux-x64-glibc/watcher.node
generated
vendored
Normal file
Binary file not shown.
21
web/node_modules/@parcel/watcher/LICENSE
generated
vendored
Normal file
21
web/node_modules/@parcel/watcher/LICENSE
generated
vendored
Normal 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
135
web/node_modules/@parcel/watcher/README.md
generated
vendored
Normal 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
93
web/node_modules/@parcel/watcher/binding.gyp
generated
vendored
Normal 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
49
web/node_modules/@parcel/watcher/index.d.ts
generated
vendored
Normal 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
41
web/node_modules/@parcel/watcher/index.js
generated
vendored
Normal 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
48
web/node_modules/@parcel/watcher/index.js.flow
generated
vendored
Normal 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
88
web/node_modules/@parcel/watcher/package.json
generated
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
web/node_modules/@parcel/watcher/scripts/build-from-source.js
generated
vendored
Normal file
13
web/node_modules/@parcel/watcher/scripts/build-from-source.js
generated
vendored
Normal 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
182
web/node_modules/@parcel/watcher/src/Backend.cc
generated
vendored
Normal 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
37
web/node_modules/@parcel/watcher/src/Backend.hh
generated
vendored
Normal 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
113
web/node_modules/@parcel/watcher/src/Debounce.cc
generated
vendored
Normal 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
|
||||||
|
}
|
||||||
49
web/node_modules/@parcel/watcher/src/Debounce.hh
generated
vendored
Normal file
49
web/node_modules/@parcel/watcher/src/Debounce.hh
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef DEBOUNCE_H
|
||||||
|
#define DEBOUNCE_H
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include "Signal.hh"
|
||||||
|
|
||||||
|
#define MIN_WAIT_TIME 50
|
||||||
|
#define MAX_WAIT_TIME 500
|
||||||
|
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" {
|
||||||
|
int set_timeout(int ms, void *ctx);
|
||||||
|
void clear_timeout(int timeout);
|
||||||
|
void on_timeout(void *ctx);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Debounce {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Debounce> getShared();
|
||||||
|
|
||||||
|
Debounce();
|
||||||
|
~Debounce();
|
||||||
|
|
||||||
|
void add(void *key, std::function<void()> cb);
|
||||||
|
void remove(void *key);
|
||||||
|
void trigger();
|
||||||
|
void notify();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mRunning;
|
||||||
|
std::mutex mMutex;
|
||||||
|
#ifdef __wasm32__
|
||||||
|
int mTimeout;
|
||||||
|
#else
|
||||||
|
Signal mWaitSignal;
|
||||||
|
std::thread mThread;
|
||||||
|
#endif
|
||||||
|
std::unordered_map<void *, std::function<void()>> mCallbacks;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> mLastTime;
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
void notifyIfReady();
|
||||||
|
void wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
152
web/node_modules/@parcel/watcher/src/DirTree.cc
generated
vendored
Normal file
152
web/node_modules/@parcel/watcher/src/DirTree.cc
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#include "DirTree.hh"
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
static std::mutex mDirCacheMutex;
|
||||||
|
static std::unordered_map<std::string, std::weak_ptr<DirTree>> dirTreeCache;
|
||||||
|
|
||||||
|
struct DirTreeDeleter {
|
||||||
|
void operator()(DirTree *tree) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex);
|
||||||
|
dirTreeCache.erase(tree->root);
|
||||||
|
delete tree;
|
||||||
|
|
||||||
|
// Free up memory.
|
||||||
|
if (dirTreeCache.size() == 0) {
|
||||||
|
dirTreeCache.rehash(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<DirTree> DirTree::getCached(std::string root) {
|
||||||
|
std::lock_guard<std::mutex> lock(mDirCacheMutex);
|
||||||
|
|
||||||
|
auto found = dirTreeCache.find(root);
|
||||||
|
std::shared_ptr<DirTree> tree;
|
||||||
|
|
||||||
|
// Use cached tree, or create an empty one.
|
||||||
|
if (found != dirTreeCache.end()) {
|
||||||
|
tree = found->second.lock();
|
||||||
|
} else {
|
||||||
|
tree = std::shared_ptr<DirTree>(new DirTree(root), DirTreeDeleter());
|
||||||
|
dirTreeCache.emplace(root, tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirTree::DirTree(std::string root, FILE *f) : root(root), isComplete(true) {
|
||||||
|
size_t size;
|
||||||
|
if (fscanf(f, "%zu", &size)) {
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
DirEntry entry(f);
|
||||||
|
entries.emplace(entry.path, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal find method that has no lock
|
||||||
|
DirEntry *DirTree::_find(std::string path) {
|
||||||
|
auto found = entries.find(path);
|
||||||
|
if (found == entries.end()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
DirEntry entry(path, mtime, isDir);
|
||||||
|
auto it = entries.emplace(entry.path, entry);
|
||||||
|
return &it.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::find(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
return _find(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry *DirTree::update(std::string path, uint64_t mtime) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
DirEntry *found = _find(path);
|
||||||
|
if (found) {
|
||||||
|
found->mtime = mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::remove(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
DirEntry *found = _find(path);
|
||||||
|
|
||||||
|
// Remove all sub-entries if this is a directory
|
||||||
|
if (found && found->isDir) {
|
||||||
|
std::string pathStart = path + DIR_SEP;
|
||||||
|
for (auto it = entries.begin(); it != entries.end();) {
|
||||||
|
if (it->first.rfind(pathStart, 0) == 0) {
|
||||||
|
it = entries.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.erase(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::write(FILE *f) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
fprintf(f, "%zu\n", entries.size());
|
||||||
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
||||||
|
it->second.write(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirTree::getChanges(DirTree *snapshot, EventList &events) {
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
std::lock_guard<std::mutex> snapshotLock(snapshot->mMutex);
|
||||||
|
|
||||||
|
for (auto it = entries.begin(); it != entries.end(); it++) {
|
||||||
|
auto found = snapshot->entries.find(it->first);
|
||||||
|
if (found == snapshot->entries.end()) {
|
||||||
|
events.create(it->second.path);
|
||||||
|
} else if (found->second.mtime != it->second.mtime && !found->second.isDir && !it->second.isDir) {
|
||||||
|
events.update(it->second.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = snapshot->entries.begin(); it != snapshot->entries.end(); it++) {
|
||||||
|
size_t count = entries.count(it->first);
|
||||||
|
if (count == 0) {
|
||||||
|
events.remove(it->second.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry::DirEntry(std::string p, uint64_t t, bool d) {
|
||||||
|
path = p;
|
||||||
|
mtime = t;
|
||||||
|
isDir = d;
|
||||||
|
state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirEntry::DirEntry(FILE *f) {
|
||||||
|
size_t size;
|
||||||
|
if (fscanf(f, "%zu", &size)) {
|
||||||
|
path.resize(size);
|
||||||
|
if (fread(&path[0], sizeof(char), size, f)) {
|
||||||
|
int d = 0;
|
||||||
|
fscanf(f, "%" PRIu64 " %d\n", &mtime, &d);
|
||||||
|
isDir = d == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirEntry::write(FILE *f) const {
|
||||||
|
fprintf(f, "%zu%s%" PRIu64 " %d\n", path.size(), path.c_str(), mtime, isDir);
|
||||||
|
}
|
||||||
50
web/node_modules/@parcel/watcher/src/DirTree.hh
generated
vendored
Normal file
50
web/node_modules/@parcel/watcher/src/DirTree.hh
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef DIR_TREE_H
|
||||||
|
#define DIR_TREE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include "Event.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define DIR_SEP "\\"
|
||||||
|
#else
|
||||||
|
#define DIR_SEP "/"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct DirEntry {
|
||||||
|
std::string path;
|
||||||
|
uint64_t mtime;
|
||||||
|
bool isDir;
|
||||||
|
mutable void *state;
|
||||||
|
|
||||||
|
DirEntry(std::string p, uint64_t t, bool d);
|
||||||
|
DirEntry(FILE *f);
|
||||||
|
void write(FILE *f) const;
|
||||||
|
bool operator==(const DirEntry &other) const {
|
||||||
|
return path == other.path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DirTree {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<DirTree> getCached(std::string root);
|
||||||
|
DirTree(std::string root) : root(root), isComplete(false) {}
|
||||||
|
DirTree(std::string root, FILE *f);
|
||||||
|
DirEntry *add(std::string path, uint64_t mtime, bool isDir);
|
||||||
|
DirEntry *find(std::string path);
|
||||||
|
DirEntry *update(std::string path, uint64_t mtime);
|
||||||
|
void remove(std::string path);
|
||||||
|
void write(FILE *f);
|
||||||
|
void getChanges(DirTree *snapshot, EventList &events);
|
||||||
|
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::string root;
|
||||||
|
bool isComplete;
|
||||||
|
std::unordered_map<std::string, DirEntry> entries;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DirEntry *_find(std::string path);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
109
web/node_modules/@parcel/watcher/src/Event.hh
generated
vendored
Normal file
109
web/node_modules/@parcel/watcher/src/Event.hh
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#ifndef EVENT_H
|
||||||
|
#define EVENT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "wasm/include.h"
|
||||||
|
#include <napi.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct Event {
|
||||||
|
std::string path;
|
||||||
|
bool isCreated;
|
||||||
|
bool isDeleted;
|
||||||
|
Event(std::string path) : path(path), isCreated(false), isDeleted(false) {}
|
||||||
|
|
||||||
|
Value toJS(const Env& env) {
|
||||||
|
EscapableHandleScope scope(env);
|
||||||
|
Object res = Object::New(env);
|
||||||
|
std::string type = isCreated ? "create" : isDeleted ? "delete" : "update";
|
||||||
|
res.Set(String::New(env, "path"), String::New(env, path.c_str()));
|
||||||
|
res.Set(String::New(env, "type"), String::New(env, type.c_str()));
|
||||||
|
return scope.Escape(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventList {
|
||||||
|
public:
|
||||||
|
void create(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
Event *event = internalUpdate(path);
|
||||||
|
if (event->isDeleted) {
|
||||||
|
// Assume update event when rapidly removed and created
|
||||||
|
// https://github.com/parcel-bundler/watcher/issues/72
|
||||||
|
event->isDeleted = false;
|
||||||
|
} else {
|
||||||
|
event->isCreated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event *update(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return internalUpdate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(std::string path) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
Event *event = internalUpdate(path);
|
||||||
|
event->isDeleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mEvents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Event> getEvents() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
std::vector<Event> eventsCloneVector;
|
||||||
|
for(auto it = mEvents.begin(); it != mEvents.end(); ++it) {
|
||||||
|
if (!(it->second.isCreated && it->second.isDeleted)) {
|
||||||
|
eventsCloneVector.push_back(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eventsCloneVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
mEvents.clear();
|
||||||
|
mError.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void error(std::string err) {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
if (!mError.has_value()) {
|
||||||
|
mError.emplace(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasError() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mError.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getError() {
|
||||||
|
std::lock_guard<std::mutex> l(mMutex);
|
||||||
|
return mError.value_or("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mMutex;
|
||||||
|
std::map<std::string, Event> mEvents;
|
||||||
|
std::optional<std::string> mError;
|
||||||
|
Event *internalUpdate(std::string path) {
|
||||||
|
auto found = mEvents.find(path);
|
||||||
|
if (found == mEvents.end()) {
|
||||||
|
auto it = mEvents.emplace(path, Event(path));
|
||||||
|
return &it.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
22
web/node_modules/@parcel/watcher/src/Glob.cc
generated
vendored
Normal file
22
web/node_modules/@parcel/watcher/src/Glob.cc
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "Glob.hh"
|
||||||
|
|
||||||
|
#ifdef __wasm32__
|
||||||
|
extern "C" bool wasm_regex_match(const char *s, const char *regex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Glob::Glob(std::string raw) {
|
||||||
|
mRaw = raw;
|
||||||
|
mHash = std::hash<std::string>()(raw);
|
||||||
|
#ifndef __wasm32__
|
||||||
|
mRegex = std::regex(raw);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Glob::isIgnored(std::string relative_path) const {
|
||||||
|
// Use native JS regex engine for wasm to reduce binary size.
|
||||||
|
#ifdef __wasm32__
|
||||||
|
return wasm_regex_match(relative_path.c_str(), mRaw.c_str());
|
||||||
|
#else
|
||||||
|
return std::regex_match(relative_path, mRegex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
34
web/node_modules/@parcel/watcher/src/Glob.hh
generated
vendored
Normal file
34
web/node_modules/@parcel/watcher/src/Glob.hh
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef GLOB_H
|
||||||
|
#define GLOB_H
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
struct Glob {
|
||||||
|
std::size_t mHash;
|
||||||
|
std::string mRaw;
|
||||||
|
#ifndef __wasm32__
|
||||||
|
std::regex mRegex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Glob(std::string raw);
|
||||||
|
|
||||||
|
bool operator==(const Glob &other) const {
|
||||||
|
return mHash == other.mHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIgnored(std::string relative_path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<Glob>
|
||||||
|
{
|
||||||
|
size_t operator()(const Glob& g) const {
|
||||||
|
return g.mHash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
101
web/node_modules/@parcel/watcher/src/PromiseRunner.hh
generated
vendored
Normal file
101
web/node_modules/@parcel/watcher/src/PromiseRunner.hh
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef PROMISE_RUNNER_H
|
||||||
|
#define PROMISE_RUNNER_H
|
||||||
|
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "wasm/include.h"
|
||||||
|
#include <napi.h>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
class PromiseRunner {
|
||||||
|
public:
|
||||||
|
const Env env;
|
||||||
|
Promise::Deferred deferred;
|
||||||
|
|
||||||
|
PromiseRunner(Env env) : env(env), deferred(Promise::Deferred::New(env)) {
|
||||||
|
napi_status status = napi_create_async_work(env, nullptr, env.Undefined(),
|
||||||
|
onExecute, onWorkComplete, this, &work);
|
||||||
|
if (status != napi_ok) {
|
||||||
|
work = nullptr;
|
||||||
|
const napi_extended_error_info *error_info = 0;
|
||||||
|
napi_get_last_error_info(env, &error_info);
|
||||||
|
if (error_info->error_message) {
|
||||||
|
Error::New(env, error_info->error_message).ThrowAsJavaScriptException();
|
||||||
|
} else {
|
||||||
|
Error::New(env).ThrowAsJavaScriptException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~PromiseRunner() {}
|
||||||
|
|
||||||
|
Value queue() {
|
||||||
|
if (work) {
|
||||||
|
napi_status status = napi_queue_async_work(env, work);
|
||||||
|
if (status != napi_ok) {
|
||||||
|
onError(Error::New(env));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.Promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
napi_async_work work;
|
||||||
|
std::string error;
|
||||||
|
|
||||||
|
static void onExecute(napi_env env, void *this_pointer) {
|
||||||
|
PromiseRunner* self = (PromiseRunner*) this_pointer;
|
||||||
|
try {
|
||||||
|
self->execute();
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
self->error = err.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onWorkComplete(napi_env env, napi_status status, void *this_pointer) {
|
||||||
|
PromiseRunner* self = (PromiseRunner*) this_pointer;
|
||||||
|
if (status != napi_cancelled) {
|
||||||
|
HandleScope scope(self->env);
|
||||||
|
if (status == napi_ok) {
|
||||||
|
status = napi_delete_async_work(self->env, self->work);
|
||||||
|
if (status == napi_ok) {
|
||||||
|
if (self->error.size() == 0) {
|
||||||
|
self->onOK();
|
||||||
|
} else {
|
||||||
|
self->onError(Error::New(self->env, self->error));
|
||||||
|
}
|
||||||
|
delete self;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallthrough for error handling
|
||||||
|
const napi_extended_error_info *error_info = 0;
|
||||||
|
napi_get_last_error_info(env, &error_info);
|
||||||
|
if (error_info->error_message){
|
||||||
|
self->onError(Error::New(env, error_info->error_message));
|
||||||
|
} else {
|
||||||
|
self->onError(Error::New(env));
|
||||||
|
}
|
||||||
|
delete self;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void execute() {}
|
||||||
|
virtual Value getResult() {
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onOK() {
|
||||||
|
HandleScope scope(env);
|
||||||
|
Value result = getResult();
|
||||||
|
deferred.Resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onError(const Error &e) {
|
||||||
|
deferred.Reject(e.Value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
46
web/node_modules/@parcel/watcher/src/Signal.hh
generated
vendored
Normal file
46
web/node_modules/@parcel/watcher/src/Signal.hh
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef SIGNAL_H
|
||||||
|
#define SIGNAL_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
class Signal {
|
||||||
|
public:
|
||||||
|
Signal() : mFlag(false), mWaiting(false) {}
|
||||||
|
void wait() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
while (!mFlag) {
|
||||||
|
mWaiting = true;
|
||||||
|
mCond.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cv_status waitFor(std::chrono::milliseconds ms) {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
return mCond.wait_for(lock, ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mFlag = true;
|
||||||
|
mCond.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mFlag = false;
|
||||||
|
mWaiting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWaiting() {
|
||||||
|
return mWaiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mFlag;
|
||||||
|
bool mWaiting;
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::condition_variable mCond;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
237
web/node_modules/@parcel/watcher/src/Watcher.cc
generated
vendored
Normal file
237
web/node_modules/@parcel/watcher/src/Watcher.cc
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#include "Watcher.hh"
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct WatcherHash {
|
||||||
|
std::size_t operator() (WatcherRef const &k) const {
|
||||||
|
return std::hash<std::string>()(k->mDir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WatcherCompare {
|
||||||
|
size_t operator() (WatcherRef const &a, WatcherRef const &b) const {
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_set<WatcherRef , WatcherHash, WatcherCompare> sharedWatchers;
|
||||||
|
|
||||||
|
WatcherRef Watcher::getShared(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs) {
|
||||||
|
WatcherRef watcher = std::make_shared<Watcher>(dir, ignorePaths, ignoreGlobs);
|
||||||
|
auto found = sharedWatchers.find(watcher);
|
||||||
|
if (found != sharedWatchers.end()) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedWatchers.insert(watcher);
|
||||||
|
return watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeShared(Watcher *watcher) {
|
||||||
|
for (auto it = sharedWatchers.begin(); it != sharedWatchers.end(); it++) {
|
||||||
|
if (it->get() == watcher) {
|
||||||
|
sharedWatchers.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free up memory.
|
||||||
|
if (sharedWatchers.size() == 0) {
|
||||||
|
sharedWatchers.rehash(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher::Watcher(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs)
|
||||||
|
: mDir(dir),
|
||||||
|
mIgnorePaths(ignorePaths),
|
||||||
|
mIgnoreGlobs(ignoreGlobs) {
|
||||||
|
mDebounce = Debounce::getShared();
|
||||||
|
mDebounce->add(this, [this] () {
|
||||||
|
triggerCallbacks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher::~Watcher() {
|
||||||
|
mDebounce->remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::wait() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
mCond.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::notify() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
mCond.notify_all();
|
||||||
|
|
||||||
|
if (mCallbacks.size() > 0 && mEvents.size() > 0) {
|
||||||
|
// We must release our lock before calling into the debouncer
|
||||||
|
// to avoid a deadlock: the debouncer thread itself will require
|
||||||
|
// our lock from its thread when calling into `triggerCallbacks`
|
||||||
|
// while holding its own debouncer lock.
|
||||||
|
lk.unlock();
|
||||||
|
mDebounce->trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CallbackData {
|
||||||
|
std::string error;
|
||||||
|
std::vector<Event> events;
|
||||||
|
CallbackData(std::string error, std::vector<Event> events) : error(error), events(events) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Value callbackEventsToJS(const Env &env, std::vector<Event> &events) {
|
||||||
|
EscapableHandleScope scope(env);
|
||||||
|
Array arr = Array::New(env, events.size());
|
||||||
|
size_t currentEventIndex = 0;
|
||||||
|
for (auto eventIterator = events.begin(); eventIterator != events.end(); eventIterator++) {
|
||||||
|
arr.Set(currentEventIndex++, eventIterator->toJS(env));
|
||||||
|
}
|
||||||
|
return scope.Escape(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callJSFunction(Napi::Env env, Function jsCallback, CallbackData *data) {
|
||||||
|
HandleScope scope(env);
|
||||||
|
auto err = data->error.size() > 0 ? Error::New(env, data->error).Value() : env.Null();
|
||||||
|
auto events = callbackEventsToJS(env, data->events);
|
||||||
|
jsCallback.Call({err, events});
|
||||||
|
delete data;
|
||||||
|
|
||||||
|
// Throw errors from the callback as fatal exceptions
|
||||||
|
// If we don't handle these node segfaults...
|
||||||
|
if (env.IsExceptionPending()) {
|
||||||
|
Napi::Error err = env.GetAndClearPendingException();
|
||||||
|
napi_fatal_exception(env, err.Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::notifyError(std::exception &err) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
CallbackData *data = new CallbackData(err.what(), {});
|
||||||
|
it->tsfn.BlockingCall(data, callJSFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called from the debounce thread.
|
||||||
|
void Watcher::triggerCallbacks() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
if (mCallbacks.size() > 0 && (mEvents.size() > 0 || mEvents.hasError())) {
|
||||||
|
auto error = mEvents.getError();
|
||||||
|
auto events = mEvents.getEvents();
|
||||||
|
mEvents.clear();
|
||||||
|
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
it->tsfn.BlockingCall(new CallbackData(error, events), callJSFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
bool Watcher::watch(Function callback) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
|
||||||
|
auto it = findCallback(callback);
|
||||||
|
if (it != mCallbacks.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tsfn = ThreadSafeFunction::New(
|
||||||
|
callback.Env(),
|
||||||
|
callback,
|
||||||
|
"Watcher callback",
|
||||||
|
0, // Unlimited queue
|
||||||
|
1 // Initial thread count
|
||||||
|
);
|
||||||
|
|
||||||
|
mCallbacks.push_back(Callback {
|
||||||
|
tsfn,
|
||||||
|
Napi::Persistent(callback),
|
||||||
|
std::this_thread::get_id()
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
std::vector<Callback>::iterator Watcher::findCallback(Function callback) {
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
// Only consider callbacks created by the same thread, or V8 will panic.
|
||||||
|
if (it->threadId == std::this_thread::get_id() && it->ref.Value() == callback) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mCallbacks.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be called from the JavaScript thread.
|
||||||
|
bool Watcher::unwatch(Function callback) {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
|
||||||
|
bool removed = false;
|
||||||
|
auto it = findCallback(callback);
|
||||||
|
if (it != mCallbacks.end()) {
|
||||||
|
it->tsfn.Release();
|
||||||
|
it->ref.Unref();
|
||||||
|
mCallbacks.erase(it);
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed && mCallbacks.size() == 0) {
|
||||||
|
unref();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::unref() {
|
||||||
|
if (mCallbacks.size() == 0) {
|
||||||
|
removeShared(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Watcher::destroy() {
|
||||||
|
std::unique_lock<std::mutex> lk(mMutex);
|
||||||
|
clearCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private because it doesn't lock.
|
||||||
|
void Watcher::clearCallbacks() {
|
||||||
|
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
|
||||||
|
it->tsfn.Release();
|
||||||
|
it->ref.Unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallbacks.clear();
|
||||||
|
unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watcher::isIgnored(std::string path) {
|
||||||
|
for (auto it = mIgnorePaths.begin(); it != mIgnorePaths.end(); it++) {
|
||||||
|
auto dir = *it + DIR_SEP;
|
||||||
|
if (*it == path || path.compare(0, dir.size(), dir) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto basePath = mDir + DIR_SEP;
|
||||||
|
|
||||||
|
if (path.rfind(basePath, 0) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relativePath = path.substr(basePath.size());
|
||||||
|
|
||||||
|
for (auto it = mIgnoreGlobs.begin(); it != mIgnoreGlobs.end(); it++) {
|
||||||
|
if (it->isIgnored(relativePath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
73
web/node_modules/@parcel/watcher/src/Watcher.hh
generated
vendored
Normal file
73
web/node_modules/@parcel/watcher/src/Watcher.hh
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef WATCHER_H
|
||||||
|
#define WATCHER_H
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <set>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include "Glob.hh"
|
||||||
|
#include "Event.hh"
|
||||||
|
#include "Debounce.hh"
|
||||||
|
#include "DirTree.hh"
|
||||||
|
#include "Signal.hh"
|
||||||
|
|
||||||
|
using namespace Napi;
|
||||||
|
|
||||||
|
struct Watcher;
|
||||||
|
using WatcherRef = std::shared_ptr<Watcher>;
|
||||||
|
|
||||||
|
struct Callback {
|
||||||
|
Napi::ThreadSafeFunction tsfn;
|
||||||
|
Napi::FunctionReference ref;
|
||||||
|
std::thread::id threadId;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WatcherState {
|
||||||
|
public:
|
||||||
|
virtual ~WatcherState() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Watcher {
|
||||||
|
std::string mDir;
|
||||||
|
std::unordered_set<std::string> mIgnorePaths;
|
||||||
|
std::unordered_set<Glob> mIgnoreGlobs;
|
||||||
|
EventList mEvents;
|
||||||
|
std::shared_ptr<WatcherState> state;
|
||||||
|
|
||||||
|
Watcher(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs);
|
||||||
|
~Watcher();
|
||||||
|
|
||||||
|
bool operator==(const Watcher &other) const {
|
||||||
|
return mDir == other.mDir && mIgnorePaths == other.mIgnorePaths && mIgnoreGlobs == other.mIgnoreGlobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
void notify();
|
||||||
|
void notifyError(std::exception &err);
|
||||||
|
bool watch(Function callback);
|
||||||
|
bool unwatch(Function callback);
|
||||||
|
void unref();
|
||||||
|
bool isIgnored(std::string path);
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
static WatcherRef getShared(std::string dir, std::unordered_set<std::string> ignorePaths, std::unordered_set<Glob> ignoreGlobs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mMutex;
|
||||||
|
std::condition_variable mCond;
|
||||||
|
std::vector<Callback> mCallbacks;
|
||||||
|
std::shared_ptr<Debounce> mDebounce;
|
||||||
|
|
||||||
|
std::vector<Callback>::iterator findCallback(Function callback);
|
||||||
|
void clearCallbacks();
|
||||||
|
void triggerCallbacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
class WatcherError : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
WatcherRef mWatcher;
|
||||||
|
WatcherError(std::string msg, WatcherRef watcher) : std::runtime_error(msg), mWatcher(watcher) {}
|
||||||
|
WatcherError(const char *msg, WatcherRef watcher) : std::runtime_error(msg), mWatcher(watcher) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user