Compare commits
79 Commits
2c0057ca44
...
a17c120ebe
Author | SHA1 | Date | |
---|---|---|---|
a17c120ebe | |||
ecd4e87f98 | |||
f004a34b54 | |||
4199fadc0a | |||
7bc8293c65 | |||
1d9824dfdc | |||
08202691f6 | |||
188dd50a84 | |||
930ecd490b | |||
0ea4fef9f7 | |||
d10b22f145 | |||
a4945e5972 | |||
33a4cc49e1 | |||
6c5448c464 | |||
b4eefb60c4 | |||
3c8d240549 | |||
262839f276 | |||
5cc08d657d | |||
c0353c0915 | |||
67ae4095cb | |||
14d82576f8 | |||
a9b67660b1 | |||
34123d9f79 | |||
0b1a6acd3a | |||
c9e47abd8e | |||
98b2b3d5f4 | |||
0db0a0f590 | |||
9694fe6512 | |||
fea5b6f3d8 | |||
4c54ebf6c3 | |||
a5aee6c6ed | |||
922716e1b7 | |||
de75f5398a | |||
c3d341d439 | |||
a799edc78b | |||
fb2e4a72ed | |||
bf50f952dc | |||
35f6dd2a38 | |||
1e9339642f | |||
b0562299d6 | |||
8251d3b648 | |||
70ead9a970 | |||
fd3ed5a272 | |||
becd1b99e6 | |||
f96086753d | |||
cf257c5f65 | |||
ffb437e1f4 | |||
0ec49ea264 | |||
5c30fa3cfa | |||
53ed032c25 | |||
f001aaf90f | |||
e053ff96cf | |||
f28a66b11e | |||
a87832e4f4 | |||
5b4238da92 | |||
5c7cd58edc | |||
30bc29e987 | |||
aefa625524 | |||
59b5b1bf97 | |||
a3c7461c2b | |||
|
e1b9cdcd3b | ||
7baa47d859 | |||
56163a4732 | |||
be52b0daf5 | |||
bfd9178d82 | |||
65edd4caa8 | |||
23a21b55c7 | |||
5c3be25285 | |||
f7c3fa01bc | |||
e5f6a543e1 | |||
87b5cff190 | |||
9b86d93979 | |||
9bfa89a841 | |||
|
e8efe13ef5 | ||
|
d60cbc7b5a | ||
|
7e02c840b4 | ||
|
cdc42d7154 | ||
|
88c99b0898 | ||
b4e7a5f28b |
136
.gitea/workflows/action.yml
Normal file
136
.gitea/workflows/action.yml
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TARGET: x86_64-unknown-linux-musl
|
||||||
|
SSH_HOST: ${{ secrets.SSH_HOST }}
|
||||||
|
SSH_USER: ${{ secrets.SSH_USER }}
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: rust:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Setup Environment
|
||||||
|
run: |
|
||||||
|
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run Test DB Script
|
||||||
|
run: ./test_db.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cargo build
|
||||||
|
cd frontend && npm install && npm run build
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: cargo test --verbose
|
||||||
|
|
||||||
|
deploy-staging:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: rust:latest
|
||||||
|
needs: [test]
|
||||||
|
if: github.ref == 'refs/heads/staging'
|
||||||
|
steps:
|
||||||
|
- name: Setup Environment
|
||||||
|
run: |
|
||||||
|
rustup target add $CARGO_TARGET
|
||||||
|
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg
|
||||||
|
|
||||||
|
# Handling NodeSource GPG key
|
||||||
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o nodesource.gpg.key
|
||||||
|
if [ -f /etc/apt/keyrings/nodesource.gpg ]; then
|
||||||
|
rm /etc/apt/keyrings/nodesource.gpg
|
||||||
|
fi
|
||||||
|
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg nodesource.gpg.key
|
||||||
|
|
||||||
|
# Adding NodeSource repository
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||||
|
|
||||||
|
# Installing Node.js and npm
|
||||||
|
apt-get update
|
||||||
|
apt-get install nodejs -y
|
||||||
|
apt-get install npm -y
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run Test DB Script
|
||||||
|
run: ./test_db.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cargo build --release --target $CARGO_TARGET
|
||||||
|
strip target/$CARGO_TARGET/release/rot
|
||||||
|
cd frontend && npm install && npm run build
|
||||||
|
|
||||||
|
- name: Deploy to Staging
|
||||||
|
run: |
|
||||||
|
mkdir ~/.ssh
|
||||||
|
ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
|
||||||
|
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/rot-updating
|
||||||
|
|
||||||
|
scp staging-diff.sql $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
|
scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
|
scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
|
scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
SSH_HOST: ${{ secrets.SSH_HOST }}
|
||||||
|
SSH_USER: ${{ secrets.SSH_USER }}
|
||||||
|
|
||||||
|
deploy-main:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: rust:latest
|
||||||
|
needs: [test]
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
steps:
|
||||||
|
- name: Setup Environment
|
||||||
|
run: |
|
||||||
|
rustup target add $CARGO_TARGET
|
||||||
|
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Run Test DB Script
|
||||||
|
run: ./test_db.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cargo build --release --target $CARGO_TARGET
|
||||||
|
strip target/$CARGO_TARGET/release/rot
|
||||||
|
cd frontend && npm install && npm run build
|
||||||
|
|
||||||
|
- name: Deploy to Main
|
||||||
|
run: |
|
||||||
|
mkdir ~/.ssh
|
||||||
|
ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
|
||||||
|
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
|
||||||
|
chmod 600 ~/.ssh/id_rsa
|
||||||
|
|
||||||
|
scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing/rot-updating
|
||||||
|
scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
|
scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
|
scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing/data-ergo/thirty && mkdir -p /home/k004373/rowing/data-ergo/dozen'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
|
||||||
|
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
SSH_HOST: ${{ secrets.SSH_HOST }}
|
||||||
|
SSH_USER: ${{ secrets.SSH_USER }}
|
@ -1,70 +0,0 @@
|
|||||||
image: rust:latest
|
|
||||||
|
|
||||||
variables:
|
|
||||||
CARGO_TARGET: x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- rustup target add $CARGO_TARGET
|
|
||||||
- apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
|
|
||||||
- ./test_db.sh
|
|
||||||
|
|
||||||
build:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- cargo build --release --target $CARGO_TARGET
|
|
||||||
- strip target/$CARGO_TARGET/release/rot
|
|
||||||
- cd frontend && npm install && npm run build
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- target/$CARGO_TARGET/release/rot
|
|
||||||
- static
|
|
||||||
expire_in: 3 hours
|
|
||||||
|
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
image: rust:latest
|
|
||||||
script:
|
|
||||||
- cargo test --verbose
|
|
||||||
|
|
||||||
deploy-staging:
|
|
||||||
stage: deploy
|
|
||||||
before_script:
|
|
||||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- echo "$SSH_PRIVATE_KEY" | ssh-add -
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- chmod 700 ~/.ssh
|
|
||||||
- ssh-keyscan -H $SSH_HOST > ~/.ssh/known_hosts
|
|
||||||
script:
|
|
||||||
- scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/rot-updating
|
|
||||||
- scp staging-diff.sql $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
|
||||||
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
|
||||||
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
|
||||||
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
|
|
||||||
only:
|
|
||||||
- staging
|
|
||||||
|
|
||||||
deploy-main:
|
|
||||||
stage: deploy
|
|
||||||
before_script:
|
|
||||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- echo "$SSH_PRIVATE_KEY" | ssh-add -
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- chmod 700 ~/.ssh
|
|
||||||
- ssh-keyscan -H $SSH_HOST > ~/.ssh/known_hosts
|
|
||||||
script:
|
|
||||||
- scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing/rot-updating
|
|
||||||
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
|
||||||
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
|
||||||
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing/data-ergo/thirty && mkdir -p /home/k004373/rowing/data-ergo/dozen'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
|
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
|
|
||||||
only:
|
|
||||||
- main
|
|
@ -26,9 +26,9 @@ INSERT INTO "boat" (name, amount_seats, location_id) VALUES ('Ottensheim Boot',
|
|||||||
INSERT INTO "boat" (name, amount_seats, location_id, owner) VALUES ('second_private_boat_from_rower', 1, 1, 2);
|
INSERT INTO "boat" (name, amount_seats, location_id, owner) VALUES ('second_private_boat_from_rower', 1, 1, 2);
|
||||||
INSERT INTO "logbook_type" (name) VALUES ('Wanderfahrt');
|
INSERT INTO "logbook_type" (name) VALUES ('Wanderfahrt');
|
||||||
INSERT INTO "logbook_type" (name) VALUES ('Regatta');
|
INSERT INTO "logbook_type" (name) VALUES ('Regatta');
|
||||||
INSERT INTO "logbook" (boat_id, shipmaster,steering_person, shipmaster_only_steering, departure) VALUES (2, 2, 2, false, '1142-12-24 10:00');
|
INSERT INTO "logbook" (boat_id, shipmaster,steering_person, shipmaster_only_steering, departure) VALUES (2, 2, 2, false, strftime('%Y', 'now') || '-12-24 10:00');
|
||||||
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (1, 4, 4, false, '1141-12-24 10:00', '2141-12-24 15:00', 'Ottensheim', 25);
|
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (1, 4, 4, false, strftime('%Y', 'now') || '-12-24 10:00', strftime('%Y', 'now') || '-12-24 15:00', 'Ottensheim', 25);
|
||||||
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (3, 4, 4, false, '1142-12-24 10:00', '2142-12-24 11:30', 'Ottensheim + Regattastrecke', 29);
|
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (3, 4, 4, false, strftime('%Y', 'now') || '-12-24 10:00', strftime('%Y', 'now') || '-12-24 11:30', 'Ottensheim + Regattastrecke', 29);
|
||||||
INSERT INTO "rower" (logbook_id, rower_id) VALUES(3,3);
|
INSERT INTO "rower" (logbook_id, rower_id) VALUES(3,3);
|
||||||
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02');
|
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02');
|
||||||
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);
|
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::{Datelike, NaiveDateTime, Utc};
|
||||||
use rocket::FromForm;
|
use rocket::FromForm;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
@ -99,6 +99,7 @@ pub enum LogbookUpdateError {
|
|||||||
ShipmasterNotInRowers,
|
ShipmasterNotInRowers,
|
||||||
SteeringPersonNotInRowers,
|
SteeringPersonNotInRowers,
|
||||||
UserNotAllowedToUseBoat,
|
UserNotAllowedToUseBoat,
|
||||||
|
OnlyAllowedToEndTripsEndingToday,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -120,6 +121,7 @@ pub enum LogbookCreateError {
|
|||||||
ShipmasterNotInRowers,
|
ShipmasterNotInRowers,
|
||||||
NotYourEntry,
|
NotYourEntry,
|
||||||
ArrivalSetButNotRemainingTwo,
|
ArrivalSetButNotRemainingTwo,
|
||||||
|
OnlyAllowedToEndTripsEndingToday,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LogbookUpdateError> for LogbookCreateError {
|
impl From<LogbookUpdateError> for LogbookCreateError {
|
||||||
@ -140,6 +142,9 @@ impl From<LogbookUpdateError> for LogbookCreateError {
|
|||||||
LogbookUpdateError::UserNotAllowedToUseBoat => {
|
LogbookUpdateError::UserNotAllowedToUseBoat => {
|
||||||
LogbookCreateError::UserNotAllowedToUseBoat
|
LogbookCreateError::UserNotAllowedToUseBoat
|
||||||
}
|
}
|
||||||
|
LogbookUpdateError::OnlyAllowedToEndTripsEndingToday => {
|
||||||
|
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,14 +225,14 @@ ORDER BY departure DESC
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> {
|
pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> {
|
||||||
let logs = sqlx::query_as!(
|
let year = chrono::Utc::now().year();
|
||||||
Logbook,
|
let logs = sqlx::query_as(
|
||||||
"
|
&format!("
|
||||||
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
|
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
|
||||||
FROM logbook
|
FROM logbook
|
||||||
WHERE arrival is not null
|
WHERE arrival is not null AND arrival LIKE '{}-%'
|
||||||
ORDER BY departure DESC
|
ORDER BY departure DESC
|
||||||
"
|
", year)
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
@ -446,13 +451,8 @@ ORDER BY departure DESC
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !boat.shipmaster_allowed(user).await && self.shipmaster != user.id {
|
if !boat.shipmaster_allowed(user).await && self.shipmaster != user.id {
|
||||||
//second part:
|
//second part: shipmaster has entered a different user, then the user should be able to
|
||||||
//shipmaster has
|
//`home` it
|
||||||
//entered a
|
|
||||||
//different user,
|
|
||||||
//then the user
|
|
||||||
//should be able
|
|
||||||
//to `home` it
|
|
||||||
return Err(LogbookUpdateError::UserNotAllowedToUseBoat);
|
return Err(LogbookUpdateError::UserNotAllowedToUseBoat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,6 +468,10 @@ ORDER BY departure DESC
|
|||||||
if arr.timestamp() <= dep.timestamp() {
|
if arr.timestamp() <= dep.timestamp() {
|
||||||
return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
|
return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
|
||||||
}
|
}
|
||||||
|
let today = Utc::now().date_naive();
|
||||||
|
if arr.date() != today && !user.is_admin {
|
||||||
|
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
|
||||||
|
}
|
||||||
|
|
||||||
Log::create_with_tx(db, format!("New trip: {log:?}")).await;
|
Log::create_with_tx(db, format!("New trip: {log:?}")).await;
|
||||||
|
|
||||||
@ -549,11 +553,11 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completed[0].logbook,
|
completed[0].logbook,
|
||||||
Logbook::find_by_id(&pool, 3).await.unwrap()
|
Logbook::find_by_id(&pool, 2).await.unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
completed[1].logbook,
|
completed[1].logbook,
|
||||||
Logbook::find_by_id(&pool, 2).await.unwrap()
|
Logbook::find_by_id(&pool, 3).await.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,6 +797,8 @@ mod test {
|
|||||||
let logbook = Logbook::find_by_id(&pool, 1).await.unwrap();
|
let logbook = Logbook::find_by_id(&pool, 1).await.unwrap();
|
||||||
let user = User::find_by_id(&pool, 2).await.unwrap();
|
let user = User::find_by_id(&pool, 2).await.unwrap();
|
||||||
|
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
logbook
|
logbook
|
||||||
.home(
|
.home(
|
||||||
&pool,
|
&pool,
|
||||||
@ -806,8 +812,8 @@ mod test {
|
|||||||
shipmaster: Some(2),
|
shipmaster: Some(2),
|
||||||
steering_person: Some(2),
|
steering_person: Some(2),
|
||||||
shipmaster_only_steering: false,
|
shipmaster_only_steering: false,
|
||||||
departure: "1990-01-01T10:00".into(),
|
departure: format!("{}T10:00", current_date),
|
||||||
arrival: "1990-01-01T12:00".into(),
|
arrival: format!("{}T12:00", current_date),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::model::user::User;
|
use crate::model::user::User;
|
||||||
|
use chrono::Datelike;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{FromRow, Row, SqlitePool};
|
use sqlx::{FromRow, Row, SqlitePool};
|
||||||
|
|
||||||
@ -9,15 +10,20 @@ pub struct Stat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Stat {
|
impl Stat {
|
||||||
pub async fn boats(db: &SqlitePool) -> Vec<Stat> {
|
pub async fn boats(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
|
||||||
|
let year = match year {
|
||||||
|
Some(year) => year,
|
||||||
|
None => chrono::Utc::now().year(),
|
||||||
|
};
|
||||||
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
||||||
sqlx::query(
|
sqlx::query(&format!(
|
||||||
"
|
"
|
||||||
SELECT (SELECT name FROM boat WHERE id=logbook.boat_id) as name, CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km
|
SELECT (SELECT name FROM boat WHERE id=logbook.boat_id) as name, CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km
|
||||||
FROM logbook
|
FROM logbook
|
||||||
|
WHERE arrival LIKE '{}-%'
|
||||||
GROUP BY boat_id
|
GROUP BY boat_id
|
||||||
ORDER BY rowed_km DESC;
|
ORDER BY rowed_km DESC;
|
||||||
",
|
",year)
|
||||||
)
|
)
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
@ -30,19 +36,24 @@ ORDER BY rowed_km DESC;
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn people(db: &SqlitePool) -> Vec<Stat> {
|
pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
|
||||||
|
let year = match year {
|
||||||
|
Some(year) => year,
|
||||||
|
None => chrono::Utc::now().year(),
|
||||||
|
};
|
||||||
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
||||||
sqlx::query(
|
sqlx::query(&format!(
|
||||||
"
|
"
|
||||||
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
|
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
|
||||||
FROM user u
|
FROM user u
|
||||||
INNER JOIN rower r ON u.id = r.rower_id
|
INNER JOIN rower r ON u.id = r.rower_id
|
||||||
INNER JOIN logbook l ON r.logbook_id = l.id
|
INNER JOIN logbook l ON r.logbook_id = l.id
|
||||||
WHERE u.is_guest = 0 AND l.distance_in_km IS NOT NULL
|
WHERE u.is_guest = 0 AND l.distance_in_km IS NOT NULL AND l.arrival LIKE '{}-%'
|
||||||
GROUP BY u.name
|
GROUP BY u.name
|
||||||
ORDER BY rowed_km DESC;
|
ORDER BY rowed_km DESC;
|
||||||
",
|
",
|
||||||
)
|
year
|
||||||
|
))
|
||||||
.fetch_all(db)
|
.fetch_all(db)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -182,6 +182,7 @@ async fn create_logbook(
|
|||||||
Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"),
|
Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"),
|
||||||
Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"),
|
Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"),
|
||||||
Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"),
|
Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"),
|
||||||
|
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), format!("Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at).")),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,6 +252,7 @@ async fn home_logbook(
|
|||||||
match logbook.home(db, &user.user, data.into_inner()).await {
|
match logbook.home(db, &user.user, data.into_inner()).await {
|
||||||
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
|
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
|
||||||
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
|
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
|
||||||
|
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), format!("Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at).")),
|
||||||
Err(e) => Flash::error(
|
Err(e) => Flash::error(
|
||||||
Redirect::to("/log"),
|
Redirect::to("/log"),
|
||||||
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
|
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
|
||||||
@ -522,11 +524,12 @@ mod test {
|
|||||||
.header(ContentType::Form) // Set the content type to form
|
.header(ContentType::Form) // Set the content type to form
|
||||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||||
login.dispatch().await;
|
login.dispatch().await;
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client
|
let req = client.post("/log").header(ContentType::Form).body(format!(
|
||||||
.post("/log")
|
"boat_id=1&shipmaster=4&departure={0}T10:00&steering_person=4&rowers[]=4",
|
||||||
.header(ContentType::Form)
|
current_date
|
||||||
.body("boat_id=1&shipmaster=4&departure=2199-12-31T10:00&steering_person=4&rowers[]=4");
|
));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -554,10 +557,12 @@ mod test {
|
|||||||
let req = client.get("/log/kiosk/ekrv2019/Linz");
|
let req = client.get("/log/kiosk/ekrv2019/Linz");
|
||||||
let _ = req.dispatch().await;
|
let _ = req.dispatch().await;
|
||||||
|
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client
|
let req = client
|
||||||
.post("/log/1")
|
.post("/log/1")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
.body("destination=Ottensheim&distance_in_km=25&shipmaster=2&steering_person=2&departure=1990-01-01T10:00&arrival=1990-01-01T12:00&rowers[]=2");
|
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster=2&steering_person=2&departure={0}T10:00&arrival={0}T12:00&rowers[]=2", current_date));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -664,9 +669,10 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.id;
|
.id;
|
||||||
let shipmaster_id = User::find_by_name(&db, "rower2".into()).await.unwrap().id;
|
let shipmaster_id = User::find_by_name(&db, "rower2".into()).await.unwrap().id;
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client.post("/log").header(ContentType::Form).body(format!(
|
let req = client.post("/log").header(ContentType::Form).body(format!(
|
||||||
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=1199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
|
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure={0}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}", current_date
|
||||||
));
|
));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
@ -695,7 +701,7 @@ mod test {
|
|||||||
let req = client
|
let req = client
|
||||||
.post(format!("/log/{log_id}"))
|
.post(format!("/log/{log_id}"))
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure=1990-01-01T10:00&arrival=1990-01-01T12:00&rowers[]={shipmaster_id}"));
|
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure={0}T10:00&arrival={0}T12:00&rowers[]={shipmaster_id}", current_date));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -882,11 +888,12 @@ mod test {
|
|||||||
.header(ContentType::Form) // Set the content type to form
|
.header(ContentType::Form) // Set the content type to form
|
||||||
.body("name=rower2&password=rower"); // Add the form data to the request body;
|
.body("name=rower2&password=rower"); // Add the form data to the request body;
|
||||||
login.dispatch().await;
|
login.dispatch().await;
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client
|
let req = client
|
||||||
.post("/log/1")
|
.post("/log/1")
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
.body("destination=Ottensheim&distance_in_km=25&shipmaster=1&steering_person=1&departure=1199-12-12T10:00&arrival=1199-12-12T12:00&rowers[]=1");
|
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster=1&steering_person=1&departure={0}T10:00&arrival={0}T12:00&rowers[]=1", current_date));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -912,8 +919,10 @@ mod test {
|
|||||||
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
|
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
|
||||||
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
|
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
|
||||||
|
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client.post("/log").header(ContentType::Form).body(format!(
|
let req = client.post("/log").header(ContentType::Form).body(format!(
|
||||||
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=1199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
|
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
|
||||||
));
|
));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
@ -935,7 +944,7 @@ mod test {
|
|||||||
let req = client
|
let req = client
|
||||||
.post(format!("/log/{log_id}"))
|
.post(format!("/log/{log_id}"))
|
||||||
.header(ContentType::Form)
|
.header(ContentType::Form)
|
||||||
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure=1199-12-31T10:00&arrival=1199-12-31T12:00&rowers[]={shipmaster_id}"));
|
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure={current_date}T10:00&arrival={current_date}T12:00&rowers[]={shipmaster_id}"));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::SeeOther);
|
assert_eq!(response.status(), Status::SeeOther);
|
||||||
@ -962,8 +971,10 @@ mod test {
|
|||||||
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
|
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
|
||||||
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
|
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
|
||||||
|
|
||||||
|
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
|
||||||
let req = client.post("/log").header(ContentType::Form).body(format!(
|
let req = client.post("/log").header(ContentType::Form).body(format!(
|
||||||
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=2199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
|
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
|
||||||
));
|
));
|
||||||
let response = req.dispatch().await;
|
let response = req.dispatch().await;
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ use crate::model::{
|
|||||||
|
|
||||||
use super::log::KioskCookie;
|
use super::log::KioskCookie;
|
||||||
|
|
||||||
#[get("/boats", rank = 2)]
|
#[get("/boats?<year>", rank = 2)]
|
||||||
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
||||||
let stat = Stat::boats(db).await;
|
let stat = Stat::boats(db, year).await;
|
||||||
let kiosk = false;
|
let kiosk = false;
|
||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
@ -20,17 +20,21 @@ async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/boats")]
|
#[get("/boats?<year>")]
|
||||||
async fn index_boat_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
|
async fn index_boat_kiosk(
|
||||||
let stat = Stat::boats(db).await;
|
db: &State<SqlitePool>,
|
||||||
|
_kiosk: KioskCookie,
|
||||||
|
year: Option<i32>,
|
||||||
|
) -> Template {
|
||||||
|
let stat = Stat::boats(db, year).await;
|
||||||
let kiosk = true;
|
let kiosk = true;
|
||||||
|
|
||||||
Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true))
|
Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
#[get("/?<year>", rank = 2)]
|
||||||
async fn index(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
async fn index(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
||||||
let stat = Stat::people(db).await;
|
let stat = Stat::people(db, year).await;
|
||||||
let personal = stat::get_personal(db, &user.user).await;
|
let personal = stat::get_personal(db, &user.user).await;
|
||||||
let kiosk = false;
|
let kiosk = false;
|
||||||
|
|
||||||
@ -40,9 +44,9 @@ async fn index(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/?<year>")]
|
||||||
async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
|
async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie, year: Option<i32>) -> Template {
|
||||||
let stat = Stat::people(db).await;
|
let stat = Stat::people(db, year).await;
|
||||||
let kiosk = true;
|
let kiosk = true;
|
||||||
|
|
||||||
Template::render(
|
Template::render(
|
||||||
|
2480
staging-diff.sql
2480
staging-diff.sql
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
|||||||
<div class="max-w-screen-xl w-full flex justify-between items-center">
|
<div class="max-w-screen-xl w-full flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-[#ff0000]">♥</span>
|
<span class="text-[#ff0000]">♥</span>
|
||||||
ASKÖ Ruderverein Donau Linz <small class="text-primary-100">© {{ now() | date(format="%Y") }}</smalL>
|
Erstellt vom ASKÖ Ruderverein Donau Linz <a onclick="alert('Wir suchen kreative und motivierte Köpfe, die diesen Ruderassistenten mitgestalten möchten. Das Backend ist in Rust (Rocket), das Frontend in TypeScript und Teraform, wobei wir mit dem Gedanken spielen, zu Svelte(Kit) zu wechseln.\n\nWenn du Lust hast, deine Skills in ein Projekt zu stecken, das Wellen schlagen wird, dann komm an Bord! Wir sind offen für frische Ideen, haben jedoch auch selber noch genügend; langweilig wird uns bestimmt nicht.\n\nWirf den Anker bei uns ausi und melde dich bei Marie oder Philipp oder it@rudernlinz.at – für eine Zukunft ohne optische Kenterung in Form von hässlichen Alerts ;)');" style="text-decoration:underline">... und dir?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten</h1>
|
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten!</h1>
|
||||||
|
|
||||||
{% include "includes/buttons" %}
|
{% include "includes/buttons" %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user