This commit is contained in:
PhilippHofer 2023-09-05 21:03:18 +00:00
parent cbfc6be0e9
commit fe4c0bebfc
No known key found for this signature in database
13 changed files with 346 additions and 310 deletions

33
frontend/logbook.ts Normal file
View File

@ -0,0 +1,33 @@
/*document.addEventListener('DOMContentLoaded', function() {
setDistance('.set-distance-js');
});
function setDistance(selector: string) {
const fields = document.querySelectorAll(selector);
if(fields) {
Array.prototype.forEach.call(fields, (field: HTMLInputElement) => {
if(field.dataset.relation){
const relatedField = <HTMLInputElement>document.getElementById(field.dataset.relation);
if(relatedField) {
field.addEventListener('input', (e) => {
e.preventDefault();
const dataList = <HTMLDataListElement>document.getElementById('destinations');
if(dataList) {
var option = Array.prototype.find.call(dataList.options, function(option) {
return option.value === field.value;
});
// Get distance
const distance = option.getAttribute('distance');
if(distance) relatedField.value = distance;
}
});
}
}
});
}
}
*/
export {}

View File

@ -202,9 +202,22 @@ function initTripSidebar(triggerElement: HTMLElement) {
}
});
const prefixedContent = <NodeListOf<HTMLElement>>bodyElement.querySelectorAll('.change-id-js');
Array.prototype.forEach.call(prefixedContent, (content: HTMLElement) => {
if(content) {
content.id += 'js';
if(content.dataset.relation){
content.dataset.relation += 'js';
}
}
});
if(bodyContainerElement) {
bodyContainerElement.innerHTML = '';
bodyContainerElement.append(bodyElement);
addRelationMagic(bodyElement);
}
if(triggerElement.dataset.day) {
let hiddenElement = <HTMLInputElement>bodyElement.querySelector('.day-js');
@ -220,6 +233,33 @@ function initTripSidebar(triggerElement: HTMLElement) {
}
}
function addRelationMagic(bodyElement: HTMLElement) {
const fields = bodyElement.querySelectorAll('.set-distance-js');
if(fields) {
Array.prototype.forEach.call(fields, (field: HTMLInputElement) => {
if(field.dataset.relation){
const relatedField = <HTMLInputElement>bodyElement.querySelector('#' + field.dataset.relation);
if(relatedField) {
field.addEventListener('input', (e) => {
e.preventDefault();
const dataList = <HTMLDataListElement>document.getElementById('destinations');
if(dataList) {
var option = Array.prototype.find.call(dataList.options, function(option) {
return option.value === field.value;
});
// Get distance
const distance = option.getAttribute('distance');
if(distance) relatedField.value = distance;
}
});
}
}
});
}
}
function replaceStrings() {
const weekdays = document.querySelectorAll('.weekday-js');
Array.prototype.forEach.call(weekdays, (weekday: HTMLElement) => {

View File

@ -17,6 +17,7 @@ export default defineConfig({
rollupOptions: {
input: {
main: './main.ts',
logbook: './logbook.ts',
// Example for more entry points
// test: './src/test.ts',
},

View File

@ -204,6 +204,7 @@ ORDER BY departure DESC
let mut tx = db.begin().await.unwrap();
let departure = NaiveDateTime::parse_from_str(&log.departure, "%Y-%m-%dT%H:%M").unwrap();
println!("@@@@@@ {:?}", log.arrival);
let arrival = log
.arrival
.map(|a| NaiveDateTime::parse_from_str(&a, "%Y-%m-%dT%H:%M").unwrap());

View File

@ -341,8 +341,9 @@ mod test {
let response = req.dispatch().await;
let text = response.into_string().await.unwrap();
println!("{text:?}");
assert!(text.contains("Logbuch"));
assert!(text.contains("Bootsname: Joe"));
assert!(text.contains("Joe"));
}
#[sqlx::test]
@ -361,7 +362,7 @@ mod test {
let text = response.into_string().await.unwrap();
assert!(text.contains("Logbuch"));
assert!(text.contains("Bootsname: Joe"));
assert!(text.contains("Joe"));
}
#[sqlx::test]

View File

@ -10,7 +10,7 @@
{{ macros::checkbox(label='Gäste erlauben', name='tripdetails.allow_guests') }}
{{ macros::checkbox(label='Immer anzeigen', name='tripdetails.always_show') }}
{{ macros::input(label='Anmerkungen', name='tripdetails.notes', type='input') }}
{{ macros::select(data=trip_types, select_name='tripdetails.trip_type', default='Reguläre Ausfahrt') }}
{{ macros::select(data=trip_types, name='tripdetails.trip_type', default='Reguläre Ausfahrt') }}
<input value="Erstellen" class="w-full btn btn-primary" type="submit"/>
</form>

View File

@ -8,7 +8,7 @@
{{ macros::checkbox(label='Gäste erlauben', name='allow_guests') }}
{{ macros::checkbox(label='Immer anzeigen', name='always_show') }}
{{ macros::input(label='Anmerkungen', name='notes', type='input') }}
{{ macros::select(data=trip_types, select_name='trip_type', default='Reguläre Ausfahrt') }}
{{ macros::select(data=trip_types, name='trip_type', default='Reguläre Ausfahrt') }}
<input value="Erstellen" class="w-full btn btn-primary" type="submit"/>
</form>

View File

@ -8,8 +8,8 @@
{{ macros::input(label="Anzahl Sitze", name="amount_seats", type="number", required=true, min=1) }}
{{ macros::input(label="Baujahr", name="year_built", type="number", min=1950, max=2050) }}
{{ macros::input(label="Bootsbauer", name="boatbuilder", type="text") }}
{{ macros::select(data=locations, label='location', select_name='location_id', selected_id=1) }}
{{ macros::select(data=users, label='users', select_name='owner', default="Vereinsboot") }}
{{ macros::select(data=locations, label='location', name='location_id', selected_id=1) }}
{{ macros::select(data=users, label='users', name='owner', default="Vereinsboot") }}
{{ macros::checkbox(label="Steuerperson steuert nur", name="default_shipmaster_only_steering")}}
{{ macros::checkbox(label="Skull", name="skull", checked=true)}}
{{ macros::checkbox(label="Externes Boot (anderer Verein)", name="external")}}
@ -29,8 +29,8 @@
<div class="grid md:grid-cols-3">
{{ macros::input(label='Name', name='name', type='text', value=boat.name) }}
{{ macros::input(label='Amount Seats', name='amount_seats', type='number', min=0, value=boat.amount_seats) }}
{{ macros::select(data=locations, label='location', select_name='location_id', selected_id=boat.location_id) }}
{{ macros::select(data=users, label='users', select_name='owner', selected_id=boat.owner, default="Vereinsboot") }}
{{ macros::select(data=locations, label='location', name='location_id', selected_id=boat.location_id) }}
{{ macros::select(data=users, label='users', name='owner', selected_id=boat.owner, default="Vereinsboot") }}
{{ macros::input(label='Baujahr', name='year_built', type='number', min=1950, value=boat.year_built) }}
{{ macros::input(label='Bootsbauer', name='boatbuilder', type='text', value=boat.boatbuilder) }}
{{ macros::checkbox(label='default_shipmaster_only_steering', name='default_shipmaster_only_steering', id=uuid , checked=boat.default_shipmaster_only_steering) }}

View File

@ -1,5 +1,34 @@
{# Shows a fancy, optional lists of boats. They are grouped by boat category.
Inputs: boats
Parameters: only_ones: if set, only 1x boats are shown
#}
{% macro show_boats(only_ones) %}
{% if only_ones %}
{% set_global boats = boats | filter(attribute="amount_seats", value=1) %}
{% endif %}
{% for amount_seats, grouped_boats in boats | group_by(attribute="amount_seats") %}
<div class="pb-2">
<div class="bg-gray-100 text-primary-950 text-center text-sm mb-2">
<strong>{{ amount_seats }}x</strong>
</div>
{% for boat in grouped_boats %}
<div id="boat-{{ boat.id }}" class="px-3" {% if boat.damage != 'locked' %} onclick="document.getElementById('boat_id').value={{ boat.id }}; document.getElementById('newrower-max_rower_allowed').innerHTML={{ boat.amount_seats }}; document.getElementById('departure').value = new Date().toISOString().slice(0,16);" {% endif %}>
<span class="status-damage status-damage-{{ boat.damage }}"></span>
<span {% if boat.damage == 'locked' or boat.on_water %} class="opacity-50" {% endif %}>{{ boat.name }}
{% if boat.owner %}
<span class="opacity-50">(privat)</span>
{% endif %}
</span>
</div>
{% endfor %}
</div>
{% endfor %}
{% endmacro show_boats %}
{# Shows the form for creating a new logbook entry. #}
{% macro new(only_ones, allow_any_shipmaster, shipmaster) %}
<form action="/log" method="post" id="form" class="grid grid-cols-2 gap-3">
<form action="/log" method="post" id="form" class="grid grid-cols-2 gap-3" onsubmit="Array.from(this.elements).forEach(e=>!e.value.trim()&&(e.disabled=true));">
{{ log::boat_select(only_ones=only_ones) }}
{% if allow_any_shipmaster %}
@ -16,7 +45,7 @@
</optgroup>
</select>
{% else %}
<input type="hidden" name="shipmaster" value="{{shipmaster}}" />
<input type="hidden" name="shipmaster" value="{{shipmaster}}"/>
{% endif %}
{% if not only_ones %}
{{ macros::checkbox(label='handgesteuert', name='shipmaster_only_steering') }}
@ -28,24 +57,24 @@
<div></div>
<div>
<label for="destination" class="small text-gray-600">Ziel</label>
<input type="search" class="input rounded-md" list="destinations" placeholder="Destination" name="destination" id="destination">
<datalist id="destinations">
{% for distance in distances %}
<option value="{{ distance.0 }}" distance={{ distance.1 }}/>
{% endfor %}
</datalist>
</div>
{{ macros::input(label='Abfahrtszeit', name='departure', type='datetime-local', required=true) }}
{{ macros::input(label='Ankunftszeit', name='arrival', type='datetime-local') }}
<div>
<label for="destination" class="small text-gray-600">Ziel</label>
<input type="search" class="input rounded-md" list="destinations" placeholder="Destination" name="destination" id="destination" oninput="var inputElement = document.getElementById('destination');
var dataList = document.getElementById('destinations');
var selectedValue = inputElement.value;
for (var i = 0; i < dataList.options.length; i++) {
if (dataList.options[i].value === selectedValue) {
var distance = dataList.options[i].getAttribute('distance');
document.getElementById('distance_in_km').value = distance;
break;
}
}">
<input type="search" class="input rounded-md set-distance-js" list="destinations" placeholder="Destination" name="destination" id="destination" data-relation="distance_in_km">
<datalist id="destinations">
{% for distance in distances %}
<option value="{{ distance.0 }}" distance={{ distance.1 }} />
<option value="{{ distance.0 }}" distance={{ distance.1 }}/>
{% endfor %}
</datalist>
</div>
@ -58,92 +87,43 @@
<div class="col-span-2">
<label for="logtype" class=" small text-gray-600 ">Typ</label>
{{ macros::select(data=logtypes, select_name='logtype', default="Normal") }}
{{ macros::select(data=logtypes, name='logtype', default="Normal") }}
</div>
<input type="submit" value="Ausfahrt starten" class="btn btn-primary w-full col-span-2 m-auto" />
<script>
const currentDate = new Date();
const localTime = new Date(currentDate.getTime() - (currentDate.getTimezoneOffset() * 60000));
const formattedDate = localTime.toISOString().slice(0, 16);
// Set the formatted string as the value of the input field
document.getElementById("departure").value = formattedDate;
</script>
<input type="submit" value="Ausfahrt starten" class="btn btn-primary w-full col-span-2 m-auto"/>
</form>
<script src="/public/js/multiselect-dropdown.js" ></script>
<script>
document.getElementById('form').addEventListener('submit', function(e) {
e.preventDefault();
for(let optional_elem of ["arrival", "distance_in_km", "comments", "logtype"]){
let myInput = document.getElementById(optional_elem);
if (myInput.value === '') {
myInput.removeAttribute('name');
}
}
this.submit();
});
</script>
{% endmacro new %}
{% macro show_boats(only_ones) %}
{% if only_ones %}
{% set_global boats = boats | filter(attribute="amount_seats", value=1) %}
{% endif %}
{% for amount_seats, grouped_boats in boats | group_by(attribute="amount_seats") %}
<div class="pb-2">
<div class="bg-gray-100 text-primary-950 text-center text-sm mb-2">
<strong>{{ amount_seats }}x</strong>
</div>
{% for boat in grouped_boats %}
<div id="boat-{{ boat.id }}" {% if boat.damage != 'locked' %} onclick="document.getElementById('boat_id').value='{{ boat.id }}';updateElementsBasedOnSelectedOption()"{% endif %} class="px-3">
<span class="status-damage status-damage-{{ boat.damage }}"></span>
<span {% if boat.damage == 'locked' or boat.on_water %} class="opacity-50" {% endif %}>{{ boat.name }}</span>
</div>
{% endfor %}
</div>
{% endfor %}
<script>
function updateElementsBasedOnSelectedOption() {
var selectElement = document.getElementById('boat_id');
var selectedOption = selectElement.selectedOptions[0];
var shipmaster_only_steering = selectedOption.getAttribute("extra-default_shipmaster_only_steering") === 'true';
document.getElementById('shipmaster_only_steering').checked = shipmaster_only_steering;
document.getElementById('newrower-max_rower_allowed').innerHTML = selectedOption.getAttribute("extra-amount_seats");
console.log(selectElement.selectedOptions[0]);
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('boat_id').addEventListener('change', updateElementsBasedOnSelectedOption);
});
document.addEventListener('DOMContentLoaded', updateElementsBasedOnSelectedOption);
</script>
{% endmacro boats %}
{% macro boat_select(only_ones) %}
{% if not only_ones %}
{{ macros::select(data=boats, select_name='boat_id', display=["name", " (","amount_seats", " x)"], extras=["default_shipmaster_only_steering", "amount_seats"], class="col-span-2") }}
{{ macros::select(data=boats, name='boat_id', display=["name", " (","amount_seats", " x)"], extras=["default_shipmaster_only_steering", "amount_seats"], class="col-span-2") }}
{% else %}
{% set ones = boats | filter(attribute="amount_seats", value=1) %}
{{ macros::select(data=ones, select_name='boat_id', display=["name", " (","amount_seats", " x)"], extras=["default_shipmaster_only_steering", "amount_seats"], class="col-span-2") }}
{{ macros::select(data=ones, name='boat_id', display=["name", " (","amount_seats", " x)"], extras=["default_shipmaster_only_steering", "amount_seats"], class="col-span-2") }}
{% endif %}
<script>
function updateElementsBasedOnSelectedOption() {
var selectElement = document.getElementById('boat_id');
var selectedOption = selectElement.selectedOptions[0];
var shipmaster_only_steering = selectedOption.getAttribute("extra-default_shipmaster_only_steering") === 'true';
document.getElementById('shipmaster_only_steering').checked = shipmaster_only_steering;
document.getElementById('newrower-max_rower_allowed').innerHTML = selectedOption.getAttribute("extra-amount_seats");
}
document.getElementById('boat_id').addEventListener('change', updateElementsBasedOnSelectedOption);
document.addEventListener('DOMContentLoaded', updateElementsBasedOnSelectedOption);
</script>
{% endmacro boat_select %}
{% macro rower_select(id, selected, amount_seats='', class='') %}
<div class="{{ class }}">
<select style="width: 100px;" multiple="multiple" name="rowers[]" multiselect-search="true" id="{{id}}" class="w-full">
{% for user in users %}
{% set_global sel = false %}
{% for rower in selected %}
{% if rower.id == user.id %}
{% set_global sel = true %}
{% endif %}
{% endfor %}
<option value="{{ user.id }}" {% if sel %} selected {% endif %}>{{user.name}}</option>
{% endfor %}
</select>
</div>
<div>
<span id="{{id}}-amount_rower_selected"></span>
<span id="{{id}}-max_rower_allowed">{{amount_seats}}</span>
Ruderer auszuwählen
</div>
{% endmacro rower_select %}
{% macro show(log, state, allowed_to_close=false, only_ones) %}
<div class="grid grid-cols-1 gap-3 mb-3 w-full">
<div class="pt-2 px-3 {% if not loop.first %} border-t {% endif %}">
@ -151,7 +131,8 @@
<div class="flex justify-between">
<div>
<strong class="text-primary-900">
{{ log.departure_timestamp | date(format="%H:%M") }} Uhr
{{ log.departure_timestamp | date(format="%H:%M") }}
Uhr
</strong>
<small class="text-gray-600">({{ log.boat.name }})</small>
</div>
@ -199,35 +180,44 @@
{% endmacro show %}
{% macro show_old(log, state, allowed_to_close=false, only_ones) %}
Bootsname: {{ log.boat.name }}<br />
Schiffsführer: {{ log.shipmaster_user.name }}<br />
Bootsname:
{{ log.boat.name }}<br/>
Schiffsführer:
{{ log.shipmaster_user.name }}<br/>
{% if log.shipmaster_only_steering %}
Schiffsführer steuert nur
{% endif %}
Weggefahren: {{ log.departure }}<br />
Weggefahren:
{{ log.departure }}<br/>
{% if state == "completed" %}
Angekommen: {{ log.arrival}}<br />
Angekommen:
{{ log.arrival}}<br/>
{% endif %}
{% set amount_rowers = log.rowers | length %}
{% set amount_guests = log.boat.amount_seats - amount_rowers -1 %}
{% if allowed_to_close and state == "on_water" %}
{{ log::home(log=log, only_ones=only_ones) }}
{% else %}
Ziel: {{ log.destination }}<br />
Ziel:
{{ log.destination }}<br/>
{% if state == "completed" %}
Km: {{ log.distance_in_km }}<br />
Km:
{{ log.distance_in_km }}<br/>
{% endif %}
{% if log.comments %}
Kommentare: {{ log.comments }}<br />
Kommentare:
{{ log.comments }}<br/>
{% endif %}
{% if log.logtype %}
Logtype: {{ log.logtype }}<br />
Logtype:
{{ log.logtype }}<br/>
{% endif %}
{% if amount_guests > 0 or log.rowers | length > 0 %}
Ruderer:
{% endif %}
{% if amount_guests > 0 %}
{{ amount_guests }} Gäste (ohne Account)
{{ amount_guests }}
Gäste (ohne Account)
{% endif %}
{% for rower in log.rowers %}
{{ rower.name }}
@ -236,62 +226,27 @@
{% endmacro show_old %}
{% macro home(log, only_ones) %}
<form class="grid grid-cols-1 gap-3" action="/log/{{log.id}}" method="post">
<form class="grid grid-cols-1 gap-3" action="/log/{{log.id}}" method="post">
{% if not only_ones %}
{{ log::rower_select(id="rowers"~log.id, selected=log.rowers, amount_seats=log.boat.amount_seats) }}
{% endif %}
<div class="relative">
<label for="destination" class="small text-gray-600">Ziel</label>
<input class="input rounded-md" type="search" list="destinations" placeholder="Destination" required="required", id="destination-home" name="destination" value="{{log.destination}}" oninput="var inputElement = document.getElementById('destination-home');
var dataList = document.getElementById('destinations');
var selectedValue = inputElement.value;
for (var i = 0; i < dataList.options.length; i++) {
if (dataList.options[i].value === selectedValue) {
var distance = dataList.options[i].getAttribute('distance');
document.getElementById('distance_in_km_home').value = distance;
break;
}
}">
<input class="input rounded-md set-distance-js change-id-js" type="search" list="destinations" placeholder="Destination" required="required" id="destination{{ log.id }}" name="destination" value="{{log.destination}}" data-relation="distance_in_km{{log.id}}"/>
</div>
<div class="relative">
{{ macros::input(label="Distanz", name="distance_in_km", id="distance_in_km_home", type="number", min=0, value=log.distance_in_km, required=true) }}
{{ macros::input(label="Distanz", name="distance_in_km", id="distance_in_km" ~ log.id , type="number", min=0, value=log.distance_in_km, required=true, class="rounded-md change-id-js") }}
<span class="absolute right-0 bottom-0 py-1.5 px-2 bg-white border-0 text-gray-600 ring-1 ring-inset ring-gray-300 rounded-br-md rounded-tr-md">km</span>
</div>
{{ macros::input(label="Kommentar", name="comments", type="text", value=log.comments) }}
{{ macros::input(label="Kommentar", name="comments", id="comments" ~ log.id, type="text", value=log.comments, class="rounded-md change-id-js") }}
<div>
<label for="logtype" class=" small text-gray-600 ">Typ</label>
{{ macros::select(data=logtypes, select_name='logtype', default="Normal", selected_id=log.logtype) }}
{{ macros::select(data=logtypes, name="logtype", id="logtype" ~ log.id, default="Normal", selected_id=log.logtype, class="rounded-md change-id-js") }}
</div>
<input class="btn btn-primary" type="submit" value="Ausfahrt beenden"/>
</form>
{% endmacro home %}
{% macro rower_select(id, selected, amount_seats='', class='') %}
<div class="{{ class }}">
<select multiple="multiple" name="rower[]" multiselect-search="true" multiselect-max-items="8" id="{{id}}" onclick="updateSelectedRowersCount{{id}}()" onblur="updateSelectedRowersCount{{id}}()" class="w-full">
{% for user in users %}
{% set_global sel = false %}
{% for rower in selected %}
{% if rower.id == user.id %}
{% set_global sel = true %}
{% endif %}
{% endfor %}
<option value="{{ user.id }}" {% if sel %}selected{% endif %} onmousedown="event.preventDefault();this.selected = !this.selected; return false;">{{user.name}}</option>
{% endfor %}
</select>
<script>
function updateSelectedRowersCount{{id}}() {
document.getElementById('{{id}}-amount_rower_selected').textContent = document.getElementById('{{id}}').selectedOptions.length+1;
}
document.addEventListener('DOMContentLoaded', updateSelectedRowersCount{{id}});
</script>
</div>
<div>
<span id="{{id}}-amount_rower_selected"></span>/<span id="{{id}}-max_rower_allowed">{{amount_seats}}</span> Ruderer ausgewählt
</div>
{% endmacro rower_select %}

View File

@ -74,11 +74,11 @@
</label>
{% endmacro checkbox %}
{% macro select(data, select_name='trip_type', default='', selected_id='', display='', extras='', class='') %}
{% macro select(data, name='trip_type', default='', id='', selected_id='', display='', extras='', class='') %}
{% if display == '' %}
{% set display = ["name"] %}
{% endif %}
<select name="{{ select_name }}" id="{{ select_name }}" class="input rounded-md h-10 {{ class }}">
<select name="{{ name }}" {% if id %} id="{{ id }}" {% else %} id="{{ name }}" {% endif %} class="input rounded-md h-10 {{ class }}">
{% if default %}
<option selected value>{{ default }}</option>
{% endif %}

View File

@ -239,7 +239,7 @@
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=trip.notes) }}
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=trip.id,checked=trip.always_show) }}
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }}
{{ macros::select(select_name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=trip.trip_type_id) }}
{{ macros::select(name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=trip.trip_type_id) }}
<input value="Speichern" class="btn btn-primary" type="submit"/>
</form>

View File

@ -39,4 +39,6 @@
</div>
{% include "dynamics/sidebar" %}
<script src="/public/logbook.js"></script>
{% endblock content%}

View File

@ -37,7 +37,10 @@
{% endfor %}
</div>
</div>
</div>
</div>
{% include "dynamics/sidebar" %}
{% include "dynamics/sidebar" %}
<script src="/public/logbook.js"></script>
<script src="/public/js/multiselect-dropdown.js"></script>
{% endblock content%}