880 Commits

Author SHA1 Message Date
2159696112 fix migration
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m19s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-16 20:00:44 +02:00
39bde35864 Merge pull request 'add wifi pw in welcome mail' (#593) from add-wifi-pw-new-members into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m45s
Reviewed-on: #593
2024-06-10 22:08:18 +02:00
5f301324ee add wifi pw in welcome mail
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-10 22:07:14 +02:00
9558965e8f Merge pull request 'require necessary fields' (#591) from require-necessary-fields into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m48s
Reviewed-on: #591
2024-06-10 20:59:43 +02:00
5f4d8982a8 require necessary fields
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-10 20:58:42 +02:00
0e2ef9e256 Merge pull request 'add trailer reservation funcitonality; Fixes #443' (#588) from trailer-reservation into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m7s
Reviewed-on: #588
2024-06-10 20:00:25 +02:00
1dc91f4f28 merged :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-10 19:59:39 +02:00
f56da43723 fix copied stuff
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-10 19:48:16 +02:00
9dc1ec6fa0 add trailer reservation funcitonality; Fixes #443
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-10 19:43:59 +02:00
c9b67f5790 Merge pull request 'add delete trip button in edit window; Fixes #449' (#586) from delte-trip-btn into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m53s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m5s
Reviewed-on: #586
2024-06-10 19:08:22 +02:00
16687e39ab remove unused attribute
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-10 19:07:55 +02:00
957c474389 add delete trip button in edit window; Fixes #449
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-10 19:07:05 +02:00
f7aed68423 Merge pull request 'show if a user has < 30 km to thousand km trip, Fixes #551' (#582) from thousand-km-trips into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m59s
Reviewed-on: #582
2024-06-10 15:50:31 +02:00
01637d0800 Merge pull request 'use proper hydro license; add fluctuation' (#584) from proper-hydro into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #584
2024-06-10 15:49:51 +02:00
0a77011170 use proper hydro license; add fluctuation
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-10 15:48:49 +02:00
dea0c65da3 fix ci + add test
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-10 15:12:23 +02:00
31bf38f112 show if a user has < 30 km to thousand km trip, Fixes #551
Some checks failed
CI/CD Pipeline / test (push) Failing after 7m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-10 14:55:01 +02:00
6b29907596 Merge pull request 'fix boat select' (#580) from fix-boat-select into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m34s
Reviewed-on: #580
2024-06-10 11:01:47 +02:00
7b17c30ce2 fix boat select
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-10 11:00:54 +02:00
ec4068e499 Merge pull request 'cleaner cal' (#578) from cleaner-cal into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m47s
Reviewed-on: #578
2024-06-06 17:22:14 +02:00
fca19745f8 even nicer cal entries
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-06-06 17:21:55 +02:00
bb48ddb3de fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-06 17:17:40 +02:00
34b098fa2a cleaner cal
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-06 17:16:47 +02:00
df1a06531f Merge pull request 'try new badge :-)' (#576) from add-badge into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 18m18s
Reviewed-on: #576
2024-06-06 10:48:51 +02:00
7b499fb457 try new badge :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-06 10:38:18 +02:00
d00570ff2f Merge pull request 'nicer-cal' (#574) from nicer-cal into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m0s
Reviewed-on: #574
2024-06-06 07:11:43 +02:00
bd63f2c386 use new additional information in test
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-06 06:59:41 +02:00
e1b78b2725 don't lose trip_type on event cancellation; don't add empty notes in cal
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-06 06:57:43 +02:00
fa14cfbf83 add cancellation, trip_type and notes to cal export
Some checks failed
CI/CD Pipeline / test (push) Failing after 9m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-06 06:47:41 +02:00
5f6cb9a12b Merge pull request 'update deps' (#572) from update-deps into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m43s
Reviewed-on: #572
2024-06-05 15:07:08 +02:00
1cac70cabb update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 19m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-05 14:57:28 +02:00
09cb8ebfa9 Merge pull request 'allow-event-triptype-update' (#570) from allow-event-triptype-update into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m12s
Reviewed-on: #570
2024-06-04 08:32:57 +02:00
ab88ce3230 fix tests
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-06-04 08:13:24 +02:00
30a6bc7109 allow event trip_type update
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-06-04 08:12:20 +02:00
99409f9407 Merge pull request 'remove space' (#568) from fix-spaces into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m42s
Reviewed-on: #568
2024-06-02 14:53:14 +02:00
7eff2a948a remove space
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-06-02 14:52:07 +02:00
243838fd44 Merge pull request 'remove debug println; better phrasing' (#566) from better-text into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #566
2024-05-30 18:53:42 +02:00
2de4c86c26 remove debug println; better phrasing
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-30 11:57:03 +02:00
2889d40d55 Merge pull request 'better-text' (#564) from better-text into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m55s
Reviewed-on: #564
2024-05-30 11:52:17 +02:00
e8d4672176 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-30 11:34:01 +02:00
1bd643f6f4 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-30 11:11:08 +02:00
562c32939d better phrasing of text
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m38s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-30 10:39:55 +02:00
3c0b8e5114 Merge pull request 'fix error' (#560) from fix-user-find-bug into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m56s
Reviewed-on: #560
2024-05-28 15:04:50 +02:00
6d5ff5404b fix error
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-28 15:04:06 +02:00
a8c0282918 Merge pull request 'migrated db' (#558) from migrated-db into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m56s
Reviewed-on: #558
2024-05-28 11:27:46 +02:00
9973913af6 migrated db
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-28 11:26:22 +02:00
7055c999e8 Merge pull request 'rename role to manage_events' (#556) from reanme-to-event into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #556
2024-05-28 11:18:50 +02:00
c0d766832e rename role to manage_events
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-28 10:46:21 +02:00
f88c0be781 Merge pull request 'reanme-to-event' (#554) from reanme-to-event into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #554
2024-05-28 10:06:50 +02:00
91fa2a7762 add test for cancel-event-notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-28 09:59:16 +02:00
82aa94c024 rename planned_event to event
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-28 09:08:48 +02:00
aaf09208f3 Merge pull request 'don't care about cases for username' (#550) from case-insensitive-auth into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m33s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m15s
Reviewed-on: #550
2024-05-27 08:33:07 +02:00
86f7ca7065 don't care about cases for username
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m38s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-27 08:32:00 +02:00
e325e0478a Merge pull request 'better spacing' (#548) from fix-spacing into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m57s
Reviewed-on: #548
2024-05-26 18:48:07 +02:00
0298617fc9 better spacing
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-26 18:47:09 +02:00
02ff89ba34 Merge pull request 'fix spacing' (#546) from fix-spacing into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m38s
Reviewed-on: #546
2024-05-26 14:18:13 +02:00
47a543fa64 fix spacing
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-26 14:17:26 +02:00
4e8fd84134 Merge pull request 'fix typo' (#544) from type into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m22s
Reviewed-on: #544
2024-05-25 18:53:00 +02:00
544267a037 fix typo
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-25 18:52:00 +02:00
97b0ae83a9 Merge pull request 'extend log filter' (#540) from filter-logs into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m20s
Reviewed-on: #540
2024-05-22 23:42:19 +02:00
f6d8c07c08 extend log filter
Some checks failed
CI/CD Pipeline / test (push) Has started running
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-22 23:41:24 +02:00
da56723909 Merge pull request 'fix footer' (#538) from fix-footer into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m0s
Reviewed-on: #538
2024-05-22 22:51:54 +02:00
603aed8394 fix footer
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-22 22:51:10 +02:00
f22d3b65be Merge pull request 'only allow realistic values for logbook entries' (#536) from sanity-check-timing into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #536
2024-05-22 22:41:35 +02:00
446e48020e test fails due to new sanity check (25km in 1 min :-)); started trip 2 hours ago now in tests
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 22:28:33 +02:00
93e3e0ef5c only allow realistic values for logbook entries
Some checks failed
CI/CD Pipeline / test (push) Failing after 14m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 22:05:03 +02:00
8f5cc70981 Merge pull request 'automate schnupper mails' (#534) from automate-schnupper-mails into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m19s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m8s
Reviewed-on: #534
2024-05-22 08:51:41 +02:00
71c228f202 automate schnupper mails
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 08:30:38 +02:00
3ebde6afce Merge pull request 'allow to add reason for canceled event; fixes #530' (#532) from reason-for-canceled-event into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 9m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #532
2024-05-22 08:13:58 +02:00
a797180b0d allow to add reason for canceled event; fixes #530
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m4s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 08:04:17 +02:00
9704893329 Merge pull request 'fix ci' (#529) from fix-ci into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m29s
Reviewed-on: #529
2024-05-22 00:24:46 +02:00
05c4c4f6a2 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-22 00:23:51 +02:00
daf9460bf7 Merge pull request 'clean code with clippy' (#527) from clippy into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #527
2024-05-22 00:18:47 +02:00
db5e0873a6 Merge branch 'staging' into clippy
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 00:18:17 +02:00
40f97f18a9 clean code with clippy
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-05-22 00:13:23 +02:00
6b911f242a Merge pull request 'fix html duplicate tags' (#525) from fix-html into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #525
2024-05-21 23:46:17 +02:00
f4ce748a74 Merge pull request 'fix html duplicate tags' (#524) from fix-html into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m47s
CI/CD Pipeline / deploy-staging (push) Successful in 7m45s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #524
2024-05-21 23:46:14 +02:00
d4ffd8850e fix html duplicate tags
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 23:35:36 +02:00
c93556a5ab Merge pull request 'allow cancel of events' (#522) from allow-cancel-of-events into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m8s
CI/CD Pipeline / deploy-staging (push) Successful in 7m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #522
2024-05-21 22:39:56 +02:00
96ce46d39c Merge branch 'main' into allow-cancel-of-events
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 22:19:33 +02:00
412ec27927 Merge pull request 'remove notificatoin about canceled trip if cancelation has been canceled' (#521) from remove-notification into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m36s
Reviewed-on: #521
2024-05-21 22:18:11 +02:00
a1c7e4c690 Merge pull request 'remove-notification' (#520) from remove-notification into staging
Some checks failed
CI/CD Pipeline / test (push) Successful in 9m25s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #520
2024-05-21 22:17:56 +02:00
1f0de7abf4 allow cancel of events
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-21 22:16:42 +02:00
64f3596132 remove notificatoin about canceled trip if cancelation has been canceled
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 20:43:05 +02:00
3b75f38dca Merge pull request 'remove users from trip after they have read the notificatoin' (#518) from remove-users-from-canceled-trips-after-reading-notification into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m30s
Reviewed-on: #518
2024-05-21 20:01:25 +02:00
10f2e3016a Merge pull request 'Merge pull request 'allow for sending notifications to single users' (#516) from allow-notifications-for-single-user into main' (#517) from remove-users-from-canceled-trips-after-reading-notification into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Successful in 9m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #517
2024-05-21 20:01:17 +02:00
1069e29cf0 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m29s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 19:47:10 +02:00
b4967b54e9 remove users from trip after they have read the notificatoin
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 19:41:00 +02:00
1285c3bc28 Merge pull request 'allow for sending notifications to single users' (#516) from allow-notifications-for-single-user into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #516
2024-05-21 19:27:41 +02:00
b6c9cb0b99 Merge pull request 'allow-notifications-for-single-user' (#515) from allow-notifications-for-single-user into staging
Some checks failed
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
Reviewed-on: #515
2024-05-21 19:27:36 +02:00
29fabb04b0 allow for sending notifications to single users
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 18:57:24 +02:00
830aa58e7b Merge pull request 'don't allow to delete an event if someone is registered' (#514) from deletion-event-only-ok-noone-registered into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m37s
Reviewed-on: #514
2024-05-21 18:44:54 +02:00
1b6aec8d89 Merge pull request 'deletion-event-only-ok-noone-registered' (#513) from deletion-event-only-ok-noone-registered into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m14s
CI/CD Pipeline / deploy-staging (push) Successful in 8m2s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #513
2024-05-21 18:44:47 +02:00
1bf1cc9c68 inform user in case event can't be deleted
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-21 18:34:17 +02:00
02e1f77f65 don't allow to delete an event if someone is registered
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-21 18:28:56 +02:00
d819462b0d Merge pull request 'log if admin resets the pw for someone' (#512) from log-pw-reset into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m21s
Reviewed-on: #512
2024-05-21 09:06:46 +02:00
387acdbd09 Merge pull request 'log if admin resets the pw for someone' (#511) from log-pw-reset into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m31s
CI/CD Pipeline / deploy-staging (push) Successful in 8m6s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #511
2024-05-21 09:06:37 +02:00
b36144832a log if admin resets the pw for someone
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-05-21 09:05:36 +02:00
fc49e6c977 Merge pull request 'inform coxes about status of boats' (#510) from notification-on-boatdamages into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m57s
Reviewed-on: #510
2024-05-20 21:21:56 +02:00
b8463122d6 Merge pull request 'notification-on-boatdamages' (#509) from notification-on-boatdamages into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m48s
CI/CD Pipeline / deploy-staging (push) Successful in 7m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #509
2024-05-20 21:21:42 +02:00
86db4cb2f4 inform coxes about status of boats
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-20 20:32:26 +02:00
82865799ce Merge pull request 'add impressum' (#505) from impressum into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m8s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m46s
Reviewed-on: #505
2024-05-17 15:35:33 +02:00
76f08905ab Merge pull request '[TASK] change layout imprint' (#508) from impressum into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m17s
CI/CD Pipeline / deploy-staging (push) Successful in 5m16s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #508
2024-05-17 13:46:17 +02:00
8dd878b492 [TASK] change layout imprint
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m31s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-17 13:44:09 +02:00
b405cf9936 Merge pull request '[TASK] change layout footer' (#507) from impressum into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #507
2024-05-17 13:43:16 +02:00
01c2f0c4a3 [TASK] change layout footer
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-17 13:39:50 +02:00
0318d1dfb2 Merge pull request 'impressum' (#504) from impressum into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m58s
CI/CD Pipeline / deploy-staging (push) Successful in 6m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #504
2024-05-17 12:29:20 +02:00
261753c6b4 reformat
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-17 12:28:45 +02:00
d0038677ca add last data field
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-17 12:27:58 +02:00
4bd91b2a7e add impressum
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-17 12:26:10 +02:00
17f4291af0 Merge pull request 'marie-magic' (#501) from marie-magic into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m18s
Reviewed-on: #501
2024-05-16 22:46:42 +02:00
073f5aed0c Merge pull request 'marie-magic' (#500) from marie-magic into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m3s
CI/CD Pipeline / deploy-staging (push) Successful in 6m21s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #500
2024-05-16 22:46:39 +02:00
3097d99e00 remove unused imports
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 22:37:57 +02:00
0eac1a66f9 better layout
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-16 22:35:26 +02:00
f034f80794 Merge branch 'main' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt 2024-05-16 22:07:55 +02:00
57c9d532c8 push 2024-05-16 22:07:30 +02:00
b774acf9ae Merge pull request 'show info, if scheckbuch is not yet paid' (#499) from show-scheckbuch-info into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 9m8s
Reviewed-on: #499
2024-05-16 22:07:00 +02:00
20cc085562 Merge pull request 'show info, if scheckbuch is not yet paid' (#498) from show-scheckbuch-info into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m15s
CI/CD Pipeline / deploy-staging (push) Successful in 6m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #498
2024-05-16 21:40:30 +02:00
862ec5624a Merge pull request 'show waterlevel for the next days' (#466) from show-waterlevel into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #466
2024-05-16 21:32:23 +02:00
77a90a8086 Merge branch 'staging' into show-scheckbuch-info
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 21:31:12 +02:00
ca5a932ae5 show info, if scheckbuch is not yet paid
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-16 21:28:36 +02:00
626be1c9fb Merge pull request 'no rain level -> 0; round values' (#497) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Successful in 5m58s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #497
2024-05-16 16:36:39 +02:00
7e2c185c03 no rain level -> 0; round values
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 16:35:49 +02:00
65068e44a5 Merge pull request 'show-waterlevel' (#496) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Successful in 5m59s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #496
2024-05-16 14:59:05 +02:00
133a517a2e Merge branch 'staging' into show-waterlevel
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m33s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 14:47:50 +02:00
3d45310c73 Merge branch 'main' into show-waterlevel
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-16 14:45:31 +02:00
e4ef1f1584 add weather infos
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m16s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-16 14:41:15 +02:00
ebb4fe84bb Merge pull request 'allow removal of guests with special chars (e.g. questionamrk)' (#494) from fix-guest-encoding into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 22m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m41s
Reviewed-on: #494
2024-05-16 08:38:23 +02:00
2bf517ccd8 Merge pull request 'fix-guest-encoding' (#493) from fix-guest-encoding into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 23m39s
CI/CD Pipeline / deploy-staging (push) Successful in 7m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #493
2024-05-16 08:38:21 +02:00
1908f61268 allow removal of guests with special chars (e.g. questionamrk)
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 08:17:53 +02:00
ac3301e97b Merge pull request 'add mail for scheckbuch people' (#492) from welcome-mail-scheckbuch into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m53s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m52s
Reviewed-on: #492
2024-05-15 16:20:19 +02:00
18faf4a72d Merge pull request 'welcome-mail-scheckbuch' (#491) from welcome-mail-scheckbuch into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Successful in 5m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #491
2024-05-15 16:20:12 +02:00
e3c30e010b add mail for scheckbuch people
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m36s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-15 16:01:01 +02:00
d9aa7cafe1 Merge pull request 'welcome-mail' (#490) from welcome-mail into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m11s
Reviewed-on: #490
2024-05-15 14:52:46 +02:00
97b0ce65f9 Merge pull request 'welcome-mail' (#489) from welcome-mail into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Successful in 6m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #489
2024-05-15 14:52:42 +02:00
a465dfcce5 reformat tera
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-15 14:42:58 +02:00
6371366a96 send welcome mail to new members
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-15 14:41:18 +02:00
0952bf7878 Merge pull request 'dont show guests on external boats' (#487) from dont-show-guests-on-external-boats into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m38s
Reviewed-on: #487
2024-05-12 23:08:48 +02:00
fa0dc5b544 Merge pull request 'dont-show-guests-on-external-boats' (#486) from dont-show-guests-on-external-boats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m57s
CI/CD Pipeline / deploy-staging (push) Successful in 6m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #486
2024-05-12 23:08:41 +02:00
c3c7ecec98 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-12 22:38:25 +02:00
a0d53366e0 dont show guests on external boats
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
CI/CD Pipeline / test (push) Failing after 5m15s
2024-05-12 22:26:02 +02:00
b69eded21d Merge pull request 'allow-membershippdf-upload' (#483) from allow-membershippdf-upload into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #483
2024-05-06 13:46:40 +02:00
bd68bfc668 Merge pull request 'allow-membershippdf-upload' (#482) from allow-membershippdf-upload into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m21s
CI/CD Pipeline / deploy-staging (push) Successful in 4m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #482
2024-05-06 13:46:37 +02:00
7355d9d69b allow upload of membership pdf
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-06 13:35:42 +02:00
5602ad2681 Merge pull request 'only have a single user with details struct' (#481) from simplify-user-structs into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #481
2024-05-06 13:30:36 +02:00
6813d75db5 Merge pull request 'only have a single user with details struct' (#480) from simplify-user-structs into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #480
2024-05-06 13:30:33 +02:00
b4023c1ea8 Merge branch 'main' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt 2024-05-06 13:30:17 +02:00
45b51f4698 only have a single user with details struct
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-06 12:17:03 +02:00
31fda6bee9 Merge pull request 'trim name of new user name' (#479) from trim-new-user-names into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m23s
Reviewed-on: #479
2024-05-04 18:36:00 +02:00
e728c4dbea Merge pull request 'Merge pull request 'allow scheckbuch people to be entered in logbook' (#477) from allow-scheckbuch-to-be-entered into main' (#478) from trim-new-user-names into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m16s
CI/CD Pipeline / deploy-staging (push) Successful in 6m10s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #478
2024-05-04 18:35:48 +02:00
1d9adf071f trim name of new user name
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-04 18:19:07 +02:00
fcb4d65d32 Merge pull request 'allow scheckbuch people to be entered in logbook' (#477) from allow-scheckbuch-to-be-entered into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #477
2024-05-01 19:37:14 +02:00
96036b180b Merge pull request 'allow-scheckbuch-to-be-entered' (#476) from allow-scheckbuch-to-be-entered into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m21s
CI/CD Pipeline / deploy-staging (push) Successful in 5m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #476
2024-05-01 19:37:09 +02:00
8c563a9c36 allow scheckbuch people to be entered in logbook
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-01 19:27:52 +02:00
b70929c5ce Merge pull request 'clippy :-)' (#475) from clippy into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m16s
Reviewed-on: #475
2024-04-30 22:26:09 +02:00
c98f33e138 Merge pull request 'clippy :-)' (#474) from clippy into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m20s
CI/CD Pipeline / deploy-staging (push) Successful in 6m16s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #474
2024-04-30 22:26:04 +02:00
17d1ee3566 clippy :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 21:35:14 +02:00
a75ba765df Merge pull request 'don't use default distance of 11; don't overwrite distance if already entered' (#473) from logbook-entry-improvement into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m15s
Reviewed-on: #473
2024-04-30 21:31:33 +02:00
1503544a73 Merge pull request 'don't use default distance of 11; don't overwrite distance if already entered' (#472) from logbook-entry-improvement into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m12s
CI/CD Pipeline / deploy-staging (push) Successful in 6m17s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #472
2024-04-30 21:31:29 +02:00
0b350d344d don't use default distance of 11; don't overwrite distance if already entered
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m4s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 21:21:01 +02:00
67c8431157 Merge pull request 'better phrasing' (#471) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m43s
CI/CD Pipeline / deploy-staging (push) Successful in 6m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #471
2024-04-30 17:04:45 +02:00
d6ecd87593 better phrasing
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 17:02:22 +02:00
03073965a1 Merge pull request 'use recommended method of 'sleep'' (#470) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m52s
CI/CD Pipeline / deploy-staging (push) Successful in 6m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #470
2024-04-30 15:56:47 +02:00
2189b082c0 use recommended method of 'sleep'
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 15:56:21 +02:00
6d4bc81720 Merge pull request 'remove unnecessary async' (#469) from show-waterlevel into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #469
2024-04-30 15:48:45 +02:00
25fe4c23ef remove unnecessary async
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-30 15:47:40 +02:00
2fdfddbd2e Merge pull request 'add tooltip + link' (#468) from show-waterlevel into staging
Reviewed-on: #468
2024-04-30 15:14:26 +02:00
dea6520aa9 add tooltip + link
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 15:13:42 +02:00
f8e0cd2d5b Merge pull request 'deployed :-)' (#467) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Successful in 5m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #467
2024-04-30 14:35:45 +02:00
9fda9cbde2 deployed :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 14:35:30 +02:00
74b24569dd Merge pull request 'show-waterlevel' (#465) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m30s
CI/CD Pipeline / deploy-staging (push) Successful in 7m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #465
2024-04-30 13:06:08 +02:00
3a39315a01 show waterlevel for the next days
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 11:59:33 +02:00
3323807e46 Merge pull request 'no boat is selected by default in the logbook -> users don't accidentally 'select' external boat' (#459) from no-boat-selected-by-default-in-logbook into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m21s
Reviewed-on: #459
2024-04-29 22:30:29 +02:00
65d51c2cc2 Merge pull request 'no-boat-selected-by-default-in-logbook' (#458) from no-boat-selected-by-default-in-logbook into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m9s
CI/CD Pipeline / deploy-staging (push) Successful in 6m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #458
2024-04-29 22:30:24 +02:00
8773bbb9d1 Merge branch 'main' into no-boat-selected-by-default-in-logbook
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-29 22:29:52 +02:00
89ac4974db Merge pull request 'rephrase scheckbuch button' (#461) from rephrase-scheckbuch-button into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #461
2024-04-29 22:29:16 +02:00
7dfdc55adb Merge pull request 'rephrase-scheckbuch-button' (#460) from rephrase-scheckbuch-button into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #460
2024-04-29 22:29:12 +02:00
e4f1528b15 fix ci; proper click @ boat selector
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-29 22:17:40 +02:00
c449e878f0 rephrase scheckbuch button
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-29 21:51:27 +02:00
311e611d5f no boat is selected by default in the logbook -> users don't accidentally 'select' external boat
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-29 21:40:35 +02:00
8e5661b2f3 Merge pull request 'treat empty membership pdf as non-existing' (#457) from treat-empty-membershippdf-as-nonexisting into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m7s
Reviewed-on: #457
2024-04-29 21:33:49 +02:00
79976b751f Merge pull request 'treat empty membership pdf as non-existing' (#456) from treat-empty-membershippdf-as-nonexisting into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m29s
CI/CD Pipeline / deploy-staging (push) Successful in 6m6s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #456
2024-04-29 21:33:44 +02:00
139acb2ec5 treat empty membership pdf as non-existing
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-29 21:10:34 +02:00
5dcd7bc745 Merge pull request 'proper-time-in-notificatoins' (#453) from proper-time-in-notificatoins into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m20s
Reviewed-on: #453
2024-04-29 09:18:58 +02:00
ebdfe37bec Merge pull request 'proper-time-in-notificatoins' (#452) from proper-time-in-notificatoins into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m55s
CI/CD Pipeline / deploy-staging (push) Successful in 5m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #452
2024-04-29 09:18:55 +02:00
08fe779403 remove debug println
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-29 08:48:43 +02:00
9ca510b892 show proper time in notifications
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-29 08:48:15 +02:00
d01895df90 Merge pull request 'don't respond with 500 if no rower is selected' (#451) from require-user-for-logentry into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m46s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m18s
Reviewed-on: #451
2024-04-28 19:25:33 +02:00
f08d9728eb Merge pull request 'require-user-for-logentry' (#450) from require-user-for-logentry into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m57s
CI/CD Pipeline / deploy-staging (push) Successful in 6m0s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #450
2024-04-28 19:25:27 +02:00
c27a2ad15e don't respond with 500 if no rower is selected
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-28 19:16:38 +02:00
18047d16bf Merge pull request 'fix spacing if boat was steered by hand' (#447) from fix-spacing into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #447
2024-04-26 10:07:03 +02:00
1866034431 Merge pull request 'fix-spacing' (#446) from fix-spacing into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #446
2024-04-26 10:07:00 +02:00
e2306e890d Merge pull request 'if user is logged in, use that user as default rower' (#445) from prefill-own-user-default-log into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #445
2024-04-26 09:59:09 +02:00
688ce4c6fc Merge pull request 'prefill-own-user-default-log' (#444) from prefill-own-user-default-log into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #444
2024-04-26 09:59:02 +02:00
46f8ca230a fix spacing if boat was steered by hand
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-26 09:54:24 +02:00
fcf86c7ff1 if user is logged in, use that user as default rower
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-26 09:50:01 +02:00
123b4bd39f Merge pull request 'no-special-treatment-with-boatname' (#442) from no-special-treatment-with-boatname into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m34s
Reviewed-on: #442
2024-04-24 17:23:49 +02:00
7a8b79ccef Merge pull request 'no-special-treatment-with-boatname' (#441) from no-special-treatment-with-boatname into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Successful in 5m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #441
2024-04-24 17:23:46 +02:00
5eadfd42bb don't show link if the user has no permission
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-24 17:18:57 +02:00
87307378c6 no special treatment for 'externes boot' 2024-04-24 17:15:53 +02:00
8b42bdce0c Merge pull request 'easier handling of external boats, show all in separate category' (#440) from log-handle-external-boats into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #440
2024-04-24 17:03:44 +02:00
5bb0cb4112 Merge pull request 'log-handle-external-boats' (#439) from log-handle-external-boats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m17s
CI/CD Pipeline / deploy-staging (push) Successful in 5m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #439
2024-04-24 17:03:41 +02:00
237377dc05 easier handling of external boats, show all in separate category
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 16:56:48 +02:00
fc7ca28f56 Merge pull request 'fix-ci-db' (#438) from fix-ci-db into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m16s
Reviewed-on: #438
2024-04-24 16:41:02 +02:00
6a18d7435a Merge pull request 'fix-ci-db' (#437) from fix-ci-db into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #437
2024-04-24 15:58:18 +02:00
a42191715d fix ci
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-24 15:56:40 +02:00
43e073c54e calc general boat cat (#436)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m19s
Reviewed-on: #436
2024-04-24 15:39:13 +02:00
14e7616b88 calc-general-boatcat (#435)
Some checks failed
CI/CD Pipeline / test (push) Successful in 7m59s
CI/CD Pipeline / deploy-staging (push) Failing after 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #435
2024-04-24 15:39:07 +02:00
8e03c935a5 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 15:32:33 +02:00
0560ed7a6a fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 15:29:52 +02:00
07b197cc63 check changing of handoperated in backend
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 15:25:42 +02:00
a1ebd59f22 calc general boat cat
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 15:12:27 +02:00
aac7444896 Merge pull request 'handoperatable-feature' (#432) from handoperatable-feature into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m57s
Reviewed-on: #432
2024-04-24 14:43:24 +02:00
d646996c80 Merge branch 'main' into handoperatable-feature
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 12:21:07 +02:00
1412852087 Merge pull request 'handoperatable-feature' (#431) from handoperatable-feature into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m28s
CI/CD Pipeline / deploy-staging (push) Successful in 17m27s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #431
2024-04-24 12:19:06 +02:00
5f5f215aad Merge pull request 'switch to smarter ci cache, which knows intricacies of rust; e.g. auto deletes unused deps' (#434) from smarter-ci-cache into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m31s
Reviewed-on: #434
2024-04-24 12:04:26 +02:00
7a59c67763 Merge pull request 'smarter-ci-cache' (#433) from smarter-ci-cache into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #433
2024-04-24 12:04:22 +02:00
a2b0146d6d fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 11:57:26 +02:00
7f813823f3 switch to smarter ci cache, which knows intricacies of rust; e.g. auto deletes unused deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 10:14:11 +02:00
d91baeb7bc Merge branch 'staging' into handoperatable-feature
Some checks failed
CI/CD Pipeline / test (push) Failing after 11m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 09:56:47 +02:00
b5cdc8827a Merge pull request 'format-tera' (#429) from format-tera into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m7s
CI/CD Pipeline / deploy-staging (push) Successful in 7m15s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #429
2024-04-24 09:47:57 +02:00
b6b88ead37 Merge branch 'staging' into format-tera
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-24 09:46:54 +02:00
6b3851bfe4 Merge pull request 'format tera w/ linter' (#430) from format-tera into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m19s
Reviewed-on: #430
2024-04-24 09:46:19 +02:00
a0dbd0f490 implement first draft of switching handoperatable boats
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 09:37:45 +02:00
3f80cb498c format tera w/ linter
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 08:57:41 +02:00
7ba5070df3 Merge pull request 'group-reservations-in-log' (#426) from group-reservations-in-log into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m1s
Reviewed-on: #426
2024-04-24 08:53:52 +02:00
cc1a7106cb Merge pull request 'group-reservations-in-log' (#425) from group-reservations-in-log into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m51s
CI/CD Pipeline / deploy-staging (push) Successful in 7m5s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #425
2024-04-24 08:51:38 +02:00
d4218289f0 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 22m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-24 08:27:54 +02:00
349a9f843c fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 07:39:44 +02:00
982bb3b5c8 Merge pull request 'fix ci' (#428) from fix-ci into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m30s
Reviewed-on: #428
2024-04-24 07:21:34 +02:00
b4f38089ee Merge pull request 'fix-ci' (#427) from fix-ci into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m21s
CI/CD Pipeline / deploy-staging (push) Successful in 7m25s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #427
2024-04-24 07:20:50 +02:00
e22dd8eb51 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-24 07:20:16 +02:00
ac9c4b256e Merge pull request 'allow-reservation-edits' (#424) from allow-reservation-edits into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 6m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #424
2024-04-23 23:23:10 +02:00
cfec4ef8b3 Merge branch 'main' into allow-reservation-edits
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-23 23:10:02 +02:00
a424e79cba Merge pull request 'allow-reservation-edits' (#423) from allow-reservation-edits into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 6m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #423
2024-04-23 23:05:47 +02:00
be24001b4b Merge branch 'allow-reservation-edits' into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
# Conflicts:
#	src/tera/boatreservation.rs
2024-04-23 23:04:46 +02:00
d9885f9bba [TASK] change padding form
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-23 22:58:48 +02:00
6c2e0669d5 [TASK] improve sorting boats in schnellauswahl
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-23 22:46:05 +02:00
e556b1375d [TASK] improve styling boat list
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-23 22:39:59 +02:00
23a623bdc9 [TASK] add etsch edge case js
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-23 22:36:51 +02:00
b1d48a5154 [TASK] improve sidebar css 2024-04-23 22:35:52 +02:00
61261c9816 allow edits of boatreservations, Fixes #417
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-23 22:23:24 +02:00
f993eae27f Merge pull request 'add script to fetch current db' (#422) from fetch-db-script into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m39s
Reviewed-on: #422
2024-04-23 21:12:37 +02:00
f1ee266288 Merge pull request 'add script to fetch current db' (#421) from fetch-db-script into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m53s
CI/CD Pipeline / deploy-staging (push) Successful in 6m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #421
2024-04-23 21:12:14 +02:00
9aefc814a7 Merge pull request '[TASK] group reservations in log to avoid near-duplicates' (#420) from group-reservations-in-log into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #420
2024-04-23 21:11:42 +02:00
548c025fbc Merge pull request '[TASK] group reservations in log to avoid near-duplicates' (#419) from group-reservations-in-log into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #419
2024-04-23 21:11:18 +02:00
8f44fdadf2 add script to fetch current db
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-23 21:10:04 +02:00
3abff08f61 [TASK] group reservations in log to avoid near-duplicates
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-23 20:40:06 +02:00
32dee2c320 Merge pull request 'allow to upload membership pdf for users which don't have it' (#416) from allow-membership-uploads into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m48s
Reviewed-on: #416
2024-04-22 10:37:49 +02:00
3c67e0deeb Merge pull request 'allow to upload membership pdf for users which don't have it' (#415) from allow-membership-uploads into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m35s
CI/CD Pipeline / deploy-staging (push) Successful in 7m26s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #415
2024-04-22 10:37:09 +02:00
ef21e719c8 allow to upload membership pdf for users which don't have it
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-22 10:36:47 +02:00
4a7cd2f085 Merge pull request 'update deps' (#414) from update-deps into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 7m0s
Reviewed-on: #414
2024-04-19 16:38:31 +02:00
27b124cce5 Merge pull request 'update deps' (#413) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 29m46s
CI/CD Pipeline / deploy-staging (push) Successful in 23m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #413
2024-04-19 16:38:23 +02:00
254e8b7063 update deps
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-19 16:36:42 +02:00
31d45d6ab4 Merge pull request 'notification-badge' (#401) from notification-badge into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #401
2024-04-19 16:36:16 +02:00
c122dea6a9 Merge pull request 'notification-badge' (#409) from notification-badge into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m20s
CI/CD Pipeline / deploy-staging (push) Successful in 18m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #409
2024-04-19 15:54:58 +02:00
2ad5f0883c make clicking on 'create trip' button more robust (clicked on notification as this was the first .relative element
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 15:33:44 +02:00
8f2d1ef6f4 wait until dev server started for frontend tests
Some checks failed
CI/CD Pipeline / test (push) Failing after 23m51s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 15:04:03 +02:00
d6214d5369 fix comma issue
Some checks failed
CI/CD Pipeline / test (push) Failing after 23m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 13:17:22 +02:00
74ededf913 fix ci
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-19 13:09:17 +02:00
8ffed75251 don't show warning about many notifications if they are already read
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-19 13:00:39 +02:00
73a6f1d58c Merge branch 'notification-badge' of https://git.hofer.link/Ruderverein-Donau-Linz/rowt into notification-badge
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-19 12:54:47 +02:00
2540e49a31 [TASK] improve styling text warning notifications 2024-04-19 12:54:32 +02:00
d612cf01b6 Merge pull request 'main' (#412) from main into notification-badge
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #412
2024-04-19 12:53:22 +02:00
44c1b1bb72 Merge pull request 'use new docker image' (#411) from new-docker-image into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 18m3s
Reviewed-on: #411
2024-04-19 12:51:48 +02:00
e3895f3c9c Merge pull request 'use new docker image' (#410) from new-docker-image into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m33s
CI/CD Pipeline / deploy-staging (push) Successful in 18m18s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #410
2024-04-19 12:51:12 +02:00
e00142926e Merge branch 'notification-badge' of https://git.hofer.link/Ruderverein-Donau-Linz/rowt into notification-badge 2024-04-19 12:48:33 +02:00
4c6ef71a17 [TASK] improve btn sidebar unregister guest 2024-04-19 12:48:23 +02:00
7b32b9bbcb use new docker image
All checks were successful
CI/CD Pipeline / test (push) Successful in 24m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 12:27:06 +02:00
9def90daa7 show information that people should mark msgs as read if there are more than 10 unread msgs
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 12:06:24 +02:00
c528b8831a [TASK] finalize notification styling
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 11:25:25 +02:00
30756ad4aa [TASK] improve styling
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m38s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 11:09:31 +02:00
8aa4f3577a Merge pull request 'cleaner logs' (#408) from cleaner-logs into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 10m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #408
2024-04-19 10:40:40 +02:00
6416356d89 Merge pull request 'cleaner logs' (#407) from cleaner-logs into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m47s
CI/CD Pipeline / deploy-staging (push) Successful in 5m31s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #407
2024-04-19 10:40:35 +02:00
bc2790fd4d cleaner logs
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-19 09:16:55 +02:00
b0ceb38e22 Merge pull request 'improve error msg if event is locked' (#405) from improve-error-msg into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #405
2024-04-19 08:47:43 +02:00
377be7c3b7 Merge pull request 'improve error msg if event is locked' (#404) from improve-error-msg into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m58s
CI/CD Pipeline / deploy-staging (push) Successful in 5m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #404
2024-04-19 08:47:19 +02:00
96c07c0eb3 improve error msg if event is locked
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-19 08:45:56 +02:00
010e600aa6 Merge pull request 'improve error msg if event is locked' (#403) from improve-error-msg into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #403
2024-04-18 21:18:53 +02:00
cf46032f24 Merge pull request 'improve error msg if event is locked' (#402) from improve-error-msg into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m3s
CI/CD Pipeline / deploy-staging (push) Successful in 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #402
2024-04-18 21:18:22 +02:00
ff27eb5eed improve error msg if event is locked
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-18 21:17:26 +02:00
aecfb27d6e Merge pull request 'notification-badge' (#400) from notification-badge into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m11s
CI/CD Pipeline / deploy-staging (push) Successful in 5m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #400
2024-04-17 14:43:37 +02:00
686feaf66b Merge branch 'main' into notification-badge
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-17 14:10:14 +02:00
c658ea133d Merge pull request 'allow-boat-deletion' (#398) from allow-boat-deletion into staging
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #398
2024-04-17 14:08:00 +02:00
cf56e8f6fe Merge pull request 'allow deletion of boat' (#399) from allow-boat-deletion into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m30s
Reviewed-on: #399
2024-04-17 14:07:55 +02:00
4bec5443a2 Merge pull request 'dont-show-steer-btn-when-no-coxes' (#396) from dont-show-steer-btn-when-no-coxes into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #396
2024-04-17 13:58:11 +02:00
ee7c62c3b7 Merge pull request 'don't show steer button when there are no coxes planned for this event; Fixes #357' (#397) from dont-show-steer-btn-when-no-coxes into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #397
2024-04-17 13:57:53 +02:00
42a3addd9a make it more clear which action is required if notifications are present
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-17 13:57:24 +02:00
7c71ce59bd show notification badge in menu
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-17 13:51:47 +02:00
858ae28eb3 allow deletion of boat
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m16s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-17 13:18:35 +02:00
55b259061b don't show steer button when there are no coxes planned for this event; Fixes #357
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-17 13:04:03 +02:00
e2746d5105 Merge pull request 'send creator of boatdamage notification when it has been repaired' (#395) from boatdamage-notification-to-creator into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m25s
Reviewed-on: #395
2024-04-17 13:02:53 +02:00
1dd752f354 Merge pull request 'boatdamage-notification-to-creator' (#394) from boatdamage-notification-to-creator into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m46s
CI/CD Pipeline / deploy-staging (push) Successful in 5m29s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #394
2024-04-17 13:02:49 +02:00
22d499d38b send creator of boatdamage notification when it has been repaired
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-17 12:44:31 +02:00
d0830e4631 Merge pull request 'don't clutter logs' (#393) from fix-logs into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m23s
Reviewed-on: #393
2024-04-17 12:22:59 +02:00
ef85d30846 Merge pull request 'fix-logs' (#392) from fix-logs into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m47s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #392
2024-04-17 12:22:32 +02:00
36d7c43bbd don't clutter logs
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-17 12:21:57 +02:00
3ff8067c51 Merge pull request '[TASK] change favicon to make it sharper' (#391) from favicon into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m28s
Reviewed-on: #391
2024-04-17 11:41:59 +02:00
4d3af37d5f Merge pull request 'favicon' (#390) from favicon into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m1s
CI/CD Pipeline / deploy-staging (push) Successful in 5m32s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #390
2024-04-17 11:41:26 +02:00
0f96f39f24 [TASK] change favicon to make it sharper
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-17 11:35:12 +02:00
b32bdb5a70 Merge pull request 'styling-mail' (#389) from styling-mail into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #389
2024-04-17 11:34:07 +02:00
6956e4c487 Merge pull request 'styling-mail' (#388) from styling-mail into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m50s
CI/CD Pipeline / deploy-staging (push) Successful in 5m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #388
2024-04-17 11:10:45 +02:00
acb1c711e5 [TASK] style notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m53s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-17 10:56:22 +02:00
ec0bd91aa3 [TASK] styling mail
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-17 10:54:07 +02:00
de786c2b51 Merge pull request 'fix-spacing' (#387) from fix-spacing into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m31s
Reviewed-on: #387
2024-04-16 20:07:11 +02:00
558f600271 Merge pull request 'no space before '/'' (#386) from fix-spacing into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m5s
CI/CD Pipeline / deploy-staging (push) Successful in 5m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #386
2024-04-16 20:06:33 +02:00
5c680a8bea no space before '/'
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-16 20:05:55 +02:00
74f4d4854a Merge pull request 'dont clutter logs' (#385) from usage-stats into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m29s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m27s
Reviewed-on: #385
2024-04-16 10:00:45 +02:00
4b49b5517d Merge pull request 'dont clutter logs' (#384) from usage-stats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m49s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #384
2024-04-16 10:00:05 +02:00
0cd623482c dont clutter logs
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-16 09:59:39 +02:00
0a01b95c85 Merge pull request 'fix ci' (#383) from usage-stats into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m30s
Reviewed-on: #383
2024-04-16 09:08:26 +02:00
6daf2495a8 Merge pull request 'fix ci' (#381) from usage-stats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m10s
CI/CD Pipeline / deploy-staging (push) Successful in 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #381
2024-04-16 09:07:31 +02:00
8771a378da fix ci
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-16 09:06:24 +02:00
e84547c8ca Merge pull request 'log usage' (#380) from usage-stats into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #380
2024-04-16 08:54:25 +02:00
872dcd0668 Merge pull request 'log usage' (#379) from usage-stats into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 10m29s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #379
2024-04-16 08:53:46 +02:00
a9f74fcd3c log usage
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-16 08:52:37 +02:00
95752703ff Merge pull request 'clean w/ clippy' (#378) from clippy into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 13m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #378
2024-04-15 23:37:27 +02:00
5e19a62c7e Merge pull request 'clippy' (#377) from clippy into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m44s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #377
2024-04-15 23:37:18 +02:00
2694829b6e clean w/ clippy
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-15 23:26:52 +02:00
151e1b7864 Merge pull request 'show steering person in logs if not cox' (#376) from show-steering-person-in-logs into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #376
2024-04-15 22:04:14 +02:00
5965b1d626 Merge pull request 'show steering person in logs if not cox' (#375) from show-steering-person-in-logs into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m46s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #375
2024-04-15 22:03:39 +02:00
219b80377d show notification to vorstand if boat entry with 'externes boot' or on multiple days is entered
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-15 22:03:20 +02:00
8315a27ea8 fix tests (proper timezone), proper spacing
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-15 21:32:16 +02:00
d070c7731a show steering person in logs if not cox
Some checks failed
CI/CD Pipeline / test (push) Failing after 13m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-15 20:45:26 +02:00
c8f614e2d2 Merge pull request 'staging' (#374) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m46s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m29s
Reviewed-on: #374
2024-04-15 18:49:51 +02:00
4b07c11bc3 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-15 18:24:18 +02:00
0bc00472d7 don't create any notification if we are working with planned_event
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-15 18:22:58 +02:00
37da4f2c3e Merge branch 'main' into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-15 18:16:57 +02:00
74505c1554 add notification to all pot. coxes if trip is full 2024-04-15 18:16:43 +02:00
1869b36e09 add notification on canceled trips; add explicit 'cancel trip' button 2024-04-15 17:17:54 +02:00
13808d0103 Merge pull request 'staging' (#373) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m32s
Reviewed-on: #373
2024-04-15 11:26:20 +02:00
4a3803df51 Merge branch 'main' into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m8s
CI/CD Pipeline / deploy-staging (push) Successful in 5m30s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-15 11:25:49 +02:00
d58d4642af 1x boats: don't show 'Ruderer: ' in logbook; Fixes #362
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-15 11:23:07 +02:00
c3fa5195d3 Merge pull request 'hide old verified boat damages' (#372) from hide-old-verified-boatdamages into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m33s
Reviewed-on: #372
2024-04-14 21:40:39 +02:00
c4a9a541d3 Merge pull request 'don't show external boats in boatstat; hide logout in menu; move owner into own column in boatstat' (#369) from updates into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #369
2024-04-14 21:40:27 +02:00
8db9e020c8 Merge pull request 'hide old verified boat damages' (#371) from hide-old-verified-boatdamages into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m11s
CI/CD Pipeline / deploy-staging (push) Successful in 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #371
2024-04-14 20:28:42 +02:00
42277699a7 hide old verified boat damages
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-14 20:28:06 +02:00
a1b78db750 Merge pull request 'don't show external boats in boatstat; hide logout in menu; move owner into own column in boatstat' (#368) from updates into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #368
2024-04-14 20:22:18 +02:00
7ebbf5661a don't show external boats in boatstat; hide logout in menu; move owner into own column in boatstat
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-14 20:20:22 +02:00
2ff08141ae Merge pull request 'steering' (#367) from steering into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m36s
Reviewed-on: #367
2024-04-14 20:18:45 +02:00
297e0629a4 Merge pull request 'steering' (#366) from steering into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m0s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #366
2024-04-14 19:57:26 +02:00
213a80bd28 Merge branch 'staging' into steering
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-14 19:56:58 +02:00
b111c4a1b9 lint html
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-14 18:38:41 +02:00
db43ef628f [TASK] style steering html
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-04-14 18:36:27 +02:00
ccf9f41f4e Merge branch 'staging' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m18s
CI/CD Pipeline / deploy-staging (push) Successful in 5m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-14 18:27:35 +02:00
fb9e694919 add required role 2024-04-14 18:27:21 +02:00
ea70170d2b Merge pull request '[TASK] improve style searchable table' (#365) from js-table into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m36s
Reviewed-on: #365
2024-04-14 18:19:22 +02:00
36af52bf7e Merge pull request 'js-table' (#364) from js-table into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m21s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #364
2024-04-14 17:56:43 +02:00
0b6461eeb5 [TASK] improve style searchable table
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m4s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-14 17:43:18 +02:00
b0c936cc34 Merge pull request 'have test users only @ staging' (#363) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m34s
Reviewed-on: #363
2024-04-14 17:00:53 +02:00
d51174e8fd have test users only @ staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m26s
CI/CD Pipeline / deploy-staging (push) Successful in 5m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-14 16:59:17 +02:00
e2a30dad52 Merge pull request 'staging' (#360) from staging into main
Reviewed-on: #360
2024-04-13 09:52:24 +02:00
74d3957cf8 dont show external boat
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m9s
CI/CD Pipeline / deploy-staging (push) Successful in 5m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-13 09:44:22 +02:00
cfc35fbec6 only show non-placed boats in boathouse list
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-13 09:42:12 +02:00
b1d3f8ddc9 Merge pull request 'allow html in notifications' (#359) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m25s
Reviewed-on: #359
2024-04-12 11:33:26 +02:00
3b04b39d66 allow html in notifications
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m37s
CI/CD Pipeline / deploy-staging (push) Successful in 5m31s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-12 11:32:21 +02:00
cdcb07a3f7 Merge pull request 'update deps' (#356) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m26s
Reviewed-on: #356
2024-04-09 11:25:01 +02:00
19523acc67 update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 26m44s
CI/CD Pipeline / deploy-staging (push) Successful in 19m55s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-09 11:24:38 +02:00
faa8e6a13b Merge pull request 'fix ci' (#355) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m1s
Reviewed-on: #355
2024-04-09 08:08:11 +02:00
0fed206df6 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m48s
CI/CD Pipeline / deploy-staging (push) Successful in 5m6s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-09 08:07:56 +02:00
7660953e6b Merge pull request 'don't clutter logs' (#354) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #354
2024-04-09 07:30:40 +02:00
4a5d9fa65b don't clutter logs
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-09 07:30:05 +02:00
83c0285204 Merge pull request 'staging' (#353) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m32s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m4s
Reviewed-on: #353
2024-04-08 23:22:24 +02:00
3823d959e8 add stats from nginx logs
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m48s
CI/CD Pipeline / deploy-staging (push) Successful in 7m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-08 23:21:35 +02:00
6c302712d4 add stats from nginx logs
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-08 23:20:33 +02:00
f93677b420 add stats from nginx logs 2024-04-08 23:19:59 +02:00
23cd62820c Merge pull request 'only show notifications from last 2 weeks' (#352) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m19s
Reviewed-on: #352
2024-04-08 20:25:37 +02:00
39d410b050 only show notifications from last 2 weeks
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m35s
CI/CD Pipeline / deploy-staging (push) Successful in 5m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-08 20:25:13 +02:00
61c67d78da Merge pull request 'allow admin to send notifications' (#351) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m31s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m36s
Reviewed-on: #351
2024-04-08 19:36:35 +02:00
64d32e2688 allow admin to send notifications
Some checks failed
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Successful in 11m20s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
2024-04-08 19:35:31 +02:00
1aba6948ee Merge pull request 'only allow people with 'donau linz' role to be in logbook' (#350) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 11m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #350
2024-04-08 19:05:29 +02:00
3b9103e9aa fix membership application error
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m50s
CI/CD Pipeline / deploy-staging (push) Successful in 6m55s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-08 19:04:57 +02:00
db3158d4e7 only allow people with 'donau linz' role to be in logbook
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m24s
CI/CD Pipeline / deploy-staging (push) Successful in 5m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-07 23:17:06 +02:00
08a970853a Merge pull request 'better text for boat reservation' (#349) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m1s
Reviewed-on: #349
2024-04-07 22:13:32 +02:00
7cbfafa5c5 better text for boat reservation
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m45s
CI/CD Pipeline / deploy-staging (push) Successful in 6m36s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-07 22:12:56 +02:00
c64f392fe7 Merge pull request 'improve header of list' (#348) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m32s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m2s
Reviewed-on: #348
2024-04-07 20:12:22 +02:00
4fef4ca2c6 improve header of list
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m21s
CI/CD Pipeline / deploy-staging (push) Successful in 5m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-07 20:11:41 +02:00
268c2018ae Merge pull request 'allow to search by nickname' (#347) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m59s
Reviewed-on: #347
2024-04-07 15:31:07 +02:00
b7e8e1fa37 keep defaults
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m40s
CI/CD Pipeline / deploy-staging (push) Successful in 5m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-07 15:30:52 +02:00
fe6af27813 allow to search by nickname
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-07 15:26:37 +02:00
a2005c55aa Merge pull request 'show boats w/o km' (#346) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m40s
Reviewed-on: #346
2024-04-06 21:17:43 +02:00
da446c5073 show boats w/o km
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m23s
CI/CD Pipeline / deploy-staging (push) Successful in 5m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-06 21:17:23 +02:00
d3bc2bea4f Merge pull request 'staging' (#345) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m38s
Reviewed-on: #345
2024-04-06 20:00:25 +02:00
37fcdb81bc remove unnecessary if
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m32s
CI/CD Pipeline / deploy-staging (push) Successful in 6m48s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-06 19:57:42 +02:00
b3041d9ca7 also show boats w/o any km
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-06 19:56:06 +02:00
7c8f20623c Merge pull request 'show owner of boat in boat km list' (#344) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m34s
Reviewed-on: #344
2024-04-06 18:57:57 +02:00
8b07ace876 show owner of boat in boat km list
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m18s
CI/CD Pipeline / deploy-staging (push) Successful in 5m32s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-06 18:55:38 +02:00
073b2aca04 Merge pull request 'don't show external cox as cox' (#343) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m25s
Reviewed-on: #343
2024-04-06 18:40:29 +02:00
934795abd8 don't show external cox as cox
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-06 18:40:01 +02:00
c3965c9528 Merge pull request 'staging' (#342) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #342
2024-04-06 18:29:27 +02:00
5164ce1f02 show list of coxes
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-06 18:27:20 +02:00
18cdb20923 Merge pull request 'fancier-boat-stats' (#341) from fancier-boat-stats into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m33s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m25s
Reviewed-on: #341
2024-04-06 17:56:07 +02:00
85a61dfdc0 Merge pull request 'fancier-boat-stats' (#340) from fancier-boat-stats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m33s
CI/CD Pipeline / deploy-staging (push) Successful in 5m36s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #340
2024-04-06 17:54:42 +02:00
c094097af7 don't show boats which are still on the water
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-06 17:54:07 +02:00
39ba7d53dd make table readable
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-06 17:44:05 +02:00
98fc037f73 Merge pull request 'fancier boat stats' (#339) from fancier-boat-stats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m19s
CI/CD Pipeline / deploy-staging (push) Successful in 5m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #339
2024-04-06 15:28:28 +02:00
955f657298 fancier boat stats
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-06 15:27:35 +02:00
853f9d901e Merge pull request 'staging' (#338) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m22s
Reviewed-on: #338
2024-04-03 18:07:59 +02:00
d0c2b4d703 don't show duplicate boats for rennrowing
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m45s
CI/CD Pipeline / deploy-staging (push) Successful in 12m1s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-03 18:07:20 +02:00
1af3838ebc use proper timezone
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m3s
CI/CD Pipeline / deploy-staging (push) Successful in 4m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-03 08:13:32 +02:00
d0bbf8f181 Merge pull request 'staging' (#337) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m6s
Reviewed-on: #337
2024-04-03 08:07:48 +02:00
8c8a5c9762 show boats which have place in boatshouse
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-03 08:07:16 +02:00
b0ea0668c7 Merge pull request 'reservations' (#327) from reservations into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m5s
Reviewed-on: #327
2024-04-02 21:46:18 +02:00
4e1de0c886 Merge pull request 'show 'no reservations' if no reservations :-)' (#336) from reservations into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m57s
CI/CD Pipeline / deploy-staging (push) Successful in 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #336
2024-04-02 21:31:53 +02:00
244eb1be07 show 'no reservations' if no reservations :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-02 21:31:02 +02:00
06f9fcc427 Merge pull request 'reservations' (#335) from reservations into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m27s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #335
2024-04-02 21:12:14 +02:00
784deaf9f4 deployed :-)
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-04-02 21:11:31 +02:00
3b63cafa79 Merge branch 'staging' into reservations 2024-04-02 21:09:47 +02:00
0fe672c9da Merge branch 'main' into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-02 21:08:35 +02:00
37ab6e9132 reorder ergo entry 2024-04-02 21:07:58 +02:00
53afb4ee6f [TASK] style boat reservation
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 11m15s
2024-04-02 20:59:09 +02:00
1783527f39 Merge branch 'main' into reservations
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-02 11:19:51 +02:00
e7a679541b Merge pull request 'fix-notification-content' (#334) from fix-notification-content into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m0s
Reviewed-on: #334
2024-04-02 10:38:19 +02:00
32b2185e94 Merge pull request 'fix-notification-content' (#333) from fix-notification-content into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m6s
CI/CD Pipeline / deploy-staging (push) Successful in 5m8s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #333
2024-04-02 10:37:48 +02:00
c1d46a6e6b fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-02 10:37:15 +02:00
1c43d83bd4 use proper name :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-04-02 10:34:50 +02:00
6fb27d52d6 Merge branch 'main' into reservations
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-01 22:39:26 +02:00
092dba3f4b Merge pull request 'only-last-30-days-notifications' (#331) from only-last-30-days-notifications into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m17s
CI/CD Pipeline / deploy-staging (push) Successful in 5m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #331
2024-04-01 22:38:37 +02:00
6044aed46f Merge pull request 'only-last-30-days-notifications' (#332) from only-last-30-days-notifications into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m22s
Reviewed-on: #332
2024-03-31 13:09:21 +02:00
ef4e6f57d9 only show read notifications for last 30 days
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m36s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-31 13:07:40 +02:00
974b4aeb48 Merge branch 'main' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt 2024-03-31 13:07:06 +02:00
4466c9f018 Merge pull request 'hide reservations if there are none; clean code' (#330) from reservations into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m12s
CI/CD Pipeline / deploy-staging (push) Successful in 6m16s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #330
2024-03-30 09:17:59 +01:00
0ba2590bfd hide reservations if there are none; clean code
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-30 09:17:32 +01:00
cccf62bb53 Merge pull request 'reservations' (#326) from reservations into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m8s
CI/CD Pipeline / deploy-staging (push) Successful in 5m7s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #326
2024-03-30 01:50:54 +01:00
649169c192 reservations
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-30 01:36:37 +01:00
1caced26d6 Merge pull request 'proper log for registering guests' (#325) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m13s
Reviewed-on: #325
2024-03-29 21:46:58 +01:00
163c97b2f5 proper log for registering guests
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-29 21:46:15 +01:00
7a28f0360d Merge pull request 'allow planned_event role to add guests' (#324) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #324
2024-03-29 21:29:38 +01:00
a5ed2cb9e3 allow planned_event role to add guests
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 10m29s
2024-03-29 21:29:06 +01:00
35333324ed Merge pull request 'staging' (#323) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m4s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m18s
Reviewed-on: #323
2024-03-29 11:26:16 +01:00
2d36be07d2 Merge branch 'staging' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m10s
CI/CD Pipeline / deploy-staging (push) Successful in 7m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-29 11:25:34 +01:00
183f26e5c3 update boat reservation... 2024-03-29 11:25:25 +01:00
2452d31b9a Merge pull request '[BUGFIX] mobile menu fix styling' (#320) from bugfix-mobile-menu into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m47s
Reviewed-on: #320
2024-03-28 09:08:51 +01:00
4549043a08 Merge pull request 'bugfix-mobile-menu' (#319) from bugfix-mobile-menu into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m2s
CI/CD Pipeline / deploy-staging (push) Successful in 4m52s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #319
2024-03-28 09:08:36 +01:00
47986df47e [BUGFIX] mobile menu fix styling
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-28 09:05:32 +01:00
42a1579cd1 Merge pull request 'show new notifications for boatdamages; Fixes #310' (#318) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m4s
Reviewed-on: #318
2024-03-28 08:34:29 +01:00
a36fc300f0 show new notifications for boatdamages; Fixes #310
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Successful in 5m59s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-28 08:34:02 +01:00
c5af1e4cf8 Merge pull request 'show new notifications @ top' (#317) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 9m59s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #317
2024-03-28 08:11:37 +01:00
ab565f1369 show new notifications @ top
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
2024-03-28 08:10:26 +01:00
3e859ebc7b Merge pull request 'update deps' (#316) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m36s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m50s
Reviewed-on: #316
2024-03-27 14:55:25 +01:00
2b05828f6f update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 19m11s
CI/CD Pipeline / deploy-staging (push) Successful in 14m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-27 14:53:54 +01:00
df72ec9d8a Merge pull request 'only use membership_pdf when necessary' (#314) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m17s
Reviewed-on: #314
2024-03-26 12:35:19 +01:00
bf04ff780f only use membership_pdf when necessary
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m11s
CI/CD Pipeline / deploy-staging (push) Successful in 4m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-26 12:34:19 +01:00
64ca6caa3c Merge pull request 'add /planned to quick menu' (#313) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m49s
Reviewed-on: #313
2024-03-24 14:03:23 +01:00
9b70875c72 add /planned to quick menu
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Successful in 8m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-24 14:02:22 +01:00
5d55a10ad0 Merge pull request 'better wording for notification' (#312) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m33s
Reviewed-on: #312
2024-03-22 21:04:23 +01:00
272b6f3eb1 better wording for notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m27s
CI/CD Pipeline / deploy-staging (push) Successful in 4m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-22 21:03:43 +01:00
890f6cce3f Merge pull request 'staging' (#311) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #311
2024-03-22 20:50:29 +01:00
67eea1beb0 remove boat reservation
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m50s
2024-03-22 20:49:56 +01:00
abefd93be5 add db pic, created with sqleton 2024-03-22 20:48:04 +01:00
1e0096c44b Merge pull request '[BUGFIX] close div index html' (#307) from bugfix into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m31s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m31s
Reviewed-on: #307
2024-03-21 22:20:58 +01:00
c007ae6fb8 Merge pull request 'bugfix' (#306) from bugfix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Successful in 4m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #306
2024-03-21 22:20:50 +01:00
b9f11281e5 [BUGFIX] close div index html
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-21 21:58:27 +01:00
2e4a9a1168 Merge pull request 'merger :-)' (#305) from remove-merged-staging-diff into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m48s
Reviewed-on: #305
2024-03-21 21:04:30 +01:00
53ca2c24c1 Merge pull request 'merger :-)' (#304) from remove-merged-staging-diff into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m29s
CI/CD Pipeline / deploy-staging (push) Successful in 6m31s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #304
2024-03-21 21:04:14 +01:00
42e032e977 Merge pull request 'fix-ci' (#303) from fix-ci into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #303
2024-03-21 21:03:55 +01:00
ac587a1b1c Merge pull request 'fix ci' (#302) from fix-ci into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #302
2024-03-21 21:03:35 +01:00
905a22e7c0 fix ci
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-03-21 21:02:52 +01:00
15e3680a97 merger :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 20:55:47 +01:00
9eb91ee2d4 Merge pull request 'notification' (#301) from notification into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 7m55s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #301
2024-03-21 20:51:41 +01:00
266a3b978e Merge branch 'notification' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into notification
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 20:50:31 +01:00
de1b6b76c9 format with djlint 2024-03-21 20:50:25 +01:00
961cdbad09 [TASK] style notifications, add rowing icon and refactor menu 2024-03-21 20:50:25 +01:00
3416373b8f update wording 2024-03-21 20:50:25 +01:00
f2874a4c1b Merge branch 'staging' into notification
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 20:49:56 +01:00
a27e9612e4 Merge pull request 'notification' (#292) from notification into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #292
2024-03-21 20:48:20 +01:00
04b09983bc format with djlint
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 20:47:48 +01:00
7050d68293 [TASK] style notifications, add rowing icon and refactor menu
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 20:42:49 +01:00
d1067988c6 update wording
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-21 19:38:47 +01:00
257a682eb4 Merge branch 'main' into notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 22m8s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-21 00:27:11 +01:00
80cc614390 Merge pull request '...' (#300) from fix-ci into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m34s
Reviewed-on: #300
2024-03-21 00:26:54 +01:00
431accb20e Merge pull request 'fix-ci' (#299) from fix-ci into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Successful in 18m32s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #299
2024-03-21 00:26:35 +01:00
58db070cc0 ...
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 00:26:10 +01:00
31348a6a93 Merge branch 'main' into notification
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 00:18:18 +01:00
f50ea78e3f Merge pull request 'fix ci' (#298) from fix-ci into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #298
2024-03-21 00:17:44 +01:00
8792dc7cbf Merge pull request 'fix-ci' (#297) from fix-ci into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #297
2024-03-21 00:17:07 +01:00
59e5f48589 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-21 00:16:29 +01:00
4624dfaf17 Merge branch 'main' into notification
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 23:52:37 +01:00
5ddc302048 Merge pull request 'update deps' (#296) from update-deps into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 9m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #296
2024-03-20 23:51:59 +01:00
2d4b433144 Merge pull request 'update-deps' (#295) from update-deps into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 9m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #295
2024-03-20 23:51:39 +01:00
0d8040c00e update deps
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-20 23:51:00 +01:00
b920d65f1a Merge pull request 'dont log pdf content' (#294) from dont-log-pdf-content into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #294
2024-03-20 23:50:09 +01:00
9c277df2b7 Merge pull request 'dont log pdf content' (#293) from dont-log-pdf-content into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #293
2024-03-20 23:49:21 +01:00
591f9ea245 dont log pdf content
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 23:48:42 +01:00
bc35afb521 Merge pull request 'notification' (#291) from notification into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m32s
CI/CD Pipeline / deploy-staging (push) Successful in 4m25s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #291
2024-03-20 22:23:12 +01:00
6a6afe5e60 Merge branch 'staging' into notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 22:22:38 +01:00
63af74662f Merge branch 'main' into notification
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-03-20 22:21:12 +01:00
e3afe8c2ae Merge pull request 'push' (#290) from only-show-input-when-possible into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m7s
Reviewed-on: #290
2024-03-20 22:14:10 +01:00
c9270b2c54 Merge pull request 'only-show-input-when-possible' (#289) from only-show-input-when-possible into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #289
2024-03-20 22:13:48 +01:00
ac90dbedea push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-20 22:13:18 +01:00
0dcf941cd1 Merge pull request 'only accept pdf' (#288) from fix-ci into main
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m12s
Reviewed-on: #288
2024-03-20 21:40:54 +01:00
39306150bb Merge pull request 'only accept pdf' (#287) from fix-ci into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m3s
CI/CD Pipeline / deploy-staging (push) Successful in 5m41s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #287
2024-03-20 21:40:33 +01:00
868847f778 only accept pdf
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 21:40:08 +01:00
638c13bc53 Merge pull request 'fix-ci' (#286) from fix-ci into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #286
2024-03-20 21:27:44 +01:00
ffce336199 Merge pull request 'fix ci' (#285) from fix-ci into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m57s
Reviewed-on: #285
2024-03-20 21:17:54 +01:00
35900f3059 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-03-20 21:16:55 +01:00
9a1117a7c8 Merge pull request 'membership-pdf-new' (#284) from membership-pdf-new into staging
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #284
2024-03-20 21:05:22 +01:00
e48bc468cd Merge pull request 'add membership pdf' (#283) from membership-pdf-new into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 7m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #283
2024-03-20 21:04:42 +01:00
9fdc1f82bd add membership pdf
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-03-20 21:02:41 +01:00
9d14dae4a7 notification (#282)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m58s
CI/CD Pipeline / deploy-staging (push) Successful in 4m18s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #282
2024-03-20 16:19:12 +01:00
6959f71f96 show additional notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 16:09:55 +01:00
be50e65846 add notifications; fixes #127
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 15:56:34 +01:00
2ebfe7564a Merge branch 'staging' into notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 14:00:25 +01:00
fda2673f5a Merge pull request 'improve boathouse functionality, fixes #261' (#281) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m12s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m57s
Reviewed-on: #281
2024-03-20 13:59:30 +01:00
68a1153885 improve boathouse functionality, fixes #261
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m22s
CI/CD Pipeline / deploy-staging (push) Successful in 4m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 13:58:42 +01:00
7614cc8fae Merge pull request 're-wording' (#280) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 8m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #280
2024-03-20 13:41:28 +01:00
c1411b3a76 re-wording
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 8m8s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
2024-03-20 13:41:14 +01:00
6ad07f35f7 Merge pull request 'show total club km' (#279) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #279
2024-03-20 13:34:54 +01:00
5533106aca show total club km
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-20 13:34:21 +01:00
f61ffb60d8 Merge pull request 'don't count 'externe steuerkilometer' as guest kms' (#278) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m56s
Reviewed-on: #278
2024-03-20 09:16:21 +01:00
a17a08d018 create temporary boat reservation feature
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m13s
CI/CD Pipeline / deploy-staging (push) Successful in 4m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 09:15:58 +01:00
c9eecf0a29 create temporary boat reservation feature
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-20 09:14:26 +01:00
9c1bcbc5f5 don't count 'externe steuerkilometer' as guest kms
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m16s
CI/CD Pipeline / deploy-staging (push) Successful in 4m2s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 08:58:51 +01:00
d6b4f76fb5 Merge pull request 'fix ci' (#277) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m16s
Reviewed-on: #277
2024-03-20 00:48:47 +01:00
5cedbc078d fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m11s
CI/CD Pipeline / deploy-staging (push) Successful in 19m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 00:48:27 +01:00
a649912e78 Merge pull request 'push' (#276) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #276
2024-03-20 00:11:39 +01:00
0aa32654b0 push
Some checks failed
CI/CD Pipeline / test (push) Failing after 10m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-20 00:11:11 +01:00
a1126e0509 Merge pull request 'bit nicer logs' (#275) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m55s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m0s
Reviewed-on: #275
2024-03-19 10:00:12 +01:00
39a8a1563c bit nicer logs
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m54s
CI/CD Pipeline / deploy-staging (push) Successful in 19m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-19 09:59:46 +01:00
b075b8803b Merge pull request 'spacing' (#273) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 6m36s
Reviewed-on: #273
2024-03-17 21:09:19 +01:00
8645612718 spacing
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m41s
CI/CD Pipeline / deploy-staging (push) Successful in 6m19s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-17 21:08:55 +01:00
54058b0917 Merge pull request 'add more logs' (#270) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m59s
Reviewed-on: #270
2024-03-16 20:43:30 +01:00
c068713572 add more logs
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m15s
CI/CD Pipeline / deploy-staging (push) Successful in 4m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-16 20:42:41 +01:00
e228deb6cd Merge pull request 'spacing again' (#269) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m47s
Reviewed-on: #269
2024-03-16 19:47:39 +01:00
d1fa3e0336 board members can open and close trips for others
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m15s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-16 19:47:04 +01:00
4d634ce313 spacing again
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m25s
CI/CD Pipeline / deploy-staging (push) Successful in 4m41s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-16 10:56:37 +01:00
1a2f4c9920 Merge pull request 'fix whitespace' (#268) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m16s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m42s
Reviewed-on: #268
2024-03-16 09:56:28 +01:00
09a0354eee fix whitespace
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m35s
CI/CD Pipeline / deploy-staging (push) Successful in 5m0s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-16 09:55:58 +01:00
2ab164d5b9 Merge pull request 'a bit nicer mail sending layout' (#267) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m27s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m29s
Reviewed-on: #267
2024-03-15 15:30:28 +01:00
730559f2f4 a bit nicer mail sending layout
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m49s
CI/CD Pipeline / deploy-staging (push) Successful in 4m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-15 15:29:50 +01:00
ec9657c6e9 Merge pull request 'fix typo' (#266) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m49s
Reviewed-on: #266
2024-03-15 11:49:53 +01:00
2ab2472e66 fix typo
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Successful in 5m10s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-15 11:49:27 +01:00
a0183c1359 Merge pull request 'add mail for requesting fee' (#265) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #265
2024-03-15 11:42:35 +01:00
c9d10f81a9 add mail for requesting fee
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-15 11:41:03 +01:00
3b72bf279f Merge pull request 'show which schnupperant already paid' (#264) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m59s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m55s
Reviewed-on: #264
2024-03-15 10:52:58 +01:00
413d08f538 show which schnupperant already paid
Some checks failed
CI/CD Pipeline / test (push) Successful in 9m46s
CI/CD Pipeline / deploy-staging (push) Failing after 22m31s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-15 10:52:13 +01:00
11a96f4091 Merge pull request 'update docs' (#263) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m35s
Reviewed-on: #263
2024-03-15 10:06:06 +01:00
5e24f9ce04 update docs
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m47s
CI/CD Pipeline / deploy-staging (push) Successful in 4m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-15 09:54:36 +01:00
7958a9311d Merge pull request 'move to hetzner server' (#262) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m33s
Reviewed-on: #262
2024-03-15 09:35:36 +01:00
f70766e817 move to hetzner server
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m11s
CI/CD Pipeline / deploy-staging (push) Successful in 4m46s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-15 09:10:56 +01:00
da525d98cb Merge pull request 'staging' (#260) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m12s
Reviewed-on: #260
2024-03-09 18:17:15 +01:00
09e11dbb2b show two rowes of boats for the 3 most left 'aisles'
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m55s
CI/CD Pipeline / deploy-staging (push) Successful in 4m5s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-09 18:16:32 +01:00
21265e20cb Merge branch 'notification' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-09 17:22:39 +01:00
aef5748f5f add table 2024-03-09 17:21:15 +01:00
5af1860607 notifications 2024-03-09 17:20:38 +01:00
e94fc79580 deployed :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m53s
CI/CD Pipeline / deploy-staging (push) Successful in 4m5s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-09 17:02:50 +01:00
ee73509fc7 Merge pull request 'boatshouse' (#258) from boatshouse into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m53s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m17s
Reviewed-on: #258
2024-03-09 17:00:11 +01:00
2445e82c69 Merge pull request 'boatshouse' (#259) from boatshouse into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #259
2024-03-09 16:59:56 +01:00
ad2f2241aa show messages in kiosk mode
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-09 16:56:32 +01:00
f14177d497 MB: styling of boathouse
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-09 16:49:04 +01:00
1e96c113a9 single location for flash message
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-09 16:15:47 +01:00
75be5d3ca2 Merge pull request 'boatshouse' (#257) from boatshouse into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m12s
CI/CD Pipeline / deploy-staging (push) Successful in 4m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #257
2024-03-08 22:23:42 +01:00
5da1900ae8 Merge pull request 'no fee for 'ehrenmitglieder' (#256) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m5s
Reviewed-on: #256
2024-03-08 13:58:42 +01:00
a61f7cebbb no fee for 'ehrenmitglieder
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m53s
CI/CD Pipeline / deploy-staging (push) Successful in 4m7s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-08 13:57:45 +01:00
69edb63ddd styling
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-08 13:46:00 +01:00
3deb1e40fc boatshouse functionality, fixes #183
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-08 13:13:20 +01:00
92a1a8278d Merge pull request 'update filter' (#255) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m33s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m58s
Reviewed-on: #255
2024-03-08 10:17:13 +01:00
7e6b577315 update filter
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m51s
CI/CD Pipeline / deploy-staging (push) Successful in 4m0s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-08 10:16:36 +01:00
754f746094 Merge pull request 'add schnupper management' (#254) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m32s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m4s
Reviewed-on: #254
2024-03-06 15:55:49 +01:00
edb42717bc add schnupper management
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m59s
CI/CD Pipeline / deploy-staging (push) Successful in 4m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-06 15:55:13 +01:00
ef6fc3a349 Merge pull request 'clippy' (#253) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m59s
Reviewed-on: #253
2024-03-06 13:28:53 +01:00
a3ce96d4bf clippy
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m47s
CI/CD Pipeline / deploy-staging (push) Successful in 4m10s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-06 13:27:03 +01:00
25667af9c5 Merge pull request 'sort boats by name' (#252) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m3s
Reviewed-on: #252
2024-03-06 08:16:11 +01:00
4af1f48ebf sort boats by name
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m27s
CI/CD Pipeline / deploy-staging (push) Successful in 4m11s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-06 08:15:18 +01:00
9f6d7bf5d7 Merge pull request 'fix ci' (#251) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m59s
Reviewed-on: #251
2024-03-05 15:02:12 +01:00
8854ef36f3 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m20s
CI/CD Pipeline / deploy-staging (push) Successful in 4m1s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 15:01:44 +01:00
3b9d743603 Merge pull request 'fix ci' (#250) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #250
2024-03-05 14:29:58 +01:00
ef4323b275 fix ci
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 14:29:27 +01:00
292e944783 Merge pull request 'fix ci' (#249) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #249
2024-03-05 14:13:18 +01:00
1a662acf3b fix ci
Some checks failed
CI/CD Pipeline / test (push) Failing after 14m37s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 14:12:57 +01:00
8c8b9b7aca Merge pull request 'staging' (#248) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #248
2024-03-05 13:45:55 +01:00
94b622a5e7 update filter
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
2024-03-05 13:45:19 +01:00
aadc1b315e staging (#246)
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m1s
Reviewed-on: #246
2024-03-05 09:46:53 +01:00
6f5fcd59ea fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m30s
CI/CD Pipeline / deploy-staging (push) Successful in 4m0s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 09:45:54 +01:00
7a6bea3c46 fix ci (#245)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #245
2024-03-05 09:35:08 +01:00
1a482db9e2 fix ci
Some checks failed
CI/CD Pipeline / test (push) Failing after 8m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 09:34:50 +01:00
10a0e82392 fix ci
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-03-05 09:33:18 +01:00
6c2ff716e1 Merge pull request 'staging' (#244) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #244
2024-03-05 09:19:50 +01:00
8b0cbe23d1 show year selector to admins for logbook
Some checks failed
CI/CD Pipeline / test (push) Failing after 8m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 09:19:15 +01:00
58f8cb14b8 allow admins to show logbook for any year
Some checks failed
CI/CD Pipeline / test (push) Failing after 10m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 08:59:44 +01:00
814e80864d Merge pull request 'fix wording' (#243) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m5s
Reviewed-on: #243
2024-03-05 08:31:41 +01:00
ae98a4278d fix wording
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Successful in 4m9s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 08:31:03 +01:00
f3b97e0e49 Merge pull request 'fix ci?' (#242) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m6s
Reviewed-on: #242
2024-03-05 07:51:12 +01:00
4b59cdfc8f fix ci?
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m39s
CI/CD Pipeline / deploy-staging (push) Successful in 4m11s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 07:50:43 +01:00
bb6ba7730e Merge pull request 'remove timeout again' (#241) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #241
2024-03-05 00:58:20 +01:00
16e7c2379a remove timeout again
Some checks failed
CI/CD Pipeline / test (push) Failing after 13m53s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-05 00:56:40 +01:00
359e20e948 Merge pull request 'wait for webserver to respond with 2xx, fix failing first (flaky) test' (#239) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #239
2024-03-05 00:53:35 +01:00
737af80c39 wait for webserver to respond with 2xx, fix failing first (flaky) test
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-05 00:53:09 +01:00
b3779bfefd Merge pull request 'faster tests' (#238) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #238
2024-03-05 00:46:42 +01:00
212ef5faa7 faster tests
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-05 00:46:19 +01:00
5cbdb2ae63 Merge pull request 'remove 1 minute timeout + use local timezone where appropriate and fix tests between 0 and 1 o'clock' (#237) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #237
2024-03-05 00:17:56 +01:00
5078c5f341 use local timezone where appropriate and fix tests between 0 and 1 o'clock
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
2024-03-05 00:17:02 +01:00
38d4899bf4 Merge pull request 'staging' (#236) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m10s
Reviewed-on: #236
2024-03-04 23:12:05 +01:00
007b87e8ad add scheckbook functionality, Fixes #184 (#235)
All checks were successful
CI/CD Pipeline / test (push) Successful in 19m38s
CI/CD Pipeline / deploy-staging (push) Successful in 4m17s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #235
2024-03-04 23:11:44 +01:00
c813e2b3da scheckbuch people don't get error
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m46s
CI/CD Pipeline / deploy-staging (push) Successful in 4m9s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 22:04:22 +01:00
eed7718a3f Merge pull request 'alphabetically order stat if same ranking' (#233) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m58s
Reviewed-on: #233
2024-03-04 17:12:40 +01:00
4a81d2d624 alphabetically order stat if same ranking
All checks were successful
CI/CD Pipeline / test (push) Successful in 20m38s
CI/CD Pipeline / deploy-staging (push) Successful in 17m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 17:12:16 +01:00
2bff90b025 Merge pull request 'staging' (#232) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #232
2024-03-04 17:01:53 +01:00
842cc00812 Merge branch 'staging' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 17:01:33 +01:00
ff6d8adefd add new functionality to menu 2024-03-04 17:01:25 +01:00
2490428781 Merge pull request 'Create admin function to use copy-pasted users from ekey, return the ones which don't match to "Donau Linz" rowt user, Fixes #204 (#230)' (#231) from staging into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #231
2024-03-04 17:00:06 +01:00
05b7b7fbbf Create admin function to use copy-pasted users from ekey, return the ones which don't match to "Donau Linz" rowt user, Fixes #204 (#230)
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #230
2024-03-04 16:59:44 +01:00
a269efedca Merge pull request 'show user icon in all vorstand users' (#229) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #229
2024-03-04 16:38:50 +01:00
8edbf15235 show user icon in all vorstand users
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 15m40s
2024-03-04 16:37:53 +01:00
e7168d308f Merge pull request 'fix link' (#228) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m46s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m2s
Reviewed-on: #228
2024-03-04 15:57:34 +01:00
b6a318df85 fix link
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m26s
CI/CD Pipeline / deploy-staging (push) Successful in 4m1s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 15:57:06 +01:00
11188bacbe Merge pull request 'reformat files' (#227) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #227
2024-03-04 15:51:50 +01:00
a1b5c26518 Merge branch 'main' into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m41s
CI/CD Pipeline / deploy-staging (push) Successful in 4m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 15:18:56 +01:00
dfa0eaeb4f fix ci, more output @ ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 18m51s
CI/CD Pipeline / deploy-staging (push) Successful in 4m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 14:55:11 +01:00
4eeb2162d8 fix spacing issues
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 14:28:19 +01:00
df9f1c398b fix spacing issues
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 14:26:42 +01:00
62ff5f0c2e fix spacing issues 2024-03-04 14:25:14 +01:00
ee17bc2ab3 fix tests
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 14:08:46 +01:00
eb483d9959 also verify logbook entry successfully created
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 14:04:13 +01:00
c98170636b show proper amount of guests
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 13:35:13 +01:00
442257df01 reformat files
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 13:28:42 +01:00
ec5a69f3e6 Merge branch 'notification' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-04 10:03:59 +01:00
09fffa1830 add table 2024-03-04 10:03:53 +01:00
28acee3085 notifications 2024-03-04 10:03:53 +01:00
7ce535f500 Merge pull request 'staging' (#226) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 12m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m59s
Reviewed-on: #226
2024-03-04 10:03:14 +01:00
e338c78d04 Merge pull request 'allow vorstand to see member details, Fixes #199' (#225) from show-members-vorstand into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m22s
CI/CD Pipeline / deploy-staging (push) Successful in 4m4s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #225
2024-03-04 09:56:29 +01:00
0de21e9abb add tests and fix npm build, make frontend tests work @ mobile view
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #224
2024-03-04 09:55:39 +01:00
9c3ae7434e allow vorstand to see member details, Fixes #199
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-03-04 09:19:04 +01:00
996fcdc14f Merge pull request 'Allow 'Rennjugend' to use all boats in ottensheim, Fixes #200' (#222) from ottensheim-boats into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #222
2024-03-03 20:06:29 +01:00
5781937bee show ottensheim boats to rennjugend
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-03 20:03:20 +01:00
d1296ec40a show ottensheim boats to rennjugend
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-03 18:08:15 +01:00
29284b30a6 Merge pull request 'update deps and fix ci' (#219) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m32s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m57s
Reviewed-on: #219
2024-03-01 09:09:34 +01:00
cc7ba59407 update deps and fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 22m30s
CI/CD Pipeline / deploy-staging (push) Successful in 17m49s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-03-01 09:08:50 +01:00
966254807c Merge pull request 'allow sending mails w/o attachments :-)' (#218) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 12m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #218
2024-03-01 08:41:12 +01:00
770e321bed allow sending mails w/o attachments :-)
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
2024-03-01 08:40:02 +01:00
4f6dacb907 Merge pull request 'use old lock file to make ci work' (#216) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m19s
Reviewed-on: #216
2024-02-21 15:39:22 +01:00
f7a7ab8733 use old lock file to make ci work
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m24s
CI/CD Pipeline / deploy-staging (push) Successful in 3m29s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-21 15:35:12 +01:00
4ddac52a93 Merge pull request 'increase timeout for frontend tests' (#215) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 16m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #215
2024-02-21 15:08:31 +01:00
65e425fed7 increase timeout for frontend tests
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
2024-02-21 15:06:35 +01:00
cdd57eb147 Merge pull request 'code cleanup' (#214) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #214
2024-02-21 14:47:54 +01:00
6ed28994c6 code cleanup
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-21 14:46:17 +01:00
4a0026a9ee Merge pull request 'staging' (#213) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m5s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #213
2024-02-21 13:58:29 +01:00
e9bee963fe update deps
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-21 13:58:03 +01:00
75d7396b96 push
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m32s
CI/CD Pipeline / deploy-staging (push) Successful in 3m36s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-17 12:48:25 +01:00
9746a2114c push
All checks were successful
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-17 12:47:06 +01:00
ce2e4e5219 Merge pull request 'dont expect money from deleted users :-)' (#212) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m23s
Reviewed-on: #212
2024-02-16 13:14:49 +01:00
6e0c594c84 dont expect money from deleted users :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Successful in 3m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-16 13:13:57 +01:00
b2fd52e4a5 Merge pull request 'also remove js function for (unnecessary) filtering of months' (#211) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m44s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m25s
Reviewed-on: #211
2024-02-16 10:48:05 +01:00
fe2ca0f33a also remove js function for (unnecessary) filtering of months
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m4s
CI/CD Pipeline / deploy-staging (push) Successful in 3m30s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-16 10:47:28 +01:00
14fbfd200b Merge pull request 'remove useless button' (#210) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m12s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m33s
Reviewed-on: #210
2024-02-16 08:05:57 +01:00
043d042dcc remove useless button
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m15s
CI/CD Pipeline / deploy-staging (push) Successful in 3m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-16 08:05:07 +01:00
ebbd9215ea Merge pull request 'staging' (#209) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m31s
Reviewed-on: #209
2024-02-15 22:30:02 +01:00
d0b7f9c76c dont sent deleted users a mail
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m55s
CI/CD Pipeline / deploy-staging (push) Successful in 3m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-15 22:29:08 +01:00
58b498b9de use proper encoding
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-02-15 22:21:40 +01:00
23f5e3ca4a add functionality to send attachmets, Fixes #113
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Successful in 3m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-15 21:52:00 +01:00
7ff9978587 switch to new ci image
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m11s
CI/CD Pipeline / deploy-staging (push) Successful in 18m55s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-15 12:01:28 +01:00
f58d0d010e Merge pull request 'upgrade major deps' (#208) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m59s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m20s
Reviewed-on: #208
2024-02-13 23:25:55 +01:00
da9d2febf1 upgrade major deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 17m59s
CI/CD Pipeline / deploy-staging (push) Successful in 11m55s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-13 23:25:08 +01:00
86f2f3fb0c Merge pull request 'update deps' (#207) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m1s
Reviewed-on: #207
2024-02-13 14:20:19 +01:00
23cfc8aa1f update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 23m32s
CI/CD Pipeline / deploy-staging (push) Successful in 19m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-13 14:19:46 +01:00
d731f97169 Merge pull request 'only show donauLinz user in stats, otherwise count as guest' (#206) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m57s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m31s
Reviewed-on: #206
2024-02-12 20:10:03 +01:00
0231d2bd21 only show donauLinz user in stats, otherwise count as guest
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m8s
CI/CD Pipeline / deploy-staging (push) Successful in 3m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-12 20:09:30 +01:00
808cdbaddc Merge pull request 'add remark about bank synchornistaion' (#205) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m51s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m35s
Reviewed-on: #205
2024-02-12 19:08:28 +01:00
5b5eb2e831 add remark about bank synchornistaion
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m12s
CI/CD Pipeline / deploy-staging (push) Successful in 3m45s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-12 19:07:45 +01:00
cef1d91ee3 Merge pull request 'better wording' (#203) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m28s
Reviewed-on: #203
2024-02-11 16:05:37 +01:00
8c43ab3331 better wording
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m24s
CI/CD Pipeline / deploy-staging (push) Successful in 3m48s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-11 16:04:47 +01:00
08d935e6a5 Merge pull request 'update list of filtered log in attempts' (#201) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m50s
Reviewed-on: #201
2024-02-07 12:37:15 +01:00
9c8bb59ad7 update list of filtered log in attempts
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m59s
CI/CD Pipeline / deploy-staging (push) Successful in 3m41s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-07 12:36:27 +01:00
49b810d200 Merge pull request 'staging' (#198) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m39s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m42s
Reviewed-on: #198
2024-02-06 16:42:55 +01:00
664bb62733 fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m48s
CI/CD Pipeline / deploy-staging (push) Successful in 3m52s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-06 16:12:10 +01:00
c1bed58fbb no sudo for deployment...
Some checks failed
CI/CD Pipeline / test (push) Successful in 8m53s
CI/CD Pipeline / deploy-staging (push) Failing after 3m49s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-06 16:03:11 +01:00
ccd9aad51f switch deplyment
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-02-06 16:01:34 +01:00
123efa1c8c Merge pull request 'only send mails to proper addresses' (#197) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #197
2024-02-05 22:22:08 +01:00
19afa34c13 only send mails to proper addresses
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m2s
CI/CD Pipeline / deploy-staging (push) Successful in 4m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-02-04 10:34:58 +01:00
ded9ed984b Merge pull request 'increase whitelist of not logging certain (wp logins)' (#196) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m24s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m11s
Reviewed-on: #196
2024-01-31 10:24:14 +01:00
59f0ee1429 increase whitelist of not logging certain (wp logins)
All checks were successful
CI/CD Pipeline / test (push) Successful in 23m44s
CI/CD Pipeline / deploy-staging (push) Successful in 20m26s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-31 10:23:33 +01:00
5c61998ff7 Merge pull request 'update deps' (#195) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m35s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m3s
Reviewed-on: #195
2024-01-26 13:26:58 +01:00
6f94bb1186 update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 25m59s
CI/CD Pipeline / deploy-staging (push) Successful in 19m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-26 13:26:26 +01:00
9181b79166 Merge pull request 'update tests to new name field in update' (#194) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m13s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m4s
Reviewed-on: #194
2024-01-24 23:05:04 +01:00
1913bf8b22 update tests to new name field in update
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m27s
CI/CD Pipeline / deploy-staging (push) Successful in 5m14s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-24 23:04:35 +01:00
24667e56a4 Merge pull request 'allow changing title of planned-event; fixes #52' (#193) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 3m29s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #193
2024-01-24 22:48:18 +01:00
3462ac5963 allow changing title of planned-event; fixes #52
Some checks failed
CI/CD Pipeline / test (push) Failing after 10m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-24 22:47:53 +01:00
29486ac7a2 Merge pull request 'supply all variables for planned_event roles' (#191) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m8s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m31s
Reviewed-on: #191
2024-01-24 18:18:51 +01:00
9ae5963a0a supply all variables for planned_event roles
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m12s
CI/CD Pipeline / deploy-staging (push) Successful in 5m29s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-24 18:18:26 +01:00
20e2be114a Merge pull request 'fix edit/delete trip' (#190) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #190
2024-01-24 18:12:53 +01:00
99c615c400 fix edit/delete trip
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-24 18:12:22 +01:00
4ecdc8968f Merge pull request 'don't spam logs with (unsuccessful) wordpress login attempts' (#189) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m6s
Reviewed-on: #189
2024-01-24 13:10:02 +01:00
a6faa128ec don't spam logs with (unsuccessful) wordpress login attempts
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m8s
CI/CD Pipeline / deploy-staging (push) Successful in 5m7s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-24 13:09:12 +01:00
7f564100b1 Merge pull request 'add has-paid filter' (#181) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m1s
Reviewed-on: #181
2024-01-22 22:59:03 +01:00
6ad3b8f741 add has-paid filter
All checks were successful
CI/CD Pipeline / test (push) Successful in 26m35s
CI/CD Pipeline / deploy-staging (push) Successful in 19m55s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-22 22:57:42 +01:00
353b413d9f Merge pull request 'update deps' (#180) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #180
2024-01-22 22:39:17 +01:00
083ddeadc1 update deps
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-22 22:34:54 +01:00
2e3be062c6 Merge pull request 'staging' (#178) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m40s
Reviewed-on: #178
2024-01-22 22:24:24 +01:00
6d61d1f8bc Merge branch 'main' into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m37s
CI/CD Pipeline / deploy-staging (push) Successful in 4m41s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-22 22:08:39 +01:00
b7499bd6cb clean code; if logged out: save url where user tried to go to, go there once logged in, Fixes #179
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-22 22:08:05 +01:00
60b9a4dbba adapt unit tests to account for new redirect
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m16s
CI/CD Pipeline / deploy-staging (push) Successful in 4m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-22 20:55:05 +01:00
a1a5e2ad89 move managing events to own role
Some checks failed
CI/CD Pipeline / test (push) Failing after 38m58s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-22 19:27:22 +01:00
eda072c713 enable changing paid state
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m15s
CI/CD Pipeline / deploy-staging (push) Successful in 4m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-22 19:05:18 +01:00
4a06d519bf Merge pull request 'staging' (#177) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m48s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m15s
Reviewed-on: #177
2024-01-21 16:10:32 +01:00
ac5ecbafec Merge branch 'staging' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m54s
CI/CD Pipeline / deploy-staging (push) Successful in 4m33s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-21 16:09:53 +01:00
c54efdeec4 add spacing 2024-01-21 16:09:43 +01:00
491b2cac82 Merge pull request 'main' (#176) from main into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #176
2024-01-21 16:08:47 +01:00
5f31c565a3 Merge pull request 'trim mails' (#175) from staging into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #175
2024-01-21 16:08:10 +01:00
6a22811ad9 trim mails
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-21 16:07:11 +01:00
7c7163f541 Merge pull request 'staging' (#174) from staging into main
Some checks failed
CI/CD Pipeline / test (push) Failing after 2m3s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #174
2024-01-21 16:01:22 +01:00
f4cb051b84 fix mail error
Some checks failed
CI/CD Pipeline / test (push) Has started running
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-21 16:00:41 +01:00
2032b7e0db Merge pull request 'main' (#173) from main into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m26s
CI/CD Pipeline / deploy-staging (push) Successful in 4m2s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #173
2024-01-19 11:58:41 +01:00
83c7b45139 Merge pull request 'staging' (#172) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m2s
Reviewed-on: #172
2024-01-19 11:58:05 +01:00
7f7259a3e1 improve texts
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-19 11:56:49 +01:00
99e3aa22a2 fix errors
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m7s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
2024-01-19 11:47:28 +01:00
0ed740ec47 create fee reminder mail
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m10s
CI/CD Pipeline / deploy-staging (push) Successful in 4m7s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 11:19:24 +01:00
578e3df9e9 use text instead of creditor id in qr code, more clear that fee is for full family
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Successful in 4m5s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 10:22:13 +01:00
3a8650028d add qr code for payment
Some checks failed
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 9m11s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
2024-01-19 10:10:23 +01:00
c036cda593 Merge pull request 'remove wrong header' (#171) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 3m59s
Reviewed-on: #171
2024-01-19 08:48:29 +01:00
f1ba331fdf remove wrong header
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m2s
CI/CD Pipeline / deploy-staging (push) Successful in 4m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 08:47:49 +01:00
14a5952e14 Merge pull request 'group families in fee calc' (#170) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m22s
Reviewed-on: #170
2024-01-19 08:02:59 +01:00
e498b4be3b group families in fee calc
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m35s
CI/CD Pipeline / deploy-staging (push) Successful in 4m11s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 08:02:09 +01:00
ae8887c72d Merge pull request 'staging' (#169) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m20s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 4m19s
Reviewed-on: #169
2024-01-19 07:43:05 +01:00
519cd1985d Merge pull request 'final' (#168) from final into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Successful in 4m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #168
2024-01-19 01:00:07 +01:00
3df6791b6b fix ci
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m11s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 00:50:30 +01:00
cb892e1c0c remove unnecessary field
Some checks failed
CI/CD Pipeline / test (push) Failing after 2m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-19 00:46:51 +01:00
b893989dce handle supporing fees
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-19 00:44:53 +01:00
267becfbce progress @fees, next step, deply+enter families/student/pupil/...
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-19 00:39:15 +01:00
ff795ce66c start with fee calc
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m40s
CI/CD Pipeline / deploy-staging (push) Successful in 4m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-18 22:02:44 +01:00
474db1232d start working on calculating member fees
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m54s
CI/CD Pipeline / deploy-staging (push) Successful in 4m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-18 21:36:38 +01:00
dc794bde37 try
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m48s
CI/CD Pipeline / deploy-staging (push) Successful in 4m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-18 16:55:51 +01:00
85124cd699 Merge pull request 'add family' (#167) from family into staging
Some checks failed
CI/CD Pipeline / test (push) Successful in 8m44s
CI/CD Pipeline / deploy-staging (push) Failing after 4m17s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #167
2024-01-18 16:38:33 +01:00
07c76f4e64 add family
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-18 16:37:54 +01:00
357ee21533 add notes
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m54s
CI/CD Pipeline / deploy-staging (push) Successful in 4m21s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-18 15:28:51 +01:00
66365c4a68 fix typo
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m47s
CI/CD Pipeline / deploy-staging (push) Successful in 4m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-18 09:32:25 +01:00
3cd4807604 Delete package-lock.json
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m47s
CI/CD Pipeline / deploy-staging (push) Successful in 4m27s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 23:49:28 +01:00
e9dfce5c95 Delete shame.txt
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-01-17 23:48:43 +01:00
6de5d70b64 Delete update.sh
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-01-17 23:48:26 +01:00
1c21d3ad65 Merge pull request 'own-dockerfile' (#166) from own-dockerfile into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #166
2024-01-17 23:43:24 +01:00
fd99bc6f66 push
All checks were successful
CI/CD Pipeline / test (push) Successful in 22m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 23:42:07 +01:00
c5673559d0 try 2024-01-17 23:41:08 +01:00
54f9dc22e0 try 2024-01-17 23:40:49 +01:00
9267b4dbc8 try 2024-01-17 23:40:48 +01:00
6e8947a928 try 2024-01-17 23:39:57 +01:00
0ab121df8e try 2024-01-17 23:39:55 +01:00
7c7877d275 try 2024-01-17 23:39:35 +01:00
1e02b2f5bb try 2024-01-17 23:39:22 +01:00
ca11e72d00 try 2024-01-17 23:39:22 +01:00
5631b0551c try 2024-01-17 23:39:22 +01:00
97154ef4b9 try 2024-01-17 23:39:20 +01:00
e5311b4fab try 2024-01-17 23:38:06 +01:00
76a1dbccbf try 2024-01-17 23:36:18 +01:00
03947001d5 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 23:34:39 +01:00
95fb07f1e9 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 23:23:58 +01:00
22ee941ce0 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 23:08:28 +01:00
f1f17cdb44 optimize ci build: use own dockerfile for dependencies + cache for builds (#164)
Some checks failed
CI/CD Pipeline / test (push) Successful in 8m1s
CI/CD Pipeline / deploy-staging (push) Failing after 16m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #164
2024-01-17 22:56:33 +01:00
6bee538b55 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 22:52:31 +01:00
88d533c838 try
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 22:43:54 +01:00
57c1e13c14 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 21:22:57 +01:00
ba132a5735 try
All checks were successful
CI/CD Pipeline / test (push) Successful in 27m10s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 20:35:06 +01:00
0794671707 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 20:14:55 +01:00
d0adee74da try
All checks were successful
CI/CD Pipeline / test (push) Successful in 21m36s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 19:52:54 +01:00
5fec65e38f try 2024-01-17 19:50:32 +01:00
0b7711ed91 try 2024-01-17 19:49:46 +01:00
7be76f3ce8 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 19:45:21 +01:00
865c55cd18 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-01-17 19:28:13 +01:00
b258a5ac6e try
All checks were successful
CI/CD Pipeline / test (push) Successful in 19m43s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 19:07:37 +01:00
62171d72fe try 2024-01-17 19:06:12 +01:00
1f734b75a0 try
Some checks failed
CI/CD Pipeline / test-backend (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test-frontend (push) Has been cancelled
2024-01-17 18:35:29 +01:00
4af887c2b4 try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 2s
CI/CD Pipeline / test-backend (push) Failing after 1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 18:32:18 +01:00
cf1cd0c126 explain dockerfile
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 1s
CI/CD Pipeline / test-backend (push) Failing after 1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 18:21:44 +01:00
c6da8b3db1 try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 1s
CI/CD Pipeline / test-backend (push) Failing after 0s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 18:09:29 +01:00
77d9e2ea9f try faster build
All checks were successful
CI/CD Pipeline / test-frontend (push) Successful in 18m47s
CI/CD Pipeline / test-backend (push) Successful in 14m53s
CI/CD Pipeline / deploy-staging (push) Successful in 18m32s
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-17 08:23:46 +01:00
55b97eaf1f Merge pull request 'Include frontend tests, update readme, fix bugs' (#163) from test-playwright into staging
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 21m6s
CI/CD Pipeline / test-backend (push) Successful in 15m44s
CI/CD Pipeline / deploy-staging (push) Successful in 18m50s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #163
2024-01-16 23:44:03 +01:00
b7c2dff8eb try
Some checks failed
CI/CD Pipeline / test-backend (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test-frontend (push) Has been cancelled
2024-01-16 23:43:36 +01:00
80846e6986 try
Some checks failed
CI/CD Pipeline / test-backend (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Has been cancelled
2024-01-16 23:22:53 +01:00
beec5ba6c6 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 25m21s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 22:52:31 +01:00
c372051561 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 13m30s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 22:29:57 +01:00
7e1a0a2159 try
All checks were successful
CI/CD Pipeline / test-frontend (push) Successful in 17m9s
CI/CD Pipeline / test-backend (push) Successful in 15m25s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-16 20:24:02 +01:00
6a1bde55a2 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 17m22s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 20:04:34 +01:00
2a025d7519 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 2m21s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 20:00:41 +01:00
bc1916ceab try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 2m21s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 19:54:24 +01:00
7aef741c6f try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 2m22s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 19:48:41 +01:00
64bb15cc8b try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 2m23s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 19:40:20 +01:00
442453bef3 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 17m13s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 19:17:17 +01:00
b633a4bfee try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 16m59s
CI/CD Pipeline / test-backend (push) Successful in 15m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-16 16:55:47 +01:00
875799c3d7 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 11m35s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 16:42:57 +01:00
98d8b512ee try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 6m53s
CI/CD Pipeline / test-backend (push) Successful in 15m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-16 16:16:56 +01:00
ae673657b0 try
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test-frontend (push) Failing after 2m22s
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 16:13:41 +01:00
c1493ad914 try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 2m22s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 16:10:13 +01:00
4ab1a36fcd try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 2s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test-backend (push) Has been cancelled
2024-01-16 16:09:45 +01:00
bd650f738c try
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 2s
CI/CD Pipeline / test-backend (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-16 16:09:09 +01:00
537458b58e try 2024-01-16 16:07:40 +01:00
ce3562f079 add playwright tests
Some checks failed
CI/CD Pipeline / test-frontend (push) Failing after 4s
CI/CD Pipeline / test-backend (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-16 16:00:02 +01:00
2f26ce7e31 Merge pull request 'add mail' (#160) from add-rss into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m23s
CI/CD Pipeline / deploy-staging (push) Successful in 19m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #160
2024-01-10 15:52:54 +01:00
bbfc94d54b add mail
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-10 15:52:27 +01:00
0262f721e9 Merge pull request 'fix ci' (#159) from fix-redirects into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #159
2024-01-10 15:50:17 +01:00
5788d3d9f4 fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-10 15:49:57 +01:00
30105e7bbd Merge pull request 'fix ci' (#158) from fix-redirects into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #158
2024-01-10 15:11:56 +01:00
a95ff3657f fix ci
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-10 15:11:35 +01:00
f5bd470dac Merge pull request 'fix redirect' (#157) from fix-redirects into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #157
2024-01-10 15:10:48 +01:00
87e4b8fbb4 fix redirect
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-10 15:10:05 +01:00
5cc8f1ff48 Merge pull request 'limit-users-to-proper-roles' (#156) from limit-users-to-proper-roles into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #156
2024-01-10 14:55:41 +01:00
a4b8bf1e3f improve code w/ clippy
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-10 14:24:58 +01:00
f4cdd0ae28 add table
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-10 14:20:30 +01:00
fc8529c20b Merge branch 'notification' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into notification
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-01-10 14:14:13 +01:00
ab64583efc notifications 2024-01-10 14:14:06 +01:00
3e2e058bcc limit users to proper role, Fixes #135
Some checks are pending
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Successful in 15m47s
2024-01-10 14:08:15 +01:00
c9e163c92c Merge pull request 'update-deps' (#155) from update-deps into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 19m20s
Reviewed-on: #155
2024-01-09 16:15:59 +01:00
041e83b425 Merge pull request 'update deps' (#154) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 16m6s
CI/CD Pipeline / deploy-staging (push) Successful in 19m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #154
2024-01-09 16:15:36 +01:00
33a5d75b63 update deps
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-09 16:15:02 +01:00
882665cbd9 Merge pull request 'remove-external-cox' (#153) from remove-external-cox into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m58s
Reviewed-on: #153
2024-01-05 22:24:27 +01:00
4d79d232cf Merge pull request 'remove external cox from stat' (#152) from remove-external-cox into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m15s
CI/CD Pipeline / deploy-staging (push) Successful in 17m58s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #152
2024-01-05 22:24:06 +01:00
9cbbdb38c2 remove external cox from stat
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-05 22:23:36 +01:00
17119999c9 Merge pull request 'hide external boat in stats' (#151) from hide-external-boat-in-stats into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m59s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m46s
Reviewed-on: #151
2024-01-05 20:43:11 +01:00
c199c3241e Merge pull request 'hide external boat in stats' (#150) from hide-external-boat-in-stats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m22s
CI/CD Pipeline / deploy-staging (push) Successful in 17m54s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #150
2024-01-05 20:42:48 +01:00
b50471d339 hide external boat in stats
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-05 20:42:20 +01:00
5cfaf44a79 Merge pull request 'fix-stat' (#149) from fix-stat into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #149
2024-01-05 20:39:02 +01:00
f0755a44e3 Merge pull request 'fix stat' (#148) from fix-stat into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #148
2024-01-05 20:38:24 +01:00
1c020da84f fix stat
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-05 20:37:40 +01:00
251c83eb30 Merge pull request 'show guests km' (#147) from show-guests-km into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #147
2024-01-05 20:09:59 +01:00
323f6da201 Merge pull request 'show guests km' (#146) from show-guests-km into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #146
2024-01-05 20:08:47 +01:00
0aeec175ac show guests km
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-05 20:08:13 +01:00
2e91925b83 Merge pull request 'fix tests, as we now show all boats in kiosk mode' (#143) from fix-ci into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m37s
Reviewed-on: #143
2024-01-04 20:48:11 +01:00
248ab95ae9 Merge pull request 'fix tests, as we now show all boats in kiosk mode' (#142) from fix-ci into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m27s
CI/CD Pipeline / deploy-staging (push) Successful in 17m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #142
2024-01-04 20:47:47 +01:00
1cfbb97034 fix tests, as we now show all boats in kiosk mode
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-04 20:46:49 +01:00
2726db433e Merge pull request 'allow to add logbook entries up to a week late; show all boats in boatshouse; don't show guests for external boats' (#141) from fixes into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #141
2024-01-04 20:44:26 +01:00
571e80085f Merge pull request 'allow to add logbook entries up to a week late; show all boats in boatshouse; don't show guests for external boats' (#140) from fixes into staging
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #140
2024-01-04 20:44:06 +01:00
bada4deedd allow to add logbook entries up to a week late; show all boats in boatshouse; don't show guests for external boats
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-04 20:43:38 +01:00
b73ae788e6 Merge pull request 'fix weird iphone issue' (#137) from fix-weird-iphone-issue into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m22s
CI/CD Pipeline / deploy-staging (push) Successful in 17m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #137
2024-01-03 10:00:10 +01:00
86075b72c6 Merge pull request 'fix weird iphone issue' (#136) from fix-weird-iphone-issue into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m40s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 18m4s
Reviewed-on: #136
2024-01-03 09:59:50 +01:00
f5bfb9fbdf fix weird iphone issue
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-03 09:59:09 +01:00
5b087baf02 Merge pull request 'staging' (#125) from staging into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m15s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m44s
Reviewed-on: #125
2024-01-01 21:05:58 +01:00
25d48241ee Merge pull request 'main' (#124) from main into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m18s
CI/CD Pipeline / deploy-staging (push) Successful in 17m40s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #124
2024-01-01 21:05:13 +01:00
cade5e1dfc Merge pull request 'fix-boatdamage' (#123) from fix-boatdamage into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m8s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 17m46s
Reviewed-on: #123
2024-01-01 18:55:37 +01:00
4430e7413f Merge pull request 'fix boat damage show' (#122) from fix-boatdamage into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m3s
CI/CD Pipeline / deploy-staging (push) Successful in 17m48s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #122
2024-01-01 18:55:13 +01:00
e925fadbb8 fix boat damage show
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-01 18:54:44 +01:00
ecb522bd98 Merge pull request 'fix' (#119) from fix into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 15m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #119
2024-01-01 18:37:03 +01:00
9c9840ad5a Merge branch 'fix' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into fix
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-01 18:36:34 +01:00
781fb8e952 push 2024-01-01 18:36:28 +01:00
54e02b7ea1 push 2024-01-01 18:36:28 +01:00
827613ca7c fix ci 2024-01-01 18:36:28 +01:00
945b96c55d Merge pull request 'push' (#121) from fix into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 15m51s
CI/CD Pipeline / deploy-staging (push) Successful in 18m21s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #121
2024-01-01 17:53:31 +01:00
4797d242a1 push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-01 17:53:00 +01:00
d1420961a0 Merge pull request 'push' (#120) from fix into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #120
2024-01-01 17:51:45 +01:00
f4896f4d80 push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-01 17:51:23 +01:00
b916b9783b Merge pull request 'fix ci' (#118) from fix into staging
Some checks failed
CI/CD Pipeline / test (push) Successful in 11m45s
CI/CD Pipeline / deploy-staging (push) Failing after 5m45s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #118
2024-01-01 17:26:00 +01:00
71926ad4b9 fix ci
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-01-01 17:25:35 +01:00
78bd9a91f0 Merge pull request 'update' (#117) from update into main
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #117
2024-01-01 16:52:33 +01:00
a557b8cfbd Merge pull request 'update deps' (#116) from update-deps into main
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #116
2024-01-01 16:51:27 +01:00
aad947009f Merge pull request 'update deps' (#115) from update-deps into staging
Some checks failed
CI/CD Pipeline / test (push) Successful in 11m45s
CI/CD Pipeline / deploy-staging (push) Failing after 5m44s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #115
2024-01-01 16:50:58 +01:00
998054ab68 update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m19s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-01-01 16:50:34 +01:00
29e7142883 Merge pull request 'main' (#111) from main into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #111
2024-01-01 16:30:52 +01:00
1f44b73b3b Merge pull request 'push' (#110) from update into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #110
2024-01-01 16:29:06 +01:00
24608c3da9 push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-01 16:28:31 +01:00
6bbe1302d9 Merge pull request 'nxdata' (#109) from nxdata into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m22s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 15m1s
Reviewed-on: #109
2024-01-01 15:51:16 +01:00
99d5328831 Merge pull request 'add mail' (#108) from mail into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Waiting to run
CI/CD Pipeline / deploy-main (push) Waiting to run
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #108
2024-01-01 15:50:44 +01:00
0ad342b147 add mail
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-01-01 15:50:06 +01:00
8b154b9675 Merge pull request 'better filter:' (#107) from nxdata into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m21s
CI/CD Pipeline / deploy-staging (push) Successful in 15m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #107
2023-12-30 21:30:48 +01:00
cf5b2751dd better filter:
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-30 21:30:27 +01:00
640f6bd0e7 Merge pull request 'add data from nx' (#106) from nxdata into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #106
2023-12-30 21:22:11 +01:00
5800f137f3 add data from nx
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2023-12-30 21:21:30 +01:00
415ac4f8d4 remove migration, as it is deployed on main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m4s
CI/CD Pipeline / deploy-staging (push) Successful in 14m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-30 17:47:25 +01:00
f27e76f328 Merge pull request 'groups' (#104) from groups into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m50s
Reviewed-on: #104
2023-12-30 17:14:02 +01:00
c9c7ad967d push
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2023-12-30 17:13:21 +01:00
a17ac5f1b5 fix seeds 2023-12-30 17:11:48 +01:00
9fb4167b50 in preparation to moving userdata into app, we switched to arbitrary groups 2023-12-30 17:11:46 +01:00
7e625cd5a5 Merge pull request 'fix error where joining cox to planned_event is not dispaying properly. The problem was the restructuring commit 7569798b00 (diff-0496fd5ec13efcfc97602dc5784170a0dd1)…' (#103) from fix-cox-join-staging into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m53s
CI/CD Pipeline / deploy-staging (push) Successful in 14m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #103
2023-12-28 18:20:19 +01:00
34280dbbea fix error where joining cox to planned_event is not dispaying properly. The problem was the restructuring commit 7569798b00 (diff-0496fd5ec13efcfc97602dc5784170a0dd124e6e) where I compared different ids
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m1s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-28 18:19:43 +01:00
54c013ec10 Merge pull request 'fix error where joining cox to planned_event is not dispaying properly. The problem was the restructuring commit 7569798b00 (diff-0496fd5ec13efcfc97602dc5784170a0dd1)…' (#101) from fix-cox-join into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m50s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m19s
Reviewed-on: #101
2023-12-28 18:15:09 +01:00
a9fbb05944 fix error where joining cox to planned_event is not dispaying properly. The problem was the restructuring commit 7569798b00 (diff-0496fd5ec13efcfc97602dc5784170a0dd124e6e) where I compared different ids
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m45s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-28 18:14:11 +01:00
44bc217452 Merge pull request 'show guest km, Fixes #41' (#99) from guest-km into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m14s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m24s
Reviewed-on: #99
2023-12-23 22:30:27 +01:00
990a62fe16 Merge pull request 'fix seeds' (#98) from groups into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m5s
CI/CD Pipeline / deploy-staging (push) Successful in 14m48s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #98
2023-12-23 22:14:45 +01:00
411eea179c fix seeds
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-23 22:14:24 +01:00
108242139a Merge pull request 'in preparation to moving userdata into app, we switched to arbitrary groups' (#97) from groups into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m41s
CI/CD Pipeline / deploy-staging (push) Successful in 14m52s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #97
2023-12-23 21:30:02 +01:00
c7d7d0ca83 in preparation to moving userdata into app, we switched to arbitrary groups
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m4s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-23 21:27:52 +01:00
e4da952a62 Merge pull request 'show guest km, Fixes #41' (#95) from guest-km into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m54s
CI/CD Pipeline / deploy-staging (push) Successful in 14m34s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #95
2023-12-23 15:28:00 +01:00
aca5340370 show guest km, Fixes #41
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m56s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-23 15:26:49 +01:00
d586f37cbe Merge pull request 'update-deps' (#94) from update-deps into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m16s
Reviewed-on: #94
2023-12-23 13:19:35 +01:00
1c9da6815e Merge pull request 'update deps' (#93) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m0s
CI/CD Pipeline / deploy-staging (push) Successful in 14m36s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #93
2023-12-23 13:18:54 +01:00
1a24d1fad5 update deps
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m30s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-23 13:17:23 +01:00
58943bd4e2 Merge pull request 'cleaner code' (#92) from cleaner-code into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m52s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m33s
Reviewed-on: #92
2023-12-01 10:11:17 +01:00
70e209f1ce Merge pull request 'cleaner-code' (#91) from cleaner-code into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m54s
CI/CD Pipeline / deploy-staging (push) Successful in 14m35s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #91
2023-12-01 10:10:55 +01:00
e081c7a31f cleaner code
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m28s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-12-01 10:10:18 +01:00
9be23ff723 Merge pull request 'cleaner-code' (#89) from cleaner-code into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m55s
CI/CD Pipeline / deploy-staging (push) Successful in 14m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #89
2023-11-27 12:50:59 +01:00
f408d4a181 Merge pull request 'update-deps' (#88) from update-deps into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m53s
CI/CD Pipeline / deploy-staging (push) Successful in 14m38s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #88
2023-11-27 11:59:02 +01:00
e87ee53512 update deps
Some checks failed
CI/CD Pipeline / test (push) Failing after 7m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2023-11-27 09:33:10 +01:00
d411127aa3 Merge pull request 'push' (#82) from update-deps-rocket into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m42s
CI/CD Pipeline / deploy-staging (push) Successful in 14m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #82
2023-11-22 14:05:41 +01:00
9a3f857936 Merge pull request 'upgrade sqlx to 0.7 Fixes #76 restructure registrations' (#80) from update-sqlx into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m51s
CI/CD Pipeline / deploy-staging (push) Successful in 14m46s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #80
2023-11-22 13:23:10 +01:00
26ad0ba80a notifications 2023-11-17 10:30:30 +01:00
146 changed files with 11988 additions and 3758 deletions

6
.dockerignore Normal file
View File

@ -0,0 +1,6 @@
target/
db.sqlite
.history/
frontend/node_modules/*
/static/
/data-ergo/

View File

@ -11,59 +11,45 @@ env:
jobs:
test:
runs-on: ubuntu-latest
container: rust:latest
container: git.hofer.link/ruderverein-donau-linz/rowing-ci:20240419
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
- uses: actions/checkout@v3
- name: Run Test DB Script
run: ./test_db.sh
- name: Checkout
uses: actions/checkout@v3
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- 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
- name: Build
run: |
cargo build
cd frontend && npm install && npm run build
- name: Frontend tests
run: cd frontend && npx playwright install && npx playwright test --workers 1 --reporter line
- name: Backend tests
run: cargo test --verbose
#- uses: actions/upload-artifact@v3
# if: always()
# with:
# name: playwright-report
# path: frontend/playwright-report/
# retention-days: 30
deploy-staging:
runs-on: ubuntu-latest
container: rust:latest
container: git.hofer.link/ruderverein-donau-linz/rowing-ci:20240419
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: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- name: Build
run: |
cargo build --release --target $CARGO_TARGET
@ -72,20 +58,20 @@ jobs:
- name: Deploy to Staging
run: |
mkdir ~/.ssh
mkdir -p ~/.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 target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/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/
scp staging-diff.sql $SSH_USER@$SSH_HOST:/home/rowing-staging/
scp -r static $SSH_USER@$SSH_HOST:/home/rowing-staging/
scp -r templates $SSH_USER@$SSH_HOST:/home/rowing-staging/
scp -r svelte $SSH_USER@$SSH_HOST:/home/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 'rm /home/rowing-staging/db.sqlite && cp /home/rowing/db.sqlite /home/rowing-staging/db.sqlite && mkdir -p /home/rowing-staging/svelte/build && mkdir -p /home/rowing-staging/data-ergo/thirty && mkdir -p /home/rowing-staging/data-ergo/dozen && sqlite3 /home/rowing-staging/db.sqlite < /home/rowing-staging/staging-diff.sql'
ssh $SSH_USER@$SSH_HOST 'mv /home/rowing-staging/rot-updating /home/rowing-staging/rot'
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
@ -94,20 +80,18 @@ jobs:
deploy-main:
runs-on: ubuntu-latest
container: rust:latest
container: git.hofer.link/ruderverein-donau-linz/rowing-ci:20240419
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: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- name: Build
run: |
@ -115,20 +99,20 @@ jobs:
strip target/$CARGO_TARGET/release/rot
cd frontend && npm install && npm run build
- name: Deploy to Main
- name: Deploy to production
run: |
mkdir ~/.ssh
mkdir -p ~/.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'
scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/rowing/rot-updating
scp -r static $SSH_USER@$SSH_HOST:/home/rowing/
scp -r templates $SSH_USER@$SSH_HOST:/home/rowing/
scp -r svelte $SSH_USER@$SSH_HOST:/home/rowing/
ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/rowing/svelte/build && mkdir -p /home/rowing/data-ergo/thirty && mkdir -p /home/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 'mv /home/rowing/rot-updating /home/rowing/rot'
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ Rocket.toml
frontend/node_modules/*
/static/
/data-ergo/
usage.txt

1498
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,15 +10,24 @@ rest = []
[dependencies]
rocket = { version = "0.5.0", features = ["secrets"]}
rocket_dyn_templates = {version = "0.1.0", features = [ "tera" ], optional = true }
rocket_dyn_templates = {version = "0.2", features = [ "tera" ], optional = true }
log = "0.4"
env_logger = "0.10"
env_logger = "0.11"
sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio-rustls", "macros", "chrono", "time"] }
argon2 = "0.5"
serde = { version = "1.0", features = [ "derive" ]}
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"]}
chrono-tz = "0.8"
chrono-tz = "0.9"
tera = { version = "1.18", features = ["date-locale"], optional = true}
ics = "0.5"
futures = "0.3"
lettre = "0.11"
csv = "1.3"
itertools = "0.13"
job_scheduler_ng = "2.0"
ureq = { version = "2.9", features = ["json"] }
regex = "1.10"
[target.'cfg(not(windows))'.dependencies]
openssl = { version = "0.10", features = [ "vendored" ] }

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
# This dockerfile is used as basis for the CI jobs.
# Process to renew it:
# 0. Login to gitea docker registry: `docker login git.hofer.link`
# 1. Build the image `docker build .`
# 2. Tag the image: `docker tag <id> git.hofer.link/ruderverein-donau-linz/rowing-ci:<date>`
# 3. Push the image: `docker push git.hofer.link/ruderverein-donau-linz/rowing-ci:<date>`
FROM rust:1.77.2
RUN apt-get update && apt-get install -y sqlite3
# nodejs
RUN apt-get install -y curl && \
curl -sL https://deb.nodesource.com/setup_21.x | bash - && \
apt-get install -y nodejs
# playwright
RUN npx playwright install --with-deps
# deployment
RUN rustup target add x86_64-unknown-linux-musl
RUN apt-get install -y -qq pkg-config sshpass musl musl-tools curl gnupg libssl-dev
# TEMPORARY act workaround (otherwise gitea cache is not working)
RUN apt-get install -y zstd

View File

@ -1,15 +1,48 @@
# Frontend Process
´cd frontend´
´npm install´
´npm run (watch/build)´
![latest CI run on main](https://git.hofer.link/Ruderverein-Donau-Linz/rowt/actions/workflows/action.yml/badge.svg?branch=main)
# Notes / Bugfixes
# Build
## Frontend
- [] support esc to close sidebar
- [] reload page -> don't throw input away!
1. `cd frontend`
2. `npm install`
3. `npm run (watch/build)`
# Run
## Backend
1. `cargo r`
# Nice to have
# Test
## Frontend
- [] my trips for cox
- `npx playwright test --workers 1 --project firefox`
- Nice UI: `--ui`
- Generate tests: `npx playwright codegen`
## Backend (Unit + Integration)
`cargo t`
# Lints
- Rust: `cargo check`
- Tera files: `djlint **.html.tera --profile=jinja --reformat`
- Typescript: `prettier -w *.ts`
# Dependencies
- `sqlite3`
- `rust`
# Nginx config
```
server {
server_name staging.rudernlinz.at;
location / {
proxy_pass http://localhost:7999/; # The / is important!
}
}
server {
server_name app.rudernlinz.at;
location / {
proxy_pass http://localhost:8001/; # The / is important!
}
}
```

View File

@ -2,3 +2,6 @@
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
rss_key = "rss-key-for-ci"
limits = { file = "10 MiB", data-form = "10 MiB"}
smtp_pw = "8kIjlLH79Ky6D3jQ"
usage_log_path = "./usage.txt"
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"

494
db.svg Normal file
View File

@ -0,0 +1,494 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 10.0.1 (0)
-->
<!-- Title: undefined Pages: 1 -->
<svg width="2246pt" height="2402pt"
viewBox="0.00 0.00 2245.60 2401.85" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(28.8 2350.8)">
<title>undefined</title>
<polygon fill="white" stroke="none" points="-28.8,51.05 -28.8,-2350.8 2216.8,-2350.8 2216.8,51.05 -28.8,51.05"/>
<text text-anchor="start" x="1064.75" y="14.4" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">db.sqlite</text>
<!-- user -->
<g id="node1" class="node">
<title>user</title>
<path fill="none" stroke="black" d="M930.94,-936.33C930.94,-936.33 1074.02,-936.33 1074.02,-936.33 1080.02,-936.33 1086.02,-942.33 1086.02,-948.33 1086.02,-948.33 1086.02,-1306.6 1086.02,-1306.6 1086.02,-1312.6 1080.02,-1318.6 1074.02,-1318.6 1074.02,-1318.6 930.94,-1318.6 930.94,-1318.6 924.94,-1318.6 918.94,-1312.6 918.94,-1306.6 918.94,-1306.6 918.94,-948.33 918.94,-948.33 918.94,-942.33 924.94,-936.33 930.94,-936.33"/>
<text text-anchor="start" x="986.35" y="-1298.87" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">user</text>
<polyline fill="none" stroke="black" points="918.94,-1288.84 1086.02,-1288.84"/>
<text text-anchor="start" x="925.98" y="-1273.56" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="946.23" y="-1273.56" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="925.98" y="-1255.31" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="964.23" y="-1255.31" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1237.06" font-family="Helvetica,sans-Serif" font-size="12.00">pw </text>
<text text-anchor="start" x="946.98" y="-1237.06" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1218.81" font-family="Helvetica,sans-Serif" font-size="12.00">deleted </text>
<text text-anchor="start" x="974.73" y="-1218.81" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="925.98" y="-1200.56" font-family="Helvetica,sans-Serif" font-size="12.00">last_access </text>
<text text-anchor="start" x="997.23" y="-1200.56" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="925.98" y="-1182.31" font-family="Helvetica,sans-Serif" font-size="12.00">dob </text>
<text text-anchor="start" x="952.23" y="-1182.31" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1164.06" font-family="Helvetica,sans-Serif" font-size="12.00">weight </text>
<text text-anchor="start" x="969.48" y="-1164.06" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1145.81" font-family="Helvetica,sans-Serif" font-size="12.00">sex </text>
<text text-anchor="start" x="949.98" y="-1145.81" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1127.56" font-family="Helvetica,sans-Serif" font-size="12.00">dirty_thirty </text>
<text text-anchor="start" x="994.23" y="-1127.56" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1109.31" font-family="Helvetica,sans-Serif" font-size="12.00">dirty_dozen </text>
<text text-anchor="start" x="998.73" y="-1109.31" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1091.06" font-family="Helvetica,sans-Serif" font-size="12.00">member_since_date </text>
<text text-anchor="start" x="1051.23" y="-1091.06" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1072.81" font-family="Helvetica,sans-Serif" font-size="12.00">birthdate </text>
<text text-anchor="start" x="984.48" y="-1072.81" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1054.56" font-family="Helvetica,sans-Serif" font-size="12.00">mail </text>
<text text-anchor="start" x="955.23" y="-1054.56" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1036.31" font-family="Helvetica,sans-Serif" font-size="12.00">nickname </text>
<text text-anchor="start" x="988.23" y="-1036.31" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-1018.06" font-family="Helvetica,sans-Serif" font-size="12.00">notes </text>
<text text-anchor="start" x="962.73" y="-1018.06" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-999.81" font-family="Helvetica,sans-Serif" font-size="12.00">phone </text>
<text text-anchor="start" x="967.23" y="-999.81" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-981.56" font-family="Helvetica,sans-Serif" font-size="12.00">address </text>
<text text-anchor="start" x="976.23" y="-981.56" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="925.98" y="-963.31" font-family="Helvetica,sans-Serif" font-size="12.00">family_id </text>
<text text-anchor="start" x="982.98" y="-963.31" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="925.98" y="-945.06" font-family="Helvetica,sans-Serif" font-size="12.00">membership_pdf </text>
<text text-anchor="start" x="1030.98" y="-945.06" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">blob</text>
</g>
<!-- family -->
<g id="node16" class="node">
<title>family</title>
<path fill="none" stroke="black" d="M263.86,-1264.04C263.86,-1264.04 383.94,-1264.04 383.94,-1264.04 389.94,-1264.04 395.94,-1270.04 395.94,-1276.04 395.94,-1276.04 395.94,-1305.81 395.94,-1305.81 395.94,-1311.81 389.94,-1317.81 383.94,-1317.81 383.94,-1317.81 263.86,-1317.81 263.86,-1317.81 257.86,-1317.81 251.86,-1311.81 251.86,-1305.81 251.86,-1305.81 251.86,-1276.04 251.86,-1276.04 251.86,-1270.04 257.86,-1264.04 263.86,-1264.04"/>
<text text-anchor="start" x="301.03" y="-1298.08" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">family</text>
<polyline fill="none" stroke="black" points="251.86,-1288.05 395.94,-1288.05"/>
<text text-anchor="start" x="258.9" y="-1272.77" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="279.15" y="-1272.77" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
</g>
<!-- user&#45;&gt;family -->
<g id="edge1" class="edge">
<title>user&#45;&gt;family</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M918.59,-1147.68C787.22,-1179.32 534.59,-1240.17 404.97,-1271.4"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="404.68,-1268.59 397.56,-1273.18 405.99,-1274.03 404.68,-1268.59"/>
</g>
<!-- trip_details -->
<g id="node2" class="node">
<title>trip_details</title>
<path fill="none" stroke="black" d="M1115.28,-1807.64C1115.28,-1807.64 1269.61,-1807.64 1269.61,-1807.64 1275.61,-1807.64 1281.61,-1813.64 1281.61,-1819.64 1281.61,-1819.64 1281.61,-1995.41 1281.61,-1995.41 1281.61,-2001.41 1275.61,-2007.41 1269.61,-2007.41 1269.61,-2007.41 1115.28,-2007.41 1115.28,-2007.41 1109.28,-2007.41 1103.28,-2001.41 1103.28,-1995.41 1103.28,-1995.41 1103.28,-1819.64 1103.28,-1819.64 1103.28,-1813.64 1109.28,-1807.64 1115.28,-1807.64"/>
<text text-anchor="start" x="1151.19" y="-1987.68" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">trip_details</text>
<polyline fill="none" stroke="black" points="1103.28,-1977.65 1281.61,-1977.65"/>
<text text-anchor="start" x="1110.32" y="-1962.37" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="1130.57" y="-1962.37" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1110.32" y="-1944.12" font-family="Helvetica,sans-Serif" font-size="12.00">planned_starting_time </text>
<text text-anchor="start" x="1246.82" y="-1944.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1110.32" y="-1925.87" font-family="Helvetica,sans-Serif" font-size="12.00">max_people </text>
<text text-anchor="start" x="1186.82" y="-1925.87" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1110.32" y="-1907.62" font-family="Helvetica,sans-Serif" font-size="12.00">day </text>
<text text-anchor="start" x="1135.82" y="-1907.62" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1110.32" y="-1889.37" font-family="Helvetica,sans-Serif" font-size="12.00">notes </text>
<text text-anchor="start" x="1147.07" y="-1889.37" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1110.32" y="-1871.12" font-family="Helvetica,sans-Serif" font-size="12.00">trip_type_id </text>
<text text-anchor="start" x="1183.07" y="-1871.12" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1110.32" y="-1852.87" font-family="Helvetica,sans-Serif" font-size="12.00">allow_guests </text>
<text text-anchor="start" x="1189.82" y="-1852.87" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="1110.32" y="-1834.62" font-family="Helvetica,sans-Serif" font-size="12.00">always_show </text>
<text text-anchor="start" x="1191.32" y="-1834.62" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="1110.32" y="-1816.37" font-family="Helvetica,sans-Serif" font-size="12.00">is_locked </text>
<text text-anchor="start" x="1168.07" y="-1816.37" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
</g>
<!-- trip_type -->
<g id="node5" class="node">
<title>trip_type</title>
<path fill="none" stroke="black" d="M1040.53,-2194.35C1040.53,-2194.35 1160.61,-2194.35 1160.61,-2194.35 1166.61,-2194.35 1172.61,-2200.35 1172.61,-2206.35 1172.61,-2206.35 1172.61,-2309.12 1172.61,-2309.12 1172.61,-2315.12 1166.61,-2321.12 1160.61,-2321.12 1160.61,-2321.12 1040.53,-2321.12 1040.53,-2321.12 1034.53,-2321.12 1028.53,-2315.12 1028.53,-2309.12 1028.53,-2309.12 1028.53,-2206.35 1028.53,-2206.35 1028.53,-2200.35 1034.53,-2194.35 1040.53,-2194.35"/>
<text text-anchor="start" x="1067.95" y="-2301.39" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">trip_type</text>
<polyline fill="none" stroke="black" points="1028.53,-2291.36 1172.61,-2291.36"/>
<text text-anchor="start" x="1035.57" y="-2276.08" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="1055.82" y="-2276.08" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1035.57" y="-2257.83" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="1073.82" y="-2257.83" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1035.57" y="-2239.58" font-family="Helvetica,sans-Serif" font-size="12.00">desc </text>
<text text-anchor="start" x="1067.07" y="-2239.58" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1035.57" y="-2221.33" font-family="Helvetica,sans-Serif" font-size="12.00">question </text>
<text text-anchor="start" x="1090.32" y="-2221.33" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1035.57" y="-2203.08" font-family="Helvetica,sans-Serif" font-size="12.00">icon </text>
<text text-anchor="start" x="1064.07" y="-2203.08" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- trip_details&#45;&gt;trip_type -->
<g id="edge2" class="edge">
<title>trip_details&#45;&gt;trip_type</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1166.12,-2007.88C1151.28,-2064.41 1133.1,-2133.75 1119.65,-2185.02"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1116.94,-2184.29 1117.62,-2192.74 1122.36,-2185.71 1116.94,-2184.29"/>
</g>
<!-- planned_event -->
<g id="node3" class="node">
<title>planned_event</title>
<path fill="none" stroke="black" d="M853.81,-1400.29C853.81,-1400.29 1088.39,-1400.29 1088.39,-1400.29 1094.39,-1400.29 1100.39,-1406.29 1100.39,-1412.29 1100.39,-1412.29 1100.39,-1515.06 1100.39,-1515.06 1100.39,-1521.06 1094.39,-1527.06 1088.39,-1527.06 1088.39,-1527.06 853.81,-1527.06 853.81,-1527.06 847.81,-1527.06 841.81,-1521.06 841.81,-1515.06 841.81,-1515.06 841.81,-1412.29 841.81,-1412.29 841.81,-1406.29 847.81,-1400.29 853.81,-1400.29"/>
<text text-anchor="start" x="917.85" y="-1507.33" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">planned_event</text>
<polyline fill="none" stroke="black" points="841.81,-1497.3 1100.39,-1497.3"/>
<text text-anchor="start" x="848.85" y="-1482.02" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="869.1" y="-1482.02" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="848.85" y="-1463.77" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="887.1" y="-1463.77" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="848.85" y="-1445.52" font-family="Helvetica,sans-Serif" font-size="12.00">planned_amount_cox </text>
<text text-anchor="start" x="979.35" y="-1445.52" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer unsigned</text>
<text text-anchor="start" x="848.85" y="-1427.27" font-family="Helvetica,sans-Serif" font-size="12.00">trip_details_id </text>
<text text-anchor="start" x="934.35" y="-1427.27" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="848.85" y="-1409.02" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="916.35" y="-1409.02" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- planned_event&#45;&gt;trip_details -->
<g id="edge3" class="edge">
<title>planned_event&#45;&gt;trip_details</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1002.82,-1527.27C1038.14,-1598.09 1095.84,-1713.81 1138.33,-1799.02"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1135.71,-1800.04 1141.79,-1805.95 1140.72,-1797.54 1135.71,-1800.04"/>
</g>
<!-- trip -->
<g id="node4" class="node">
<title>trip</title>
<path fill="none" stroke="black" d="M571.83,-1689.61C571.83,-1689.61 718.66,-1689.61 718.66,-1689.61 724.66,-1689.61 730.66,-1695.61 730.66,-1701.61 730.66,-1701.61 730.66,-1804.38 730.66,-1804.38 730.66,-1810.38 724.66,-1816.38 718.66,-1816.38 718.66,-1816.38 571.83,-1816.38 571.83,-1816.38 565.83,-1816.38 559.83,-1810.38 559.83,-1804.38 559.83,-1804.38 559.83,-1701.61 559.83,-1701.61 559.83,-1695.61 565.83,-1689.61 571.83,-1689.61"/>
<text text-anchor="start" x="632.12" y="-1796.65" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">trip</text>
<polyline fill="none" stroke="black" points="559.83,-1786.62 730.66,-1786.62"/>
<text text-anchor="start" x="566.87" y="-1771.34" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="587.12" y="-1771.34" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="566.87" y="-1753.09" font-family="Helvetica,sans-Serif" font-size="12.00">cox_id </text>
<text text-anchor="start" x="607.37" y="-1753.09" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="566.87" y="-1734.84" font-family="Helvetica,sans-Serif" font-size="12.00">trip_details_id </text>
<text text-anchor="start" x="652.37" y="-1734.84" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="566.87" y="-1716.59" font-family="Helvetica,sans-Serif" font-size="12.00">planned_event_id </text>
<text text-anchor="start" x="674.87" y="-1716.59" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="566.87" y="-1698.34" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="634.37" y="-1698.34" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- trip&#45;&gt;user -->
<g id="edge6" class="edge">
<title>trip&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M678.73,-1689.16C716.58,-1617.53 780.51,-1498.13 838.61,-1397.09 862.41,-1355.71 888.99,-1311.27 913.7,-1270.64"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="915.98,-1272.28 917.75,-1263.99 911.2,-1269.37 915.98,-1272.28"/>
</g>
<!-- trip&#45;&gt;trip_details -->
<g id="edge5" class="edge">
<title>trip&#45;&gt;trip_details</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M731.04,-1777.23C829.53,-1805.04 990.77,-1850.57 1094.21,-1879.78"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1093.33,-1882.45 1101.79,-1881.93 1094.86,-1877.06 1093.33,-1882.45"/>
</g>
<!-- trip&#45;&gt;planned_event -->
<g id="edge4" class="edge">
<title>trip&#45;&gt;planned_event</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M717.16,-1689.15C769.1,-1643.03 839.22,-1580.77 892.62,-1533.36"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="894.23,-1535.67 898.35,-1528.27 890.51,-1531.48 894.23,-1535.67"/>
</g>
<!-- location -->
<g id="node6" class="node">
<title>location</title>
<path fill="none" stroke="black" d="M11.68,-686.78C11.68,-686.78 131.76,-686.78 131.76,-686.78 137.76,-686.78 143.76,-692.78 143.76,-698.78 143.76,-698.78 143.76,-746.8 143.76,-746.8 143.76,-752.8 137.76,-758.8 131.76,-758.8 131.76,-758.8 11.68,-758.8 11.68,-758.8 5.68,-758.8 -0.32,-752.8 -0.32,-746.8 -0.32,-746.8 -0.32,-698.78 -0.32,-698.78 -0.32,-692.78 5.68,-686.78 11.68,-686.78"/>
<text text-anchor="start" x="42.47" y="-739.07" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">location</text>
<polyline fill="none" stroke="black" points="-0.32,-729.04 143.76,-729.04"/>
<text text-anchor="start" x="6.72" y="-713.76" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="26.97" y="-713.76" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="6.72" y="-695.51" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="44.97" y="-695.51" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- boat -->
<g id="node7" class="node">
<title>boat</title>
<path fill="none" stroke="black" d="M665.61,-560.97C665.61,-560.97 912.94,-560.97 912.94,-560.97 918.94,-560.97 924.94,-566.97 924.94,-572.97 924.94,-572.97 924.94,-785.24 924.94,-785.24 924.94,-791.24 918.94,-797.24 912.94,-797.24 912.94,-797.24 665.61,-797.24 665.61,-797.24 659.61,-797.24 653.61,-791.24 653.61,-785.24 653.61,-785.24 653.61,-572.97 653.61,-572.97 653.61,-566.97 659.61,-560.97 665.61,-560.97"/>
<text text-anchor="start" x="772.77" y="-777.51" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">boat</text>
<polyline fill="none" stroke="black" points="653.61,-767.48 924.94,-767.48"/>
<text text-anchor="start" x="660.65" y="-752.2" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="680.9" y="-752.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="660.65" y="-733.95" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="698.9" y="-733.95" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="660.65" y="-715.7" font-family="Helvetica,sans-Serif" font-size="12.00">amount_seats </text>
<text text-anchor="start" x="748.4" y="-715.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="660.65" y="-697.45" font-family="Helvetica,sans-Serif" font-size="12.00">location_id </text>
<text text-anchor="start" x="728.15" y="-697.45" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="660.65" y="-679.2" font-family="Helvetica,sans-Serif" font-size="12.00">owner </text>
<text text-anchor="start" x="701.9" y="-679.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="660.65" y="-660.95" font-family="Helvetica,sans-Serif" font-size="12.00">year_built </text>
<text text-anchor="start" x="722.9" y="-660.95" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="660.65" y="-642.7" font-family="Helvetica,sans-Serif" font-size="12.00">boatbuilder </text>
<text text-anchor="start" x="732.65" y="-642.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="660.65" y="-624.45" font-family="Helvetica,sans-Serif" font-size="12.00">default_shipmaster_only_steering </text>
<text text-anchor="start" x="864.65" y="-624.45" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="660.65" y="-606.2" font-family="Helvetica,sans-Serif" font-size="12.00">skull </text>
<text text-anchor="start" x="690.65" y="-606.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="660.65" y="-587.95" font-family="Helvetica,sans-Serif" font-size="12.00">external </text>
<text text-anchor="start" x="713.15" y="-587.95" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="660.65" y="-569.7" font-family="Helvetica,sans-Serif" font-size="12.00">default_destination </text>
<text text-anchor="start" x="778.4" y="-569.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- boat&#45;&gt;user -->
<g id="edge7" class="edge">
<title>boat&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M845.57,-797.5C866.72,-841.98 891.3,-893.67 914.65,-942.76"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="911.98,-943.68 917.95,-949.7 917.04,-941.27 911.98,-943.68"/>
</g>
<!-- boat&#45;&gt;location -->
<g id="edge8" class="edge">
<title>boat&#45;&gt;location</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M653.46,-687.38C505.96,-696.35 275.15,-710.41 153.33,-717.82"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="153.21,-715.02 145.4,-718.3 153.55,-720.61 153.21,-715.02"/>
</g>
<!-- logbook_type -->
<g id="node8" class="node">
<title>logbook_type</title>
<path fill="none" stroke="black" d="M1052.95,-0.71C1052.95,-0.71 1173.03,-0.71 1173.03,-0.71 1179.03,-0.71 1185.03,-6.71 1185.03,-12.71 1185.03,-12.71 1185.03,-60.73 1185.03,-60.73 1185.03,-66.73 1179.03,-72.73 1173.03,-72.73 1173.03,-72.73 1052.95,-72.73 1052.95,-72.73 1046.95,-72.73 1040.95,-66.73 1040.95,-60.73 1040.95,-60.73 1040.95,-12.71 1040.95,-12.71 1040.95,-6.71 1046.95,-0.71 1052.95,-0.71"/>
<text text-anchor="start" x="1064.24" y="-53" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">logbook_type</text>
<polyline fill="none" stroke="black" points="1040.95,-42.97 1185.03,-42.97"/>
<text text-anchor="start" x="1047.99" y="-27.69" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="1068.24" y="-27.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1047.99" y="-9.44" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="1086.24" y="-9.44" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- log -->
<g id="node9" class="node">
<title>log</title>
<path fill="none" stroke="black" d="M1569.2,-692.51C1569.2,-692.51 1689.28,-692.51 1689.28,-692.51 1695.28,-692.51 1701.28,-698.51 1701.28,-704.51 1701.28,-704.51 1701.28,-770.78 1701.28,-770.78 1701.28,-776.78 1695.28,-782.78 1689.28,-782.78 1689.28,-782.78 1569.2,-782.78 1569.2,-782.78 1563.2,-782.78 1557.2,-776.78 1557.2,-770.78 1557.2,-770.78 1557.2,-704.51 1557.2,-704.51 1557.2,-698.51 1563.2,-692.51 1569.2,-692.51"/>
<text text-anchor="start" x="1617.99" y="-763.05" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">log</text>
<polyline fill="none" stroke="black" points="1557.2,-753.02 1701.28,-753.02"/>
<text text-anchor="start" x="1564.24" y="-737.74" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="1584.49" y="-737.74" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1564.24" y="-719.49" font-family="Helvetica,sans-Serif" font-size="12.00">msg </text>
<text text-anchor="start" x="1593.49" y="-719.49" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1564.24" y="-701.24" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="1631.74" y="-701.24" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
</g>
<!-- boat_damage -->
<g id="node10" class="node">
<title>boat_damage</title>
<path fill="none" stroke="black" d="M476.91,-984.61C476.91,-984.61 613.99,-984.61 613.99,-984.61 619.99,-984.61 625.99,-990.61 625.99,-996.61 625.99,-996.61 625.99,-1190.63 625.99,-1190.63 625.99,-1196.63 619.99,-1202.63 613.99,-1202.63 613.99,-1202.63 476.91,-1202.63 476.91,-1202.63 470.91,-1202.63 464.91,-1196.63 464.91,-1190.63 464.91,-1190.63 464.91,-996.61 464.91,-996.61 464.91,-990.61 470.91,-984.61 476.91,-984.61"/>
<text text-anchor="start" x="496.32" y="-1182.9" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">boat_damage</text>
<polyline fill="none" stroke="black" points="464.91,-1172.87 625.99,-1172.87"/>
<text text-anchor="start" x="471.95" y="-1157.59" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="492.2" y="-1157.59" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="471.95" y="-1139.34" font-family="Helvetica,sans-Serif" font-size="12.00">boat_id </text>
<text text-anchor="start" x="519.2" y="-1139.34" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="471.95" y="-1121.09" font-family="Helvetica,sans-Serif" font-size="12.00">desc </text>
<text text-anchor="start" x="503.45" y="-1121.09" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="471.95" y="-1102.84" font-family="Helvetica,sans-Serif" font-size="12.00">user_id_created </text>
<text text-anchor="start" x="570.2" y="-1102.84" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="471.95" y="-1084.59" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="539.45" y="-1084.59" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="471.95" y="-1066.34" font-family="Helvetica,sans-Serif" font-size="12.00">user_id_fixed </text>
<text text-anchor="start" x="553.7" y="-1066.34" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="471.95" y="-1048.09" font-family="Helvetica,sans-Serif" font-size="12.00">fixed_at </text>
<text text-anchor="start" x="522.95" y="-1048.09" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="471.95" y="-1029.84" font-family="Helvetica,sans-Serif" font-size="12.00">user_id_verified </text>
<text text-anchor="start" x="569.45" y="-1029.84" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="471.95" y="-1011.59" font-family="Helvetica,sans-Serif" font-size="12.00">verified_at </text>
<text text-anchor="start" x="538.7" y="-1011.59" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="471.95" y="-993.34" font-family="Helvetica,sans-Serif" font-size="12.00">lock_boat </text>
<text text-anchor="start" x="532.7" y="-993.34" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
</g>
<!-- boat_damage&#45;&gt;user -->
<g id="edge9" class="edge">
<title>boat_damage&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M626.37,-1099.61C705.57,-1105.48 826.12,-1114.41 909.29,-1120.57"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="908.93,-1123.35 917.12,-1121.15 909.35,-1117.76 908.93,-1123.35"/>
</g>
<!-- boat_damage&#45;&gt;user -->
<g id="edge10" class="edge">
<title>boat_damage&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M626.37,-1099.61C705.57,-1105.48 826.12,-1114.41 909.29,-1120.57"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="908.93,-1123.35 917.12,-1121.15 909.35,-1117.76 908.93,-1123.35"/>
</g>
<!-- boat_damage&#45;&gt;user -->
<g id="edge11" class="edge">
<title>boat_damage&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M626.37,-1099.61C705.57,-1105.48 826.12,-1114.41 909.29,-1120.57"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="908.93,-1123.35 917.12,-1121.15 909.35,-1117.76 908.93,-1123.35"/>
</g>
<!-- boat_damage&#45;&gt;boat -->
<g id="edge12" class="edge">
<title>boat_damage&#45;&gt;boat</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M609.83,-984.17C642.1,-929.3 681.35,-862.57 714.91,-805.53"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="717.27,-807.04 718.91,-798.72 712.44,-804.2 717.27,-807.04"/>
</g>
<!-- user_trip -->
<g id="node11" class="node">
<title>user_trip</title>
<path fill="none" stroke="black" d="M1355.83,-1397.72C1355.83,-1397.72 1480.16,-1397.72 1480.16,-1397.72 1486.16,-1397.72 1492.16,-1403.72 1492.16,-1409.72 1492.16,-1409.72 1492.16,-1494.24 1492.16,-1494.24 1492.16,-1500.24 1486.16,-1506.24 1480.16,-1506.24 1480.16,-1506.24 1355.83,-1506.24 1355.83,-1506.24 1349.83,-1506.24 1343.83,-1500.24 1343.83,-1494.24 1343.83,-1494.24 1343.83,-1409.72 1343.83,-1409.72 1343.83,-1403.72 1349.83,-1397.72 1355.83,-1397.72"/>
<text text-anchor="start" x="1385.37" y="-1486.51" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">user_trip</text>
<polyline fill="none" stroke="black" points="1343.83,-1476.48 1492.16,-1476.48"/>
<text text-anchor="start" x="1350.87" y="-1461.2" font-family="Helvetica,sans-Serif" font-size="12.00">user_id </text>
<text text-anchor="start" x="1397.37" y="-1461.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1350.87" y="-1442.95" font-family="Helvetica,sans-Serif" font-size="12.00">user_note </text>
<text text-anchor="start" x="1413.87" y="-1442.95" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1350.87" y="-1424.7" font-family="Helvetica,sans-Serif" font-size="12.00">trip_details_id </text>
<text text-anchor="start" x="1436.37" y="-1424.7" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1350.87" y="-1406.45" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="1418.37" y="-1406.45" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- user_trip&#45;&gt;user -->
<g id="edge14" class="edge">
<title>user_trip&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1347.97,-1397.3C1278.07,-1342.7 1170.3,-1258.53 1093.73,-1198.73"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1095.52,-1196.58 1087.49,-1193.86 1092.07,-1200.99 1095.52,-1196.58"/>
</g>
<!-- user_trip&#45;&gt;trip_details -->
<g id="edge13" class="edge">
<title>user_trip&#45;&gt;trip_details</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1391.01,-1506.48C1355.37,-1578.46 1291.79,-1706.87 1246.17,-1799.01"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1243.73,-1797.62 1242.69,-1806.03 1248.75,-1800.1 1243.73,-1797.62"/>
</g>
<!-- rower -->
<g id="node12" class="node">
<title>rower</title>
<path fill="none" stroke="black" d="M1324.54,-940.65C1324.54,-940.65 1444.62,-940.65 1444.62,-940.65 1450.62,-940.65 1456.62,-946.65 1456.62,-952.65 1456.62,-952.65 1456.62,-1000.67 1456.62,-1000.67 1456.62,-1006.67 1450.62,-1012.67 1444.62,-1012.67 1444.62,-1012.67 1324.54,-1012.67 1324.54,-1012.67 1318.54,-1012.67 1312.54,-1006.67 1312.54,-1000.67 1312.54,-1000.67 1312.54,-952.65 1312.54,-952.65 1312.54,-946.65 1318.54,-940.65 1324.54,-940.65"/>
<text text-anchor="start" x="1362.83" y="-992.94" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">rower</text>
<polyline fill="none" stroke="black" points="1312.54,-982.91 1456.62,-982.91"/>
<text text-anchor="start" x="1319.58" y="-967.63" font-family="Helvetica,sans-Serif" font-size="12.00">logbook_id </text>
<text text-anchor="start" x="1387.08" y="-967.63" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1319.58" y="-949.38" font-family="Helvetica,sans-Serif" font-size="12.00">rower_id </text>
<text text-anchor="start" x="1374.33" y="-949.38" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
</g>
<!-- rower&#45;&gt;user -->
<g id="edge15" class="edge">
<title>rower&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1312.26,-1005.2C1250.64,-1029.52 1161.71,-1064.62 1094.93,-1090.98"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1094.17,-1088.27 1087.75,-1093.81 1096.22,-1093.48 1094.17,-1088.27"/>
</g>
<!-- logbook -->
<g id="node13" class="node">
<title>logbook</title>
<path fill="none" stroke="black" d="M968.96,-391.46C968.96,-391.46 1168.29,-391.46 1168.29,-391.46 1174.29,-391.46 1180.29,-397.46 1180.29,-403.46 1180.29,-403.46 1180.29,-615.73 1180.29,-615.73 1180.29,-621.73 1174.29,-627.73 1168.29,-627.73 1168.29,-627.73 968.96,-627.73 968.96,-627.73 962.96,-627.73 956.96,-621.73 956.96,-615.73 956.96,-615.73 956.96,-403.46 956.96,-403.46 956.96,-397.46 962.96,-391.46 968.96,-391.46"/>
<text text-anchor="start" x="1039.38" y="-608" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">logbook</text>
<polyline fill="none" stroke="black" points="956.96,-597.97 1180.29,-597.97"/>
<text text-anchor="start" x="964" y="-582.69" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="984.25" y="-582.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="964" y="-564.44" font-family="Helvetica,sans-Serif" font-size="12.00">boat_id </text>
<text text-anchor="start" x="1011.25" y="-564.44" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="964" y="-546.19" font-family="Helvetica,sans-Serif" font-size="12.00">shipmaster </text>
<text text-anchor="start" x="1034.5" y="-546.19" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="964" y="-527.94" font-family="Helvetica,sans-Serif" font-size="12.00">steering_person </text>
<text text-anchor="start" x="1063.75" y="-527.94" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="964" y="-509.69" font-family="Helvetica,sans-Serif" font-size="12.00">shipmaster_only_steering </text>
<text text-anchor="start" x="1120" y="-509.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">boolean</text>
<text text-anchor="start" x="964" y="-491.44" font-family="Helvetica,sans-Serif" font-size="12.00">departure </text>
<text text-anchor="start" x="1027" y="-491.44" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="964" y="-473.19" font-family="Helvetica,sans-Serif" font-size="12.00">arrival </text>
<text text-anchor="start" x="1005.25" y="-473.19" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="964" y="-454.94" font-family="Helvetica,sans-Serif" font-size="12.00">destination </text>
<text text-anchor="start" x="1033.75" y="-454.94" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="964" y="-436.69" font-family="Helvetica,sans-Serif" font-size="12.00">distance_in_km </text>
<text text-anchor="start" x="1059.25" y="-436.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="964" y="-418.44" font-family="Helvetica,sans-Serif" font-size="12.00">comments </text>
<text text-anchor="start" x="1031.5" y="-418.44" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="964" y="-400.19" font-family="Helvetica,sans-Serif" font-size="12.00">logtype </text>
<text text-anchor="start" x="1012" y="-400.19" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
</g>
<!-- rower&#45;&gt;logbook -->
<g id="edge16" class="edge">
<title>rower&#45;&gt;logbook</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1360.05,-940.4C1316.38,-875.84 1223.23,-738.14 1153.93,-635.7"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1156.34,-634.26 1149.54,-629.2 1151.7,-637.4 1156.34,-634.26"/>
</g>
<!-- logbook&#45;&gt;user -->
<g id="edge18" class="edge">
<title>logbook&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1055.95,-628C1046.91,-712.44 1034.54,-828.03 1023.97,-926.76"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1021.2,-926.34 1023.13,-934.6 1026.76,-926.94 1021.2,-926.34"/>
</g>
<!-- logbook&#45;&gt;user -->
<g id="edge19" class="edge">
<title>logbook&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1055.95,-628C1046.91,-712.44 1034.54,-828.03 1023.97,-926.76"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1021.2,-926.34 1023.13,-934.6 1026.76,-926.94 1021.2,-926.34"/>
</g>
<!-- logbook&#45;&gt;boat -->
<g id="edge20" class="edge">
<title>logbook&#45;&gt;boat</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M956.61,-577.57C948.87,-582.26 941.01,-587.03 933.13,-591.81"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="931.81,-589.34 926.42,-595.89 934.71,-594.13 931.81,-589.34"/>
</g>
<!-- logbook&#45;&gt;logbook_type -->
<g id="edge17" class="edge">
<title>logbook&#45;&gt;logbook_type</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1079.74,-391.18C1089.13,-291.04 1102.09,-152.9 1108.72,-82.29"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1111.49,-82.78 1109.45,-74.56 1105.91,-82.26 1111.49,-82.78"/>
</g>
<!-- role -->
<g id="node14" class="node">
<title>role</title>
<path fill="none" stroke="black" d="M2055.95,-1656.37C2055.95,-1656.37 2176.03,-1656.37 2176.03,-1656.37 2182.03,-1656.37 2188.03,-1662.37 2188.03,-1668.37 2188.03,-1668.37 2188.03,-1716.39 2188.03,-1716.39 2188.03,-1722.39 2182.03,-1728.39 2176.03,-1728.39 2176.03,-1728.39 2055.95,-1728.39 2055.95,-1728.39 2049.95,-1728.39 2043.95,-1722.39 2043.95,-1716.39 2043.95,-1716.39 2043.95,-1668.37 2043.95,-1668.37 2043.95,-1662.37 2049.95,-1656.37 2055.95,-1656.37"/>
<text text-anchor="start" x="2101.36" y="-1708.66" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">role</text>
<polyline fill="none" stroke="black" points="2043.95,-1698.63 2188.03,-1698.63"/>
<text text-anchor="start" x="2050.99" y="-1683.35" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="2071.24" y="-1683.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="2050.99" y="-1665.1" font-family="Helvetica,sans-Serif" font-size="12.00">name </text>
<text text-anchor="start" x="2089.24" y="-1665.1" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- user_role -->
<g id="node15" class="node">
<title>user_role</title>
<path fill="none" stroke="black" d="M1713.26,-1155.87C1713.26,-1155.87 1833.34,-1155.87 1833.34,-1155.87 1839.34,-1155.87 1845.34,-1161.87 1845.34,-1167.87 1845.34,-1167.87 1845.34,-1215.89 1845.34,-1215.89 1845.34,-1221.89 1839.34,-1227.89 1833.34,-1227.89 1833.34,-1227.89 1713.26,-1227.89 1713.26,-1227.89 1707.26,-1227.89 1701.26,-1221.89 1701.26,-1215.89 1701.26,-1215.89 1701.26,-1167.87 1701.26,-1167.87 1701.26,-1161.87 1707.26,-1155.87 1713.26,-1155.87"/>
<text text-anchor="start" x="1739.18" y="-1208.16" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">user_role</text>
<polyline fill="none" stroke="black" points="1701.26,-1198.13 1845.34,-1198.13"/>
<text text-anchor="start" x="1708.3" y="-1182.85" font-family="Helvetica,sans-Serif" font-size="12.00">user_id </text>
<text text-anchor="start" x="1754.8" y="-1182.85" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1708.3" y="-1164.6" font-family="Helvetica,sans-Serif" font-size="12.00">role_id </text>
<text text-anchor="start" x="1751.05" y="-1164.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
</g>
<!-- user_role&#45;&gt;user -->
<g id="edge22" class="edge">
<title>user_role&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1700.78,-1185.82C1560.23,-1174.08 1250.92,-1148.23 1095.49,-1135.24"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="1095.95,-1132.47 1087.75,-1134.59 1095.48,-1138.05 1095.95,-1132.47"/>
</g>
<!-- user_role&#45;&gt;role -->
<g id="edge21" class="edge">
<title>user_role&#45;&gt;role</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1798.22,-1228.28C1859.85,-1318.3 2019.25,-1551.1 2085.95,-1648.51"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="2083.56,-1649.98 2090.39,-1655 2088.18,-1646.81 2083.56,-1649.98"/>
</g>
<!-- boathouse -->
<g id="node17" class="node">
<title>boathouse</title>
<path fill="none" stroke="black" d="M1461.39,-327.62C1461.39,-327.62 1581.47,-327.62 1581.47,-327.62 1587.47,-327.62 1593.47,-333.62 1593.47,-339.62 1593.47,-339.62 1593.47,-442.39 1593.47,-442.39 1593.47,-448.39 1587.47,-454.39 1581.47,-454.39 1581.47,-454.39 1461.39,-454.39 1461.39,-454.39 1455.39,-454.39 1449.39,-448.39 1449.39,-442.39 1449.39,-442.39 1449.39,-339.62 1449.39,-339.62 1449.39,-333.62 1455.39,-327.62 1461.39,-327.62"/>
<text text-anchor="start" x="1483.18" y="-434.66" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">boathouse</text>
<polyline fill="none" stroke="black" points="1449.39,-424.63 1593.47,-424.63"/>
<text text-anchor="start" x="1456.43" y="-409.35" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="1476.68" y="-409.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1456.43" y="-391.1" font-family="Helvetica,sans-Serif" font-size="12.00">boat_id </text>
<text text-anchor="start" x="1503.68" y="-391.1" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="1456.43" y="-372.85" font-family="Helvetica,sans-Serif" font-size="12.00">aisle </text>
<text text-anchor="start" x="1487.18" y="-372.85" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1456.43" y="-354.6" font-family="Helvetica,sans-Serif" font-size="12.00">side </text>
<text text-anchor="start" x="1484.18" y="-354.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="1456.43" y="-336.35" font-family="Helvetica,sans-Serif" font-size="12.00">level </text>
<text text-anchor="start" x="1487.93" y="-336.35" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
</g>
<!-- boathouse&#45;&gt;boat -->
<g id="edge23" class="edge">
<title>boathouse&#45;&gt;boat</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M1453.7,-454.77C1389.44,-511.19 1287.49,-590.65 1183.49,-630.93 1104.56,-661.5 1010.68,-673.83 934.33,-678.38"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="934.3,-675.58 926.47,-678.82 934.62,-681.17 934.3,-675.58"/>
</g>
<!-- notification -->
<g id="node18" class="node">
<title>notification</title>
<path fill="none" stroke="black" d="M523.13,-247.96C523.13,-247.96 643.21,-247.96 643.21,-247.96 649.21,-247.96 655.21,-253.96 655.21,-259.96 655.21,-259.96 655.21,-399.23 655.21,-399.23 655.21,-405.23 649.21,-411.23 643.21,-411.23 643.21,-411.23 523.13,-411.23 523.13,-411.23 517.13,-411.23 511.13,-405.23 511.13,-399.23 511.13,-399.23 511.13,-259.96 511.13,-259.96 511.13,-253.96 517.13,-247.96 523.13,-247.96"/>
<text text-anchor="start" x="541.55" y="-391.5" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="13.00">notification</text>
<polyline fill="none" stroke="black" points="511.13,-381.47 655.21,-381.47"/>
<text text-anchor="start" x="518.17" y="-366.19" font-family="Helvetica,sans-Serif" font-size="12.00">id* </text>
<text text-anchor="start" x="538.42" y="-366.19" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="518.17" y="-347.94" font-family="Helvetica,sans-Serif" font-size="12.00">user_id </text>
<text text-anchor="start" x="564.67" y="-347.94" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">integer</text>
<text text-anchor="start" x="518.17" y="-329.69" font-family="Helvetica,sans-Serif" font-size="12.00">message </text>
<text text-anchor="start" x="575.92" y="-329.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="518.17" y="-311.44" font-family="Helvetica,sans-Serif" font-size="12.00">read_at </text>
<text text-anchor="start" x="566.92" y="-311.44" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="518.17" y="-293.19" font-family="Helvetica,sans-Serif" font-size="12.00">created_at </text>
<text text-anchor="start" x="585.67" y="-293.19" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">datetime</text>
<text text-anchor="start" x="518.17" y="-274.94" font-family="Helvetica,sans-Serif" font-size="12.00">category </text>
<text text-anchor="start" x="575.17" y="-274.94" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
<text text-anchor="start" x="518.17" y="-256.69" font-family="Helvetica,sans-Serif" font-size="12.00">link </text>
<text text-anchor="start" x="542.17" y="-256.69" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00">text</text>
</g>
<!-- notification&#45;&gt;user -->
<g id="edge24" class="edge">
<title>notification&#45;&gt;user</title>
<path fill="none" stroke="black" stroke-width="0.9" d="M578.8,-411.63C577.06,-509.73 585.42,-676.25 650.41,-800.44 710.85,-915.96 828.71,-1012.32 911.35,-1069.85"/>
<polygon fill="black" stroke="black" stroke-width="0.9" points="909.44,-1071.94 917.62,-1074.18 912.63,-1067.33 909.44,-1071.94"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 48 KiB

5
fd Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
scp read@128.140.64.118:/home/rowing/db.sqlite db.sqlite
#sqlite3 db.sqlite < seeds.sql

5
frontend/.gitignore vendored
View File

@ -1 +1,6 @@
package-lock.json
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

View File

@ -1,77 +1,76 @@
import * as d3 from 'd3';
import * as d3 from "d3";
export interface Data {
date: Date;
km: number;
}
if(sessionStorage.getItem('userStats')) {
const data = JSON.parse(sessionStorage.getItem('userStats') || '{}') as Data[];
if (sessionStorage.getItem("userStats")) {
const data = JSON.parse(
sessionStorage.getItem("userStats") || "{}",
) as Data[];
if(data.length >= 2) {
if (data.length >= 2) {
const margin = { top: 20, right: 20, bottom: 50, left: 50 };
const width: number = 960 - margin.left - margin.right;
const height: number = 500 - margin.top - margin.bottom;
data.forEach((d: Data) => {
d.date = <Date> new Date(d.date)
d.date = <Date>new Date(d.date);
d.km = +d.km;
});
const x = d3.scaleTime()
const x = d3
.scaleTime()
.domain(<[Date, Date]>d3.extent(data, (d: Data) => d.date))
.range([0, width]);
const y = d3.scaleLinear()
const y = d3
.scaleLinear()
.domain([0, Number(d3.max(data, (d: Data) => d.km))])
.range([height, 0]);
const line = d3.line<Data>()
const line = d3
.line<Data>()
.x((d: Data) => x(d.date))
.y((d: Data) => y(d.km));
const svg = d3.select('#container')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
const svg = d3
.select("#container")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(responsivefy)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
svg.append('path')
.data([data])
.attr('class', 'line')
.attr('d', line);
svg.append('g')
.attr('transform', `translate(0,${height})`)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svg.append("path").data([data]).attr("class", "line").attr("d", line);
svg
.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
svg.append('g')
.call(d3.axisLeft(y));
svg.append("g").call(d3.axisLeft(y));
}
}
function responsivefy(svg: any) {
const container = d3.select(svg.node().parentNode);
const width = parseInt(svg.style('width'), 10);
const height = parseInt(svg.style('height'), 10);
const width = parseInt(svg.style("width"), 10);
const height = parseInt(svg.style("height"), 10);
const aspect = width / height;
svg.attr('viewBox', `0 0 ${width} ${height}`)
.attr('preserveAspectRatio', 'xMinYMid')
svg
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("preserveAspectRatio", "xMinYMid")
.call(resize);
d3.select(window).on(
'resize.' + container.attr('id'),
resize
);
d3.select(window).on("resize." + container.attr("id"), resize);
function resize() {
const w = parseInt(container.style('width'));
svg.attr('width', w);
svg.attr('height', Math.round(w / aspect));
const w = parseInt(container.style("width"));
svg.attr("width", w);
svg.attr("height", Math.round(w / aspect));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,9 @@
"preview": "vite preview"
},
"devDependencies": {
"@playwright/test": "^1.40.1",
"@types/d3": "^7.4.1",
"@types/node": "^20.11.4",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.21",
"sass": "^1.60.0",

View File

@ -0,0 +1,77 @@
import { defineConfig, devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://127.0.0.1:8000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
//{
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
//},
/* Test against mobile viewports. */
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
//{
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
//},
/* Test against branded browsers. */
//{
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
//},
//{
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
//},
],
/* Run your local dev server before starting the tests */
webServer: {
timeout: 15 * 60 * 1000,
command: 'cd .. && ./test_db.sh && cargo r',
url: 'http://127.0.0.1:8000'
},
});

View File

@ -12,3 +12,5 @@
@import 'components/chart';
@import 'components/search';
@import 'components/important';
@import 'components/searchable-table';
@import 'components/notification';

View File

@ -0,0 +1,5 @@
.notification {
right: -.2rem;
top: -.1rem;
font-size: .5rem;
}

View File

@ -0,0 +1,178 @@
/*!
* JSTable v1.6.5
*/
.dt-container{
position:relative;
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
overflow-y: hidden;
.dt-message {
text-align: center;
}
.dt-loading{
position: absolute;
top: 50%;
left: 50%;
width: 100%;
margin-left: -50%;
margin-top: -20px;
height: 40px;
text-align: center;
background-color: white;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);
}
}
.dt-top,
.dt-bottom {
padding: 8px 10px;
display:flex;
justify-content:space-between;
.dt-info {
margin: 7px 0;
}
}
/* PAGER */
.dt-pagination {
ul {
margin: 0;
padding-left: 0;
li {
list-style: none;
float: left;
}
}
a, span{
border: 1px solid transparent;
float: left;
margin-left: 2px;
padding: 6px 12px;
position: relative;
text-decoration: none;
color: inherit;
}
a:hover {
background-color: #d9d9d9;
}
.active a{
&, &:focus, &:hover{
background-color: #d9d9d9;
cursor: default;
}
}
.dt-ellipsis span{
cursor: not-allowed;
}
.disabled a{
&, &:focus, &:hover{
cursor: not-allowed;
opacity: 0.4;
}
}
.pager a {
font-weight: bold;
}
}
.dt-table {
max-width: 100%;
width: 100%;
border-spacing: 0;
& > tbody, > tfoot, > thead{
& > tr{
& > td, & > th{
vertical-align: top;
padding: 8px 10px;
white-space: nowrap;
}
}
}
& > thead > tr{
& > th, & > td{
vertical-align: bottom;
text-align: left;
border-bottom: 1px solid #d9d9d9;
}
}
& > tfoot > tr{
& > th, & > td{
vertical-align: bottom;
text-align: left;
border-top: 1px solid #d9d9d9;
}
}
th {
vertical-align: bottom;
text-align: left;
&.dt-sorter {
position: relative;
cursor: pointer;
padding-right:20px;
&::before,
&::after {
content: "";
height: 0;
width: 0;
position: absolute;
right: 7px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
opacity: 0.2;
}
&::before {
border-top: 4px solid #000;
top: 18px;
}
&::after {
border-bottom: 4px solid #000;
border-top: 4px solid transparent;
bottom: 22px;
}
&.asc::after,
&.desc::before {
opacity: 0.6;
}
}
}
}
.dt-loading.hidden{
display:none!important;
opacity:0!important;
}
.dt-input {
@extend .input;
}
.dt-selector {
@extend .input;
}

View File

@ -10,6 +10,7 @@
&.open {
display: block;
height: 100dvh;
height: 100vh;
right: 0;
top: 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 372 372" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(17.8229,1.77636e-15,-1.77636e-15,17.8229,-6843.65,-3821.15)">
<path d="M397.575,225.019C397.575,225.386 397.493,225.701 397.329,225.965C397.165,226.229 396.93,226.444 396.626,226.612C396.883,226.808 397.079,227.039 397.211,227.307C397.344,227.574 397.411,227.903 397.411,228.294C397.411,228.646 397.339,228.957 397.197,229.229C397.054,229.5 396.852,229.729 396.59,229.914C396.329,230.1 396.013,230.24 395.644,230.336C395.275,230.432 394.862,230.48 394.405,230.48C394.01,230.48 393.623,230.439 393.242,230.359C392.861,230.279 392.521,230.145 392.222,229.955C391.923,229.766 391.683,229.514 391.502,229.199C391.32,228.885 391.229,228.493 391.229,228.024L392.858,228.019C392.858,228.249 392.903,228.438 392.993,228.584C393.083,228.73 393.201,228.847 393.347,228.933C393.494,229.019 393.66,229.078 393.845,229.111C394.031,229.145 394.217,229.161 394.405,229.161C394.854,229.161 395.197,229.083 395.433,228.927C395.669,228.771 395.788,228.562 395.788,228.3C395.788,228.171 395.76,228.056 395.706,227.954C395.651,227.853 395.556,227.758 395.421,227.67C395.287,227.582 395.107,227.496 394.882,227.412C394.658,227.328 394.376,227.237 394.036,227.14C393.598,227.022 393.208,226.895 392.864,226.756C392.52,226.617 392.228,226.452 391.988,226.261C391.748,226.069 391.564,225.845 391.437,225.587C391.31,225.329 391.247,225.022 391.247,224.667C391.247,224.308 391.33,223.994 391.496,223.727C391.662,223.459 391.897,223.239 392.202,223.067C391.944,222.868 391.748,222.635 391.613,222.367C391.478,222.1 391.411,221.771 391.411,221.38C391.411,221.044 391.482,220.738 391.625,220.463C391.767,220.188 391.97,219.953 392.234,219.76C392.498,219.566 392.816,219.417 393.189,219.312C393.562,219.206 393.977,219.153 394.434,219.153C394.903,219.153 395.325,219.208 395.7,219.317C396.075,219.427 396.392,219.588 396.652,219.801C396.912,220.014 397.112,220.276 397.252,220.589C397.393,220.901 397.463,221.259 397.463,221.661L395.829,221.661C395.829,221.493 395.799,221.337 395.741,221.192C395.682,221.048 395.594,220.922 395.477,220.814C395.36,220.707 395.214,220.622 395.041,220.56C394.867,220.497 394.665,220.466 394.434,220.466C394.192,220.466 393.983,220.49 393.807,220.539C393.631,220.588 393.487,220.653 393.374,220.735C393.26,220.817 393.177,220.913 393.125,221.022C393.072,221.132 393.045,221.247 393.045,221.368C393.045,221.517 393.07,221.644 393.119,221.749C393.168,221.855 393.256,221.951 393.385,222.039C393.514,222.127 393.691,222.21 393.916,222.288C394.14,222.366 394.428,222.452 394.78,222.546C395.225,222.663 395.622,222.791 395.969,222.93C396.317,223.068 396.61,223.233 396.848,223.425C397.086,223.616 397.267,223.841 397.39,224.099C397.513,224.356 397.575,224.663 397.575,225.019ZM394.206,223.917C393.901,223.835 393.62,223.747 393.362,223.653C393.19,223.735 393.065,223.852 392.987,224.002C392.909,224.152 392.87,224.323 392.87,224.515C392.87,224.671 392.894,224.805 392.943,224.916C392.992,225.027 393.081,225.13 393.21,225.224C393.338,225.317 393.515,225.406 393.74,225.49C393.964,225.574 394.252,225.669 394.604,225.774C394.756,225.817 394.903,225.859 395.044,225.9C395.184,225.941 395.321,225.985 395.454,226.032C395.622,225.942 395.751,225.823 395.84,225.675C395.93,225.526 395.975,225.358 395.975,225.171C395.975,225.03 395.947,224.905 395.89,224.796C395.834,224.687 395.737,224.583 395.6,224.485C395.463,224.388 395.282,224.294 395.055,224.204C394.829,224.114 394.545,224.019 394.206,223.917Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(-0.695637,0.718394,-0.718394,-0.695637,185.736,185.735)">
<circle cx="0" cy="0" r="185.772" style="fill:url(#_Linear1);"/>
</g>
<g transform="matrix(0.291595,0,0,0.291595,185.736,185.735)">
<g transform="matrix(1,0,0,1,-291.5,-512)">
<clipPath id="_clip2">
<rect x="0" y="0" width="583" height="1024"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g transform="matrix(1,0,0,1,-1574,-536.199)">
<g transform="matrix(1,0,0,1,0,10.8235)">
<g transform="matrix(0.948324,0.317305,0.307947,-0.920356,-304.665,1886.18)">
<rect x="1838.29" y="1006.52" width="17.353" height="644.204" style="fill:white;"/>
</g>
<path d="M1944.68,934.772C1921.09,896.523 1932.18,782.181 1974.56,655.531C1990.23,608.676 2009.04,563.787 2029.1,525.376L2156.88,568.132C2149.78,610.876 2137.79,658.046 2122.11,704.901C2079.26,832.966 2018.43,931.728 1976.52,946.409L2085.08,568.504L1944.68,934.772Z" style="fill:white;"/>
</g>
<g transform="matrix(-1,0,0,1,3730.88,10.8235)">
<g transform="matrix(0.948324,0.317305,0.307947,-0.920356,-304.665,1886.18)">
<rect x="1838.29" y="1006.52" width="17.353" height="644.204" style="fill:white;"/>
</g>
<path d="M1944.68,934.772C1921.09,896.523 1932.18,782.181 1974.56,655.531C1990.23,608.676 2009.04,563.787 2029.1,525.376L2156.88,568.132C2149.78,610.876 2137.79,658.046 2122.11,704.901C2079.26,832.966 2018.43,931.728 1976.52,946.409L2085.08,568.504L1944.68,934.772Z" style="fill:white;"/>
</g>
</g>
</g>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(371.543,-2.84217e-14,2.84217e-14,371.543,-185.772,1.13687e-13)"><stop offset="0" style="stop-color:rgb(131,0,0);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(255,0,0);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

4
frontend/static/jstable.min.js vendored Normal file
View File

@ -0,0 +1,4 @@
/*!
* JSTable v1.6.5
*/
const JSTableDefaultConfig={perPage:5,perPageSelect:[5,10,15,20,25],sortable:!0,searchable:!0,nextPrev:!0,firstLast:!1,prevText:"&lsaquo;",nextText:"&rsaquo;",firstText:"&laquo;",lastText:"&raquo;",ellipsisText:"&hellip;",truncatePager:!0,pagerDelta:2,classes:{top:"dt-top",info:"dt-info",input:"dt-input",table:"dt-table",bottom:"dt-bottom",search:"dt-search",sorter:"dt-sorter",wrapper:"dt-wrapper",dropdown:"dt-dropdown",ellipsis:"dt-ellipsis",selector:"dt-selector",container:"dt-container",pagination:"dt-pagination",loading:"dt-loading",message:"dt-message"},labels:{placeholder:"Search...",perPage:"{select} entries per page",noRows:"No entries found",info:"Showing {start} to {end} of {rows} entries",loading:"Loading...",infoFiltered:"Showing {start} to {end} of {rows} entries (filtered from {rowsTotal} entries)"},layout:{top:"{select}{search}",bottom:"{info}{pager}"},serverSide:!1,deferLoading:null,ajax:null,ajaxParams:{},queryParams:{page:"page",search:"search",sortColumn:"sortColumn",sortDirection:"sortDirection",perPage:"perPage"},addQueryParams:!0,rowAttributesCreator:null,searchDelay:null,method:"GET"};class JSTable{constructor(e,t={}){let s=e;"string"==typeof e&&(s=document.querySelector(e)),null!==s&&(this.config=this._merge(JSTableDefaultConfig,t),this.table=new JSTableElement(s),this.currentPage=1,this.columnRenderers=[],this.columnsNotSearchable=[],this.searchQuery=null,this.sortColumn=null,this.sortDirection="asc",this.isSearching=!1,this.dataCount=null,this.filteredDataCount=null,this.searchTimeout=null,this.pager=new JSTablePager(this),this._build(),this._buildColumns(),this.update(null===this.config.deferLoading),this._bindEvents(),this._emit("init"),this._parseQueryParams())}_build(){let e=this.config;this.wrapper=document.createElement("div"),this.wrapper.className=e.classes.wrapper;var t=["<div class='",e.classes.top,"'>",e.layout.top,"</div>","<div class='",e.classes.container,"'>","<div class='",e.classes.loading," hidden'>",e.labels.loading,"</div>","</div>","<div class='",e.classes.bottom,"'>",e.layout.bottom,"</div>"].join("");if(t=t.replace("{info}","<div class='"+e.classes.info+"'></div>"),e.perPageSelect){var s=["<div class='",e.classes.dropdown,"'>","<label>",e.labels.perPage,"</label>","</div>"].join(""),a=document.createElement("select");a.className=e.classes.selector,e.perPageSelect.forEach((function(t){var s=t===e.perPage,r=new Option(t,t,s,s);a.add(r)})),s=s.replace("{select}",a.outerHTML),t=t.replace(/\{select\}/g,s)}else t=t.replace(/\{select\}/g,"");if(e.searchable){var r=["<div class='",e.classes.search,"'>","<input class='",e.classes.input,"' placeholder='",e.labels.placeholder,"' type='text'>","</div>"].join("");t=t.replace(/\{search\}/g,r)}else t=t.replace(/\{search\}/g,"");this.table.element.classList.add(e.classes.table),t=t.replace("{pager}","<div class='"+e.classes.pagination+"'></div>"),this.wrapper.innerHTML=t,this.table.element.parentNode.replaceChild(this.wrapper,this.table.element),this.wrapper.querySelector("."+e.classes.container).appendChild(this.table.element),this._updatePagination(),this._updateInfo()}async update(e=!0){var t=this;this.currentPage>this.pager.getPages()&&(this.currentPage=this.pager.getPages());let s=t.wrapper.querySelector(" ."+t.config.classes.loading);if(s.classList.remove("hidden"),this.table.header.getCells().forEach((function(e,s){let a=t.table.head.rows[0].cells[s];a.innerHTML=e.getInnerHTML(),e.classes.length>0&&(a.className=e.classes.join(" "));for(let t in e.attributes)a.setAttribute(t,e.attributes[t]);a.setAttribute("data-sortable",e.isSortable)})),e)return this.getPageData(this.currentPage).then((function(e){t.table.element.classList.remove("hidden"),t.table.body.innerHTML="",e.forEach((function(e){t.table.body.appendChild(e.getFormatted(t.columnRenderers,t.config.rowAttributesCreator))})),s.classList.add("hidden")})).then((function(){t.getDataCount()<=0&&(t.wrapper.classList.remove("search-results"),t.setMessage(t.config.labels.noRows)),t._emit("update")})).then((function(){t._updatePagination(),t._updateInfo()}));t.table.element.classList.remove("hidden"),t.table.body.innerHTML="",this.getDataCount()<=0&&(t.wrapper.classList.remove("search-results"),t.setMessage(t.config.labels.noRows)),this._getData().forEach((function(e){t.table.body.appendChild(e.getFormatted(t.columnRenderers,t.config.rowAttributesCreator))})),s.classList.add("hidden")}_updatePagination(){let e=this.wrapper.querySelector(" ."+this.config.classes.pagination);e.innerHTML="",e.appendChild(this.pager.render(this.currentPage))}_updateInfo(){let e=this.wrapper.querySelector(" ."+this.config.classes.info),t=this.isSearching?this.config.labels.infoFiltered:this.config.labels.info;if(e&&t.length){var s=t.replace("{start}",this.getDataCount()>0?this._getPageStartIndex()+1:0).replace("{end}",this._getPageEndIndex()+1).replace("{page}",this.currentPage).replace("{pages}",this.pager.getPages()).replace("{rows}",this.getDataCount()).replace("{rowsTotal}",this.getDataCountTotal());e.innerHTML=s}}_getPageStartIndex(){return(this.currentPage-1)*this.config.perPage}_getPageEndIndex(){let e=this.currentPage*this.config.perPage-1;return e>this.getDataCount()-1?this.getDataCount()-1:e}_getData(){return this._emit("getData",this.table.dataRows),this.table.dataRows.filter((function(e){return e.visible}))}_fetchData(){var e=this;let t={searchQuery:this.searchQuery,sortColumn:this.sortColumn,sortDirection:this.sortDirection,start:this._getPageStartIndex(),length:this.config.perPage,datatable:1};t=Object.assign({},this.config.ajaxParams,t);let s=this.config.ajax+"?"+this._queryParams(t);return fetch(s,{method:this.config.method,credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json"}}).then((function(e){return e.json()})).then((function(t){return e._emit("fetchData",t),e.dataCount=t.recordsTotal,e.filteredDataCount=t.recordsFiltered,t.data})).then((function(e){let t=[];return e.forEach((function(e){t.push(JSTableRow.createFromData(e))})),t})).catch((function(e){console.error(e)}))}_queryParams(e){return Object.keys(e).map((t=>encodeURIComponent(t)+"="+encodeURIComponent(e[t]))).join("&")}getDataCount(){return this.isSearching?this.getDataCountFiltered():this.getDataCountTotal()}getDataCountFiltered(){return this.config.serverSide?this.filteredDataCount:this._getData().length}getDataCountTotal(){return this.config.serverSide?null!==this.config.deferLoading?this.config.deferLoading:this.dataCount:this.table.dataRows.length}getPageData(){if(this.config.serverSide)return this._fetchData();let e=this._getPageStartIndex();var t=this._getPageEndIndex();return Promise.resolve(this._getData()).then((function(s){return s.filter((function(s,a){return a>=e&&a<=t}))}))}async search(e){var t=this;if(this.searchQuery===e.toLowerCase())return!1;if(this.searchQuery=e.toLowerCase(),this.config.searchDelay){if(this.searchTimeout)return!1;this.searchTimeout=setTimeout((function(){t.searchTimeout=null}),this.config.searchDelay)}return this.currentPage=1,this.isSearching=!0,this.searchQuery.length?(this.config.serverSide||this.table.dataRows.forEach((function(e){e.visible=!1,t.searchQuery.split(" ").reduce((function(s,a){var r;let i=e.getCells();return i=i.filter((function(e,s){if(t.columnsNotSearchable.indexOf(s)<0)return!0})),r=i.some((function(e,t){if(e.getTextContent().toLowerCase().indexOf(a)>=0)return!0})),s&&r}),!0)&&(e.visible=!0)})),this.wrapper.classList.add("search-results"),this.update().then((function(){t._emit("search",e)}))):(this.table.dataRows.forEach((function(e){e.visible=!0})),this.isSearching=!1,t.wrapper.classList.remove("search-results"),t.update(),!1)}sort(e,t,s=!1){var a=this;if(this.sortColumn=e||0,this.sortDirection=t,this.sortColumn<0||this.sortColumn>this.table.getColumnCount()-1)return!1;var r=this.table.header.getCell(this.sortColumn),i=this.table.dataRows;this.table.header.getCells().forEach((function(e){e.removeClass("asc"),e.removeClass("desc")})),r.addClass(this.sortDirection),this.config.serverSide||(i=i.sort((function(e,t){var s=e.getCellTextContent(a.sortColumn).toLowerCase(),r=t.getCellTextContent(a.sortColumn).toLowerCase();return s=s.replace(/(\$|\,|\s|%)/g,""),r=r.replace(/(\$|\,|\s|%)/g,""),s=isNaN(s)||""===s?s:parseFloat(s),r=isNaN(r)||""===r?r:parseFloat(r),""===s&&""!==r||!isNaN(s)&&isNaN(r)?"asc"===a.sortDirection?1:-1:""!==s&&""===r||isNaN(s)&&!isNaN(r)?"asc"===a.sortDirection?-1:1:"asc"===a.sortDirection?s===r?0:s>r?1:-1:s===r?0:s<r?1:-1})),this.table.dataRows=i),this.config.serverSide&&s||this.update(),this._emit("sort",this.sortColumn,this.sortDirection)}async paginate(e){var t=this;return this.currentPage=e,this.update().then((function(){t._emit("paginate",t.currentPage,e)}))}_setQueryParam(e,t){if(!this.config.addQueryParams)return;const s=new URL(window.location.href);s.searchParams.set(this.config.queryParams[e],t),window.history.replaceState(null,null,s)}_bindEvents(){var e=this;this.wrapper.addEventListener("click",(function(t){var s=t.target;if(s.hasAttribute("data-page")){t.preventDefault();let a=parseInt(s.getAttribute("data-page"),10);e.paginate(a),e._setQueryParam("page",a)}if("TH"===s.nodeName&&s.hasAttribute("data-sortable")){if("false"===s.getAttribute("data-sortable"))return!1;t.preventDefault();let a=s.classList.contains("asc")?"desc":"asc";e.sort(s.cellIndex,a),e._setQueryParam("sortColumn",s.cellIndex),e._setQueryParam("sortDirection",a)}})),this.config.perPageSelect&&this.wrapper.addEventListener("change",(function(t){var s=t.target;if("SELECT"===s.nodeName&&s.classList.contains(e.config.classes.selector)){t.preventDefault();let a=parseInt(s.value,10);e._emit("perPageChange",e.config.perPage,a),e.config.perPage=a,e.update(),e._setQueryParam("perPage",a)}})),this.config.searchable&&this.wrapper.addEventListener("keyup",(function(t){"INPUT"===t.target.nodeName&&t.target.classList.contains(e.config.classes.input)&&(t.preventDefault(),e.search(t.target.value),e._setQueryParam("search",t.target.value))}))}on(e,t){this.events=this.events||{},this.events[e]=this.events[e]||[],this.events[e].push(t)}off(e,t){this.events=this.events||{},e in this.events!=!1&&this.events[e].splice(this.events[e].indexOf(t),1)}_emit(e){if(this.events=this.events||{},e in this.events!=!1)for(var t=0;t<this.events[e].length;t++)this.events[e][t].apply(this,Array.prototype.slice.call(arguments,1))}setMessage(e){var t=this.table.getColumnCount(),s=document.createElement("tr");s.innerHTML='<td class="'+this.config.classes.message+'" colspan="'+t+'">'+e+"</td>",this.table.body.innerHTML="",this.table.body.appendChild(s)}_buildColumns(){var e=this;let t=null,s=null;this.config.columns&&this.config.columns.forEach((function(a){isNaN(a.select)||(a.select=[a.select]),a.select.forEach((function(r){var i=e.table.header.getCell(r);if(void 0!==i){if(a.hasOwnProperty("render")&&"function"==typeof a.render&&(e.columnRenderers[r]=a.render),a.hasOwnProperty("sortable")){let r=!1;i.hasSortable?r=i.isSortable:(r=a.sortable,i.setSortable(r)),r&&(i.addClass(e.config.classes.sorter),a.hasOwnProperty("sort")&&1===a.select.length&&(t=a.select[0],s=a.sort))}a.hasOwnProperty("searchable")&&(i.addAttribute("data-searchable",a.searchable),!1===a.searchable&&e.columnsNotSearchable.push(r))}}))})),this.table.header.getCells().forEach((function(a,r){null===a.isSortable&&a.setSortable(e.config.sortable),a.isSortable&&(a.addClass(e.config.classes.sorter),a.hasSort&&(t=r,s=a.sortDirection))})),null!==t&&e.sort(t,s,!0)}_merge(e,t){var s=this;return Object.keys(e).forEach((function(a){!t.hasOwnProperty(a)||"object"!=typeof t[a]||t[a]instanceof Array||null===t[a]?t.hasOwnProperty(a)||(t[a]=e[a]):s._merge(e[a],t[a])})),t}async _parseQueryParams(){const e=new URLSearchParams(window.location.search);let t=e.get(this.config.queryParams.perPage);if(t){t=parseInt(t),this.config.perPage=t,this.wrapper.querySelectorAll("."+this.config.classes.selector).forEach((function(e){e.querySelectorAll("option").forEach((e=>e.removeAttribute("selected"))),e.value=t,e.querySelector(`option[value='${t}']`).setAttribute("selected","")})),this.update()}let s=e.get(this.config.queryParams.search);if(s){this.wrapper.querySelectorAll("."+this.config.classes.input).forEach((function(e){e.value=s})),await this.search(s)}let a=e.get(this.config.queryParams.page);a&&await this.paginate(parseInt(a));let r=e.get(this.config.queryParams.sortColumn);if(r){r=parseInt(r);let t=e.get(this.config.queryParams.sortDirection);t=null==t?"asc":t,this.sort(r,t)}}}class JSTableElement{constructor(e){this.element=e,this.body=this.element.tBodies[0],this.head=this.element.tHead,this.rows=Array.from(this.element.rows).map((function(e,t){return new JSTableRow(e,e.parentNode.nodeName,t)})),this.dataRows=this._getBodyRows(),this.header=this._getHeaderRow()}_getBodyRows(){return this.rows.filter((function(e){return!e.isHeader&&!e.isFooter}))}_getHeaderRow(){return this.rows.find((function(e){return e.isHeader}))}getColumnCount(){return this.header.getColumnCount()}getFooterRow(){return this.rows.find((function(e){return e.isFooter}))}}class JSTableRow{constructor(e,t="",s=null){this.cells=Array.from(e.cells).map((function(e){return new JSTableCell(e)})),this.d=this.cells.length,this.isHeader="THEAD"===t,this.isFooter="TFOOT"===t,this.visible=!0,this.rowID=s;var a=this;this.attributes={},[...e.attributes].forEach((function(e){a.attributes[e.name]=e.value}))}getCells(){return Array.from(this.cells)}getColumnCount(){return this.cells.length}getCell(e){return this.cells[e]}getCellTextContent(e){return this.getCell(e).getTextContent()}static createFromData(e){let t=document.createElement("tr");if(e.hasOwnProperty("data")){if(e.hasOwnProperty("attributes"))for(const s in e.attributes)t.setAttribute(s,e.attributes[s]);e=e.data}return e.forEach((function(e){let s=document.createElement("td");if(s.innerHTML=e&&e.hasOwnProperty("data")?e.data:e,e&&e.hasOwnProperty("attributes"))for(const t in e.attributes)s.setAttribute(t,e.attributes[t]);t.appendChild(s)})),new JSTableRow(t)}getFormatted(e,t=null){let s=document.createElement("tr");var a=this;for(let e in this.attributes)s.setAttribute(e,this.attributes[e]);let r=t?t.call(this,this.getCells()):{};for(const e in r)s.setAttribute(e,r[e]);return this.getCells().forEach((function(t,r){var i=document.createElement("td");i.innerHTML=t.getInnerHTML(),e.hasOwnProperty(r)&&(i.innerHTML=e[r].call(a,t.getElement(),r)),t.classes.length>0&&(i.className=t.classes.join(" "));for(let e in t.attributes)i.setAttribute(e,t.attributes[e]);s.appendChild(i)})),s}setCellClass(e,t){this.cells[e].addClass(t)}}class JSTableCell{constructor(e){this.textContent=e.textContent,this.innerHTML=e.innerHTML,this.className="",this.element=e,this.hasSortable=e.hasAttribute("data-sortable"),this.isSortable=this.hasSortable?"true"===e.getAttribute("data-sortable"):null,this.hasSort=e.hasAttribute("data-sort"),this.sortDirection=e.getAttribute("data-sort"),this.classes=[];var t=this;this.attributes={},[...e.attributes].forEach((function(e){t.attributes[e.name]=e.value}))}getElement(){return this.element}getTextContent(){return this.textContent}getInnerHTML(){return this.innerHTML}setClass(e){this.className=e}setSortable(e){this.isSortable=e}addClass(e){this.classes.push(e)}removeClass(e){this.classes.indexOf(e)>=0&&this.classes.splice(this.classes.indexOf(e),1)}addAttribute(e,t){this.attributes[e]=t}}class JSTablePager{constructor(e){this.instance=e}getPages(){let e=Math.ceil(this.instance.getDataCount()/this.instance.config.perPage);return 0===e?1:e}render(){var e=this.instance.config;let t=this.getPages(),s=document.createElement("ul");if(t>1){let a=1===this.instance.currentPage?1:this.instance.currentPage-1,r=this.instance.currentPage===t?t:this.instance.currentPage+1;e.firstLast&&s.appendChild(this.createItem("pager",1,e.firstText)),e.nextPrev&&s.appendChild(this.createItem("pager",a,e.prevText)),this.truncate().forEach((function(e){s.appendChild(e)})),e.nextPrev&&s.appendChild(this.createItem("pager",r,e.nextText)),e.firstLast&&s.appendChild(this.createItem("pager",t,e.lastText))}return s}createItem(e,t,s,a){let r=document.createElement("li");return r.className=e,r.innerHTML=a?"<span>"+s+"</span>":'<a href="#" data-page="'+t+'">'+s+"</a>",r}isValidPage(e){return e>0&&e<=this.getPages()}truncate(){var e,t=this,s=t.instance.config,a=2*s.pagerDelta,r=t.instance.currentPage,i=r-s.pagerDelta,n=r+s.pagerDelta,o=this.getPages(),l=[],c=[];if(this.instance.config.truncatePager){r<4-s.pagerDelta+a?n=3+a:r>this.getPages()-(3-s.pagerDelta+a)&&(i=this.getPages()-(2+a));for(var h=1;h<=o;h++)(1===h||h===o||h>=i&&h<=n)&&l.push(h);l.forEach((function(a){e&&(a-e==2?c.push(t.createItem("",e+1,e+1)):a-e!=1&&c.push(t.createItem(s.classes.ellipsis,0,s.ellipsisText,!0))),c.push(t.createItem(a==r?"active":"",a,a)),e=a}))}else for(let e=1;e<=this.getPages();e++)c.push(this.createItem(e===r?"active":"",e,e));return c}}window.JSTable=JSTable;

21
frontend/table.ts Normal file
View File

@ -0,0 +1,21 @@
// @ts-ignore
new JSTable('#basic', {
perPage: 100,
perPageSelect: [10,100],
// Customise the display text
labels: {
placeholder: 'Suchen (z.B. "Linz")',
perPage: '{select} per Seite',
noRows: 'Keine Einträge gefunden',
info: 'Zeigt {start} bis {end} von {rows} Einträgen',
loading: 'Laden...',
infoFiltered: 'Zeigt {start} bis {end} von {rows} Einträgen (gefiltert aus {rowsTotal} Einträgen)'
},
// Customise the layout
layout: {
top: '{search}{select}',
bottom: '{info}{pager}'
},
});

146
frontend/tests/cox.spec.ts Normal file
View File

@ -0,0 +1,146 @@
import { test, expect, Page } from "@playwright/test";
test("cox can create and delete trip", async ({ page }) => {
await page.goto("/auth");
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("cox");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.locator('li').filter({ hasText: 'Geplante Ausfahrten' }).getByRole('link').click();
await page.locator('a[href="#"]:has-text("Ausfahrt")').first().click();
await page.locator("#sidebar #planned_starting_time").click();
await page.locator("#sidebar #planned_starting_time").fill("18:00");
await page.locator("#sidebar #planned_starting_time").press("Tab");
await page.locator("#sidebar #planned_starting_time").press("Tab");
await page.getByRole("spinbutton").fill("5");
await page.getByRole("button", { name: "Erstellen", exact: true }).click();
await expect(page.locator("body")).toContainText("18:00 Uhr (cox) Details");
await page.goto("/planned");
await page.getByRole("link", { name: "Details" }).click();
await page.getByRole("link", { name: "Termin löschen" }).click();
await expect(page.locator("body")).toContainText("Erfolgreich gelöscht!");
});
// TODO: group -> cox can create trips
// TODO: cox can help/register at trips/events
test.describe("cox can edit trips", () => {
let sharedPage: Page;
test.beforeAll(async ({ browser }) => {
const page = await browser.newPage();
await page.goto("/auth");
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("cox");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.locator('li').filter({ hasText: 'Geplante Ausfahrten' }).getByRole('link').click();
await page.locator('a[href="#"]:has-text("Ausfahrt")').first().click();
await page.locator("#sidebar #planned_starting_time").click();
await page.locator("#sidebar #planned_starting_time").fill("18:00");
await page.locator("#sidebar #planned_starting_time").press("Tab");
await page.locator("#sidebar #planned_starting_time").press("Tab");
await page.getByRole("spinbutton").fill("5");
await page.getByRole("button", { name: "Erstellen", exact: true }).click();
sharedPage = page;
});
test("edit remarks", async () => {
await sharedPage.goto("/planned");
await sharedPage.getByRole("link", { name: "Details" }).click();
await sharedPage.locator("#sidebar #notes").click();
await sharedPage.locator("#sidebar #notes").fill("Meine Anmerkung");
await sharedPage.getByRole("button", { name: "Speichern" }).click();
await sharedPage.getByRole("link", { name: "Details" }).click();
await expect(sharedPage.locator("#sidebar")).toContainText(
"Meine Anmerkung",
);
await sharedPage
.getByRole("button", { name: "Ausfahrt erstellen schließen" })
.click();
});
test("add and remove guest", async () => {
await sharedPage.goto("/planned");
await sharedPage.getByRole("link", { name: "Details" }).click();
await sharedPage.locator("#sidebar #user_note").click();
await sharedPage.locator("#sidebar #user_note").fill("Mein Gast");
await sharedPage.getByRole("button", { name: "Gast hinzufügen" }).click();
await expect(sharedPage.locator("body")).toContainText(
"Erfolgreich angemeldet!",
);
await sharedPage.getByRole("link", { name: "Details" }).click();
await expect(sharedPage.locator("#sidebar")).toContainText(
"Freie Plätze: 4",
);
await expect(sharedPage.locator("#sidebar")).toContainText(
"Mein Gast (Gast) Abmelden",
);
await expect(
sharedPage.getByRole("link", { name: "Termin löschen" }),
).not.toBeVisible();
await sharedPage.getByRole("link", { name: "Abmelden" }).click();
await expect(sharedPage.locator("body")).toContainText(
"Erfolgreich abgemeldet!",
);
await sharedPage.getByRole("link", { name: "Details" }).click();
await expect(sharedPage.locator("#sidebar")).toContainText(
"Freie Plätze: 5",
);
await expect(sharedPage.locator("#sidebar")).toContainText(
"Keine Ruderer angemeldet",
);
await expect(
sharedPage.getByRole("link", { name: "Termin löschen" }),
).toBeVisible();
await sharedPage
.getByRole("button", { name: "Ausfahrt erstellen schließen" })
.click();
});
test("change amount rower", async () => {
await sharedPage.goto("/planned");
await sharedPage.getByRole("link", { name: "Details" }).click();
await expect(sharedPage.locator("#sidebar")).toContainText(
"Freie Plätze: 5",
);
await sharedPage.getByRole("spinbutton").click();
await sharedPage.getByRole("spinbutton").fill("3");
await sharedPage.getByRole("button", { name: "Speichern" }).click();
await expect(sharedPage.locator("body")).toContainText(
"Ausfahrt erfolgreich aktualisiert.",
);
});
test("call off trip", async () => {
await sharedPage.goto("/planned");
await sharedPage.getByRole("link", { name: "Details" }).click();
await expect(sharedPage.locator("#sidebar")).toContainText(
"Freie Plätze: 3",
);
await sharedPage.getByRole("spinbutton").click();
await sharedPage.getByRole("spinbutton").fill("0");
await sharedPage.getByRole("button", { name: "Speichern" }).click();
await expect(sharedPage.locator("body")).toContainText(
"Ausfahrt erfolgreich aktualisiert.",
);
await expect(sharedPage.locator("body")).toContainText("(Absage cox)");
});
test.afterAll(async () => {
await sharedPage.goto("/planned");
await sharedPage.getByRole("link", { name: "Details" }).click();
await sharedPage.getByRole("link", { name: "Termin löschen" }).click();
await sharedPage.close();
});
// TODO: 'Immer anzeigen' (also verify the functionality), 'Gesperrt' + type
});

192
frontend/tests/log.spec.ts Normal file
View File

@ -0,0 +1,192 @@
import { test, expect } from "@playwright/test";
test("Cox can start and cancel trip", async ({ page }, testInfo) => {
await page.goto("/auth");
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("cox2");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/");
await page.getByRole("link", { name: "Ausfahrt eintragen" }).click();
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "Joe" }).click();
} else {
await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click();
}
await page.getByLabel('Remove item: \'6\'').click(); // remove pre-filled cox2
await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden",
);
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await expect(page.locator("body")).toContainText("Joe");
await page.getByRole("link", { name: "Joe" }).click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole("link", { name: "Löschen" }).click();
});
test("Cox can start and finish trip", async ({ page }, testInfo) => {
await page.goto("/auth");
await page.getByPlaceholder("Name").click();
await page.getByPlaceholder("Name").fill("cox2");
await page.getByPlaceholder("Name").press("Tab");
await page.getByPlaceholder("Passwort").fill("cox");
await page.getByPlaceholder("Passwort").press("Enter");
await page.goto("/");
await page.getByRole("link", { name: "Ausfahrt eintragen" }).click();
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "Joe" }).click();
} else {
await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click();
}
await page.getByLabel('Remove item: \'6\'').click(); // remove pre-filled cox2
await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden",
);
// Trip starts 2 hours ago
const datetimeSelector = '#departure';
const currentValue = await page.$eval(datetimeSelector, el => el.value);
const currentDate = new Date(currentValue);
currentDate.setMinutes(currentDate.getMinutes());
currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2);
const newDatetime = currentDate.toISOString().slice(0, 16);
await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime);
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await expect(page.locator("body")).toContainText("Joe");
await page.goto("/log");
await page.locator("div:nth-child(2) > .border-0").click();
await page.getByRole("combobox", { name: "Destination" }).click();
await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim");
await page.getByRole("button", { name: "Ausfahrt beenden" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt korrekt eingetragen",
);
await page.goto('/log/show');
await expect(page.locator('body')).toContainText('Joe');
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
});
test("Kiosk can start and cancel trip", async ({ page }, testInfo) => {
await page.goto("/log/kiosk/ekrv2019/Linz");
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "Joe" }).click();
} else {
await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click();
}
await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden",
);
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await expect(page.locator("body")).toContainText("Joe");
await page.getByRole("link", { name: "Joe" }).click();
page.once("dialog", (dialog) => {
dialog.accept().catch(() => {});
});
await page.getByRole("link", { name: "Löschen" }).click();
});
test("Kiosk can start and finish trip", async ({ page }, testInfo) => {
await page.goto("/log/kiosk/ekrv2019/Linz");
if (testInfo.project.name.includes("Mobile")) {
// No left boat selector on mobile views
await page.getByText('-- Wähle ein Boot aus ---').nth(1).click();
await page.getByRole("option", { name: "Joe" }).click();
} else {
await page.getByText('2x', { exact: true }).click();
await page.getByText("Joe", { exact: true }).click();
}
await page.getByPlaceholder("Ruderer auswählen").click();
await page.getByRole("option", { name: "rower2" }).click();
await page.getByRole("option", { name: "cox2" }).click();
await expect(page.getByRole("listbox")).toContainText(
"Nur 2 Ruderer können hinzugefügt werden",
);
// Trip starts 2 hours ago
const datetimeSelector = '#departure';
const currentValue = await page.$eval(datetimeSelector, el => el.value);
const currentDate = new Date(currentValue);
currentDate.setMinutes(currentDate.getMinutes());
currentDate.setHours(currentDate.getHours() - new Date().getTimezoneOffset()/60 - 2);
const newDatetime = currentDate.toISOString().slice(0, 16);
await page.$eval(datetimeSelector, (el, value) => el.value = value, newDatetime);
await expect(page.locator("#shipmaster-newrowerjs")).toContainText("cox");
await expect(page.locator("#steering_person-newrowerjs")).toContainText(
"rower2 cox",
);
await page.getByRole("button", { name: "Ausfahrt eintragen" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt erfolgreich hinzugefügt",
);
await expect(page.locator("body")).toContainText("Joe");
await page.goto("/log");
await page.locator('div:nth-child(2) > .pt-2 > div > div > div:nth-child(2) > .border-0').click(); // 2 trips currently running, try to close second one
await page.getByRole("combobox", { name: "Destination" }).click();
await page.getByRole("combobox", { name: "Destination" }).fill("Ottensheim");
await page.getByRole("button", { name: "Ausfahrt beenden" }).click();
await expect(page.locator("body")).toContainText(
"Ausfahrt korrekt eingetragen",
);
await page.getByRole('link', { name: 'Logbuch' }).click();
await expect(page.locator('body')).toContainText('Joe');
await expect(page.locator('body')).toContainText('(cox2)');
await expect(page.locator('body')).toContainText('Ottensheim (25 km)');
await expect(page.locator('body')).toContainText('Ruderer: cox2, rower2');
});

View File

@ -15,5 +15,6 @@
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true
}
},
"exclude": ["tests/"]
}

View File

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

View File

@ -2,17 +2,37 @@ CREATE TABLE IF NOT EXISTS "user" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL UNIQUE,
"pw" text,
"is_cox" boolean NOT NULL DEFAULT FALSE,
"is_admin" boolean NOT NULL DEFAULT FALSE,
"is_guest" boolean NOT NULL DEFAULT TRUE,
"is_tech" boolean NOT NULL DEFAULT FALSE,
"deleted" boolean NOT NULL DEFAULT FALSE,
"last_access" DATETIME,
"dob" text,
"weight" text,
"sex" text,
"dirty_thirty" text,
"dirty_dozen" text
"dirty_dozen" text,
"member_since_date" text,
"birthdate" text,
"mail" text,
"nickname" text,
"notes" text,
"phone" text,
"address" text,
"family_id" INTEGER REFERENCES family(id),
"membership_pdf" BLOB
);
CREATE TABLE IF NOT EXISTS "family" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE IF NOT EXISTS "role" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "user_role" (
"user_id" INTEGER NOT NULL REFERENCES user(id),
"role_id" INTEGER NOT NULL REFERENCES role(id),
CONSTRAINT unq UNIQUE (user_id, role_id)
);
CREATE TABLE IF NOT EXISTS "trip_type" (
@ -79,9 +99,11 @@ CREATE TABLE IF NOT EXISTS "boat" (
"year_built" INTEGER,
"boatbuilder" TEXT,
"default_shipmaster_only_steering" boolean default false not null,
"convert_handoperated_possible" boolean default false not null,
"default_destination" text,
"skull" boolean default true NOT NULL, -- false => riemen
"external" boolean default false NOT NULL -- false => owned by different club
"external" boolean default false NOT NULL, -- false => owned by different club
"deleted" boolean NOT NULL DEFAULT FALSE
);
CREATE TABLE IF NOT EXISTS "logbook_type" (
@ -121,3 +143,73 @@ CREATE TABLE IF NOT EXISTS "boat_damage" (
"verified_at" datetime,
"lock_boat" boolean not null default false -- if true: noone can use the boat
);
CREATE TABLE IF NOT EXISTS "boathouse" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"boat_id" INTEGER NOT NULL REFERENCES boat(id),
"aisle" TEXT NOT NULL CHECK (aisle in ('water', 'middle', 'mountain')),
"side" TEXT NOT NULL CHECK(side IN ('mountain', 'water')),
"level" INTEGER NOT NULL CHECK(level BETWEEN 0 AND 11),
CONSTRAINT unq UNIQUE (aisle, side, level) -- only 1 boat allowed to rest at each space
);
CREATE TABLE IF NOT EXISTS "notification" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"user_id" INTEGER NOT NULL REFERENCES user(id),
"message" TEXT NOT NULL,
"read_at" DATETIME,
"created_at" DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
"category" TEXT NOT NULL,
"action_after_reading" TEXT,
"link" TEXT
);
CREATE TABLE IF NOT EXISTS "boat_reservation" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"boat_id" INTEGER NOT NULL REFERENCES boat(id),
"start_date" DATE NOT NULL,
"end_date" DATE NOT NULL,
"time_desc" TEXT NOT NULL,
"usage" TEXT NOT NULL,
"user_id_applicant" INTEGER NOT NULL REFERENCES user(id),
"user_id_confirmation" INTEGER REFERENCES user(id),
"created_at" datetime not null default CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "waterlevel" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"day" DATE NOT NULL,
"time" TEXT NOT NULL,
"max" INTEGER NOT NULL,
"min" INTEGER NOT NULL,
"mittel" INTEGER NOT NULL,
"tumax" INTEGER NOT NULL,
"tumin" INTEGER NOT NULL,
"tumittel" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "weather" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"day" DATE NOT NULL,
"max_temp" FLOAT NOT NULL,
"wind_gust" FLOAT NOT NULL,
"rain_mm" FLOAT NOT NULL
);
CREATE TABLE IF NOT EXISTS "trailer" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "trailer_reservation" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"trailer_id" INTEGER NOT NULL REFERENCES trailer(id),
"start_date" DATE NOT NULL,
"end_date" DATE NOT NULL,
"time_desc" TEXT NOT NULL,
"usage" TEXT NOT NULL,
"user_id_applicant" INTEGER NOT NULL REFERENCES user(id),
"user_id_confirmation" INTEGER REFERENCES user(id),
"created_at" datetime not null default CURRENT_TIMESTAMP
);

73
notes.md Normal file
View File

@ -0,0 +1,73 @@
# Wordpress auth
Add the following code to `wp-content/themes/bravada/functions.php`:
```
function rot_auth( $user, $username, $password ){
// Make sure a username and password are present for us to work with
if($username == '' || $password == '') return;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://app.rudernlinz.at/wikiauth');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "name=$username&password=$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the cURL session and get the response
$response = curl_exec($ch);
// Check for cURL errors
if(curl_errno($ch)){
$user = new WP_Error( 'denied', __('Curl error: ' . curl_error($ch)) );
}
// Close the cURL session
curl_close($ch);
if (strpos($response, 'SUCC') !== false) {
$user = get_user_by('login', $username);
if (!$user) {
// User does not exist, create a new one
$userdata = array(
'user_email' => $username,
'user_login' => $username,
'first_name' => $username,
'last_name' => ''
);
$new_user_id = wp_insert_user($userdata);
if (!is_wp_error($new_user_id)) {
// Load the new user info
$user = new WP_User($new_user_id);
// Set role based on username
if ($username == 'Philipp Hofer' || $username == 'Marie Birner') {
$user->set_role('administrator');
} else {
$user->set_role('editor');
}
} else {
// Handle error in user creation
return $new_user_id;
}
} else {
}
} else {
$user = new WP_Error( 'denied', __("Falscher Benutzername/Passwort. Verwendest du deine Accountdaten vom Ruderassistenten?") );
}
return $user;
}
// Comment this line if you wish to fall back on WordPress authentication
// Useful for times when the external service is offline
remove_action('authenticate', 'wp_authenticate_username_password', 20);
add_filter( 'authenticate', 'rot_auth', 10, 3 );
```

10
package-lock.json generated
View File

@ -1,10 +0,0 @@
{
"name": "rot",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "rot"
}
}
}

View File

@ -4,12 +4,15 @@ Description=Rot
[Service]
User=root
Group=root
WorkingDirectory=/home/k004373/rowing
WorkingDirectory=/home/rowing
Environment="ROCKET_ENV=prod"
Environment="ROCKET_ADDRESS=127.0.0.1"
Environment="ROCKET_PORT=8001"
Environment="RUST_LOG=info"
ExecStart=/home/k004373/rowing/rot
ExecStart=/home/rowing/rot
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

View File

@ -4,12 +4,14 @@ Description=Rot Staging
[Service]
User=root
Group=root
WorkingDirectory=/home/k004373/rowing-staging
WorkingDirectory=/home/rowing-staging
Environment="ROCKET_ENV=prod"
Environment="ROCKET_ADDRESS=127.0.0.1"
Environment="ROCKET_PORT=7999"
Environment="ROCKET_LOG=info"
ExecStart=/home/k004373/rowing-staging/rot
ExecStart=/home/rowing-staging/rot
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

View File

@ -1,10 +1,41 @@
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('admin', true, true, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('rower', false, false, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('guest', false, false, true, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$GF6gizbI79Bh0zA9its8S0gram956v+YIV8w8VpwJnQ');
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('cox', true, false, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
INSERT INTO "role" (name) VALUES ('admin');
INSERT INTO "role" (name) VALUES ('cox');
INSERT INTO "role" (name) VALUES ('scheckbuch');
INSERT INTO "role" (name) VALUES ('tech');
INSERT INTO "role" (name) VALUES ('Donau Linz');
INSERT INTO "role" (name) VALUES ('manage_events');
INSERT INTO "role" (name) VALUES ('Rennrudern');
INSERT INTO "role" (name) VALUES ('paid');
INSERT INTO "role" (name) VALUES ('Vorstand');
INSERT INTO "role" (name) VALUES ('Bootsführer');
INSERT INTO "role" (name) VALUES ('schnupperant');
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,6);
INSERT INTO "user" (name, pw) VALUES('rower', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(2,5);
INSERT INTO "user" (name, pw) VALUES('guest', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$GF6gizbI79Bh0zA9its8S0gram956v+YIV8w8VpwJnQ');
INSERT INTO "user_role" (user_id, role_id) VALUES(3,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(3,3);
INSERT INTO "user" (name, pw) VALUES('cox', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
INSERT INTO "user_role" (user_id, role_id) VALUES(4,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(4,2);
INSERT INTO "user_role" (user_id, role_id) VALUES(4,8);
INSERT INTO "user" (name) VALUES('new');
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('cox2', true, false, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('rower2', false, false, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(5,5);
INSERT INTO "user" (name, pw) VALUES('cox2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$lnWzHx3DdqS9GQyWYel82kIotZuK2wk9EyfhPFtjNzs');
INSERT INTO "user_role" (user_id, role_id) VALUES(6,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(6,2);
INSERT INTO "user" (name, pw) VALUES('rower2', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(7,5);
INSERT INTO "user" (name, pw) VALUES('teen', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(8,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(8,7);
INSERT INTO "user" (name, pw) VALUES('Vorstandsmitglied', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$jWKzDmI0jqT2dqINFt6/1NjVF4Dx15n07PL1ZMBmFsY');
INSERT INTO "user_role" (user_id, role_id) VALUES(9,5);
INSERT INTO "user_role" (user_id, role_id) VALUES(9,9);
INSERT INTO "trip_details" (planned_starting_time, max_people, day, notes) VALUES('10:00', 2, '1970-01-01', 'trip_details for a planned event');
INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES('test-planned-event', 2, 1);
@ -32,3 +63,6 @@ INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_ste
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, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);
INSERT INTO "notification" (user_id, message, category) VALUES (1, 'This is a test notification', 'test-cat');
INSERT INTO "trailer" (name) VALUES('Großer Hänger');
INSERT INTO "trailer" (name) VALUES('Kleiner Hänger');

View File

@ -1 +0,0 @@
2023-06-06: Phil Baillon um 19:10 für 18 Uhr Fahrt abgemeldet

View File

@ -1,3 +1,5 @@
#![allow(clippy::blocks_in_conditions)]
pub mod model;
#[cfg(feature = "rowing-tera")]
@ -6,6 +8,8 @@ pub mod tera;
#[cfg(feature = "rest")]
pub mod rest;
pub mod scheduled;
#[cfg(test)]
#[macro_export]
macro_rules! testdb {

View File

@ -1,9 +1,12 @@
#![allow(clippy::blocks_in_conditions)]
use std::str::FromStr;
#[cfg(feature = "rest")]
use rot::rest;
#[cfg(feature = "rowing-tera")]
use rot::tera;
use rot::{scheduled, tera::Config};
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
@ -24,7 +27,7 @@ async fn rocket() -> _ {
.await
.unwrap();
let rocket = rocket::build().manage(db);
let rocket = rocket::build().manage(db.clone());
#[cfg(feature = "rowing-tera")]
let rocket = tera::config(rocket);
@ -32,5 +35,11 @@ async fn rocket() -> _ {
#[cfg(feature = "rest")]
let rocket = rest::config(rocket);
let config: Config = rocket
.figment()
.extract()
.expect("Config extraction failed");
scheduled::schedule(&db, &config);
rocket
}

View File

@ -1,12 +1,16 @@
use std::ops::DerefMut;
use itertools::Itertools;
use rocket::serde::{Deserialize, Serialize};
use rocket::FromForm;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use crate::model::boathouse::Boathouse;
use super::location::Location;
use super::user::User;
#[derive(FromRow, Debug, Serialize, Deserialize)]
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Boat {
pub id: i64,
pub name: String,
@ -17,11 +21,14 @@ pub struct Boat {
pub boatbuilder: Option<String>,
pub default_destination: Option<String>,
#[serde(default = "bool::default")]
default_shipmaster_only_steering: bool,
pub convert_handoperated_possible: bool,
#[serde(default = "bool::default")]
pub default_shipmaster_only_steering: bool,
#[serde(default = "bool::default")]
skull: bool,
#[serde(default = "bool::default")]
external: bool,
pub external: bool,
pub deleted: bool,
}
#[derive(Serialize, Deserialize, Debug)]
@ -35,9 +42,11 @@ pub enum BoatDamage {
#[derive(Serialize, Deserialize, Debug)]
pub struct BoatWithDetails {
#[serde(flatten)]
boat: Boat,
pub(crate) boat: Boat,
damage: BoatDamage,
on_water: bool,
reserved_today: bool,
cat: String,
}
#[derive(FromForm)]
@ -47,6 +56,7 @@ pub struct BoatToAdd<'r> {
pub year_built: Option<i64>,
pub boatbuilder: Option<&'r str>,
pub default_shipmaster_only_steering: bool,
pub convert_handoperated_possible: bool,
pub default_destination: Option<&'r str>,
pub skull: bool,
pub external: bool,
@ -63,6 +73,7 @@ pub struct BoatToUpdate<'r> {
pub default_shipmaster_only_steering: bool,
pub default_destination: Option<&'r str>,
pub skull: bool,
pub convert_handoperated_possible: bool,
pub external: bool,
pub location_id: i64,
pub owner: Option<i64>,
@ -70,26 +81,51 @@ pub struct BoatToUpdate<'r> {
impl Boat {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM boat WHERE id like ?", id)
sqlx::query_as!(Self, "SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, convert_handoperated_possible, default_destination, skull, external, deleted FROM boat WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM boat WHERE id like ?", id)
sqlx::query_as!(Self, "SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, convert_handoperated_possible, default_destination, skull, external, deleted FROM boat WHERE id like ?", id)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn find_by_name(db: &SqlitePool, name: String) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM boat WHERE name like ?", name)
sqlx::query_as!(Self, "SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, convert_handoperated_possible, default_destination, skull, external, deleted FROM boat WHERE name like ?", name)
.fetch_one(db)
.await
.ok()
}
pub async fn shipmaster_allowed(&self, user: &User) -> bool {
pub async fn shipmaster_allowed(&self, db: &SqlitePool, user: &User) -> bool {
if let Some(owner_id) = self.owner {
return owner_id == user.id;
}
if user.has_role(db, "Rennrudern").await {
let ottensheim = Location::find_by_name(db, "Ottensheim".into())
.await
.unwrap();
if self.location_id == ottensheim.id {
return true;
}
}
if self.amount_seats == 1 {
return true;
}
user.has_role(db, "cox").await
}
pub async fn shipmaster_allowed_tx(
&self,
db: &mut Transaction<'_, Sqlite>,
user: &User,
) -> bool {
if let Some(owner_id) = self.owner {
return owner_id == user.id;
}
@ -98,7 +134,7 @@ impl Boat {
return true;
}
user.is_cox
user.has_role_tx(db, "cox").await
}
pub async fn is_locked(&self, db: &SqlitePool) -> bool {
@ -109,6 +145,20 @@ impl Boat {
sqlx::query!("SELECT * FROM boat_damage WHERE boat_id=? AND lock_boat=false AND user_id_verified is null", self.id).fetch_optional(db).await.unwrap().is_some()
}
pub async fn reserved_today(&self, db: &SqlitePool) -> bool {
sqlx::query!(
"SELECT *
FROM boat_reservation
WHERE boat_id =?
AND date('now') BETWEEN start_date AND end_date;",
self.id
)
.fetch_optional(db)
.await
.unwrap()
.is_some()
}
pub async fn on_water(&self, db: &SqlitePool) -> bool {
sqlx::query!(
"SELECT * FROM logbook WHERE boat_id=? AND arrival is null",
@ -130,10 +180,20 @@ impl Boat {
if boat.is_locked(db).await {
damage = BoatDamage::Locked;
}
let cat = if boat.external {
"Vereinsfremde Boote".to_string()
} else if boat.default_shipmaster_only_steering {
format!("{}+", boat.amount_seats - 1)
} else {
format!("{}x", boat.amount_seats)
};
res.push(BoatWithDetails {
damage,
on_water: boat.on_water(db).await,
reserved_today: boat.reserved_today(db).await,
boat,
cat,
});
}
res
@ -143,8 +203,9 @@ impl Boat {
let boats = sqlx::query_as!(
Boat,
"
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE deleted=false
ORDER BY amount_seats DESC
"
)
@ -155,17 +216,52 @@ ORDER BY amount_seats DESC
Self::boats_to_details(db, boats).await
}
pub async fn all_for_boatshouse(db: &SqlitePool) -> Vec<BoatWithDetails> {
let boats = sqlx::query_as!(
Boat,
"
SELECT
b.id,
b.name,
b.amount_seats,
b.location_id,
b.owner,
b.year_built,
b.boatbuilder,
b.default_shipmaster_only_steering,
b.default_destination,
b.skull,
b.external,
b.deleted,
b.convert_handoperated_possible
FROM
boat AS b
WHERE
b.external = false
AND b.location_id = (SELECT id FROM location WHERE name = 'Linz')
AND b.deleted = false
ORDER BY
b.name DESC;
"
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
Self::boats_to_details(db, boats).await
}
pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<BoatWithDetails> {
if user.is_admin {
if user.has_role(db, "admin").await {
return Self::all(db).await;
}
let boats = if user.is_cox {
let mut boats = if user.has_role(db, "cox").await {
sqlx::query_as!(
Boat,
"
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE owner is null or owner = ?
WHERE (owner is null or owner = ?) AND deleted = 0
ORDER BY amount_seats DESC
",
user.id
@ -177,9 +273,9 @@ ORDER BY amount_seats DESC
sqlx::query_as!(
Boat,
"
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external
SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE owner = ? OR (owner is null and amount_seats = 1)
WHERE (owner = ? OR (owner is null and amount_seats = 1)) AND deleted = 0
ORDER BY amount_seats DESC
",
user.id
@ -189,6 +285,24 @@ ORDER BY amount_seats DESC
.unwrap() //TODO: fixme
};
if user.has_role(db, "Rennrudern").await {
let ottensheim = Location::find_by_name(db, "Ottensheim".into())
.await
.unwrap();
let boats_in_ottensheim = sqlx::query_as!(
Boat,
"SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
WHERE (owner is null and location_id = ?) AND deleted = 0
ORDER BY amount_seats DESC
",ottensheim.id)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
boats.extend(boats_in_ottensheim.into_iter());
}
let boats = boats.into_iter().unique().collect();
Self::boats_to_details(db, boats).await
}
@ -196,10 +310,10 @@ ORDER BY amount_seats DESC
let boats = sqlx::query_as!(
Boat,
"
SELECT boat.id, boat.name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external
SELECT boat.id, boat.name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible
FROM boat
INNER JOIN location ON boat.location_id = location.id
WHERE location.name=?
WHERE location.name=? AND deleted = 0
ORDER BY amount_seats DESC
",
location
@ -213,7 +327,7 @@ ORDER BY amount_seats DESC
pub async fn create(db: &SqlitePool, boat: BoatToAdd<'_>) -> Result<(), String> {
sqlx::query!(
"INSERT INTO boat(name, amount_seats, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, location_id, owner) VALUES (?,?,?,?,?,?,?,?,?,?)",
"INSERT INTO boat(name, amount_seats, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, location_id, owner, convert_handoperated_possible) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
boat.name,
boat.amount_seats,
boat.year_built,
@ -223,7 +337,8 @@ ORDER BY amount_seats DESC
boat.skull,
boat.external,
boat.location_id,
boat.owner
boat.owner,
boat.convert_handoperated_possible
)
.execute(db)
.await.map_err(|e| e.to_string())?;
@ -232,7 +347,7 @@ ORDER BY amount_seats DESC
pub async fn update(&self, db: &SqlitePool, boat: BoatToUpdate<'_>) -> Result<(), String> {
sqlx::query!(
"UPDATE boat SET name=?, amount_seats=?, year_built=?, boatbuilder=?, default_shipmaster_only_steering=?, default_destination=?, skull=?, external=?, location_id=?, owner=? WHERE id=?",
"UPDATE boat SET name=?, amount_seats=?, year_built=?, boatbuilder=?, default_shipmaster_only_steering=?, default_destination=?, skull=?, external=?, location_id=?, owner=?, convert_handoperated_possible=? WHERE id=?",
boat.name,
boat.amount_seats,
boat.year_built,
@ -243,6 +358,7 @@ ORDER BY amount_seats DESC
boat.external,
boat.location_id,
boat.owner,
boat.convert_handoperated_possible,
self.id
)
.execute(db)
@ -250,12 +366,31 @@ ORDER BY amount_seats DESC
Ok(())
}
pub async fn owner(&self, db: &SqlitePool) -> Option<User> {
if let Some(owner_id) = self.owner {
Some(User::find_by_id(db, owner_id as i32).await.unwrap())
} else {
None
}
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM boat WHERE id=?", self.id)
sqlx::query!("UPDATE boat SET deleted=1 WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Boat of a valid id
}
pub async fn boathouse(&self, db: &SqlitePool) -> Option<Boathouse> {
sqlx::query_as!(
Boathouse,
"SELECT * FROM boathouse WHERE boat_id like ?",
self.id
)
.fetch_one(db)
.await
.ok()
}
}
#[cfg(test)]
@ -303,6 +438,7 @@ mod test {
year_built: None,
boatbuilder: "Best Boatbuilder".into(),
default_shipmaster_only_steering: true,
convert_handoperated_possible: false,
skull: true,
external: false,
location_id: Some(1),
@ -328,6 +464,7 @@ mod test {
year_built: None,
boatbuilder: "Best Boatbuilder".into(),
default_shipmaster_only_steering: true,
convert_handoperated_possible: false,
skull: true,
external: false,
location_id: Some(1),
@ -430,6 +567,7 @@ mod test {
year_built: None,
boatbuilder: None,
default_shipmaster_only_steering: false,
convert_handoperated_possible: false,
skull: true,
external: false,
location_id: 1,
@ -453,6 +591,7 @@ mod test {
year_built: None,
boatbuilder: None,
default_shipmaster_only_steering: false,
convert_handoperated_possible: false,
skull: true,
external: false,
location_id: 999,

View File

@ -5,6 +5,8 @@ use rocket::FromForm;
use sqlx::{FromRow, SqlitePool};
use super::log::Log;
use super::notification::Notification;
use super::role::Role;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatDamage {
@ -71,6 +73,10 @@ impl BoatDamage {
"
SELECT id, boat_id, desc, user_id_created, created_at, user_id_fixed, fixed_at, user_id_verified, verified_at, lock_boat
FROM boat_damage
WHERE (
verified_at IS NULL
OR verified_at >= datetime('now', '-30 days')
)
ORDER BY created_at DESC
"
)
@ -113,6 +119,10 @@ ORDER BY created_at DESC
pub async fn create(db: &SqlitePool, boatdamage: BoatDamageToAdd<'_>) -> Result<(), String> {
Log::create(db, format!("New boat damage: {boatdamage:?}")).await;
let Some(boat) = Boat::find_by_id(db, boatdamage.boat_id as i32).await else {
return Err("Boot gibt's ned".into());
};
let was_unusable_before = boat.is_locked(db).await;
sqlx::query!(
"INSERT INTO boat_damage(boat_id, desc, user_id_created, lock_boat) VALUES (?,?,?, ?)",
@ -124,63 +134,218 @@ ORDER BY created_at DESC
.execute(db)
.await
.map_err(|e| e.to_string())?;
if !was_unusable_before && boat.is_locked(db).await {
let cox = Role::find_by_name(db, "cox").await.unwrap();
Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist.", boat.name), "Boot gesperrt", None, None).await;
}
let technicals =
User::all_with_role(db, &Role::find_by_name(db, "tech").await.unwrap()).await;
for technical in technicals {
if technical.id as i32 != boatdamage.user_id_created {
Notification::create(
db,
&technical,
&format!(
"{} hat einen neuen Bootschaden für Boot '{}' angelegt: {}",
User::find_by_id(db, boatdamage.user_id_created)
.await
.unwrap()
.name,
boat.name,
boatdamage.desc
),
"Neuer Bootsschaden angelegt",
None,
None,
)
.await;
}
}
Notification::create(
db,
&User::find_by_id(db, boatdamage.user_id_created)
.await
.unwrap(),
&format!(
"Du hat einen neuen Bootschaden für Boot '{}' angelegt: {}",
Boat::find_by_id(db, boatdamage.boat_id as i32)
.await
.unwrap()
.name,
boatdamage.desc
),
"Neuer Bootsschaden angelegt",
None,
None,
)
.await;
Ok(())
}
pub async fn fixed(&self, db: &SqlitePool, boat: BoatDamageFixed<'_>) -> Result<(), String> {
Log::create(db, format!("Fixed boat damage: {boat:?}")).await;
pub async fn fixed(
&self,
db: &SqlitePool,
boat_damage: BoatDamageFixed<'_>,
) -> Result<(), String> {
Log::create(db, format!("Fixed boat damage: {boat_damage:?}")).await;
let boat = Boat::find_by_id(db, self.boat_id as i32).await.unwrap();
sqlx::query!(
"UPDATE boat_damage SET desc=?, user_id_fixed=?, fixed_at=CURRENT_TIMESTAMP WHERE id=?",
boat.desc,
boat.user_id_fixed,
boat_damage.desc,
boat_damage.user_id_fixed,
self.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
let user = User::find_by_id(db, boat.user_id_fixed).await.unwrap();
if user.is_tech {
let user = User::find_by_id(db, boat_damage.user_id_fixed)
.await
.unwrap();
if user.has_role(db, "tech").await {
return self
.verified(
db,
BoatDamageVerified {
desc: boat.desc,
desc: boat_damage.desc,
user_id_verified: user.id as i32,
},
)
.await;
}
let technicals =
User::all_with_role(db, &Role::find_by_name(db, "tech").await.unwrap()).await;
for technical in technicals {
if technical.id as i32 != boat_damage.user_id_fixed {
Notification::create(
db,
&technical,
&format!(
"{} hat den Bootschaden '{}' beim Boot '{}' repariert. Könntest du das bei Gelegenheit verifizieren?",
User::find_by_id(db, boat_damage.user_id_fixed)
.await
.unwrap()
.name,
boat_damage.desc,
boat.name,
),
"Bootsschaden repariert",
None,None
)
.await;
}
}
if boat_damage.user_id_fixed != self.user_id_created as i32 {
let user_fixed = User::find_by_id(db, boat_damage.user_id_fixed)
.await
.unwrap();
let user_created = User::find_by_id(db, self.user_id_created as i32)
.await
.unwrap();
// Boatdamage is also directly verified, if a tech has repaired it. We don't want to
// send 2 notifications.
if !user_fixed.has_role(db, "tech").await {
Notification::create(
db,
&user_created,
&format!(
"{} hat den von dir eingetragenen Bootschaden '{}' beim Boot '{}' repariert. Dieser muss nun noch von unseren Bootswarten bestätigt werden.",
user_fixed.name,
boat_damage.desc, boat.name,
),
"Bootsschaden repariert",
None,None
)
.await;
}
}
Ok(())
}
pub async fn verified(
&self,
db: &SqlitePool,
boat: BoatDamageVerified<'_>,
boat_form: BoatDamageVerified<'_>,
) -> Result<(), String> {
if let Some(verifier) = User::find_by_id(db, boat.user_id_verified).await {
if !verifier.is_tech {
Log::create(db, format!("User {verifier:?} tried to verify boat {boat:?}. The user is no tech. Manually craftted request?")).await;
if let Some(verifier) = User::find_by_id(db, boat_form.user_id_verified).await {
if !verifier.has_role(db, "tech").await {
Log::create(db, format!("User {verifier:?} tried to verify boat {boat_form:?}. The user is no tech. Manually craftted request?")).await;
return Err("You are not allowed to verify the boat!".into());
}
} else {
Log::create(db, format!("Someone tried to verify the boat {boat:?} with user_id={} which does not exist. Manually craftted request?", boat.user_id_verified)).await;
Log::create(db, format!("Someone tried to verify the boat {boat_form:?} with user_id={} which does not exist. Manually craftted request?", boat_form.user_id_verified)).await;
return Err("Could not find user".into());
}
Log::create(db, format!("Verified boat damage: {boat:?}")).await;
let Some(boat) = Boat::find_by_id(db, self.boat_id as i32).await else {
return Err("Boot gibt's ned".into());
};
let was_unusable_before = boat.is_locked(db).await;
Log::create(db, format!("Verified boat damage: {boat_form:?}")).await;
sqlx::query!(
"UPDATE boat_damage SET desc=?, user_id_verified=?, verified_at=CURRENT_TIMESTAMP WHERE id=?",
boat.desc,
boat.user_id_verified,
boat_form.desc,
boat_form.user_id_verified,
self.id
)
.execute(db)
.await.map_err(|e| e.to_string())?;
if boat_form.user_id_verified != self.user_id_created as i32 {
let user_verified = User::find_by_id(db, boat_form.user_id_verified)
.await
.unwrap();
let user_created = User::find_by_id(db, self.user_id_created as i32)
.await
.unwrap();
if user_verified.id == self.user_id_fixed.unwrap() {
Notification::create(
db,
&user_created,
&format!(
"{} hat den von dir eingetragenen Bootschaden '{}' beim Boot '{}' repariert und verifiziert.",
user_verified.name,
self.desc, boat.name,
),
"Bootsschaden repariert & verifiziert",
None,
None
)
.await;
} else {
Notification::create(
db,
&user_created,
&format!(
"{} hat verifiziert, dass der von dir eingetragenen Bootschaden '{}' beim Boot '{}' korrekt repariert wurde.",
user_verified.name,
self.desc, boat.name,
),
"Bootsschaden verifiziert",
None,
None
).await;
}
}
if was_unusable_before && !boat.is_locked(db).await {
let cox = Role::find_by_name(db, "cox").await.unwrap();
Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-)", boat.name), "Boot repariert", None, None).await;
}
Ok(())
}
}

120
src/model/boathouse.rs Normal file
View File

@ -0,0 +1,120 @@
use std::collections::HashMap;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use crate::tera::board::boathouse::FormBoathouseToAdd;
use super::boat::Boat;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct Boathouse {
pub id: i64,
pub boat_id: i64,
pub aisle: String,
pub side: String,
pub level: i64,
}
impl Boathouse {
pub async fn get(db: &SqlitePool) -> HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> {
let mut ret: HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> = HashMap::new();
let mut mountain = HashMap::new();
mountain.insert(
"mountain",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
mountain.insert(
"water",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
ret.insert("mountain-aisle", mountain);
let mut middle = HashMap::new();
middle.insert(
"mountain",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
middle.insert(
"water",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
ret.insert("middle-aisle", middle);
let mut water = HashMap::new();
water.insert(
"mountain",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
water.insert(
"water",
[
None, None, None, None, None, None, None, None, None, None, None, None,
],
);
ret.insert("water-aisle", water);
let boathouses = sqlx::query_as!(
Boathouse,
"SELECT id, boat_id, aisle, side, level FROM boathouse"
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
for boathouse in boathouses {
let aisle = ret
.get_mut(format!("{}-aisle", boathouse.aisle).as_str())
.unwrap();
let side = aisle.get_mut(boathouse.side.as_str()).unwrap();
side[boathouse.level as usize] = Some((
boathouse.id,
Boat::find_by_id(db, boathouse.boat_id as i32)
.await
.unwrap(),
));
}
ret
}
pub async fn create(db: &SqlitePool, data: FormBoathouseToAdd) -> Result<(), String> {
sqlx::query!(
"INSERT INTO boathouse(boat_id, aisle, side, level) VALUES (?,?,?,?)",
data.boat_id,
data.aisle,
data.side,
data.level
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM boathouse WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM boathouse WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Boat of a valid id
}
}

View File

@ -0,0 +1,228 @@
use std::collections::HashMap;
use crate::model::{boat::Boat, user::User};
use crate::tera::boatreservation::ReservationEditForm;
use chrono::NaiveDate;
use chrono::NaiveDateTime;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use super::log::Log;
use super::notification::Notification;
use super::role::Role;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatReservation {
pub id: i64,
pub boat_id: i64,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: String,
pub usage: String,
pub user_id_applicant: i64,
pub user_id_confirmation: Option<i64>,
pub created_at: NaiveDateTime,
}
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct BoatReservationWithDetails {
#[serde(flatten)]
reservation: BoatReservation,
boat: Boat,
user_applicant: User,
user_confirmation: Option<User>,
}
#[derive(Debug)]
pub struct BoatReservationToAdd<'r> {
pub boat: &'r Boat,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: &'r str,
pub usage: &'r str,
pub user_applicant: &'r User,
}
impl BoatReservation {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM boat_reservation
WHERE id like ?",
id
)
.fetch_one(db)
.await
.ok()
}
pub async fn all_future(db: &SqlitePool) -> Vec<BoatReservationWithDetails> {
let boatreservations = sqlx::query_as!(
Self,
"
SELECT id, boat_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM boat_reservation
WHERE end_date >= CURRENT_DATE ORDER BY end_date
"
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut res = Vec::new();
for reservation in boatreservations {
let user_confirmation = match reservation.user_id_confirmation {
Some(id) => {
let user = User::find_by_id(db, id as i32).await;
Some(user.unwrap())
}
None => None,
};
let user_applicant = User::find_by_id(db, reservation.user_id_applicant as i32)
.await
.unwrap();
let boat = Boat::find_by_id(db, reservation.boat_id as i32)
.await
.unwrap();
res.push(BoatReservationWithDetails {
reservation,
boat,
user_applicant,
user_confirmation,
});
}
res
}
pub async fn all_future_with_groups(
db: &SqlitePool,
) -> HashMap<String, Vec<BoatReservationWithDetails>> {
let mut grouped_reservations: HashMap<String, Vec<BoatReservationWithDetails>> =
HashMap::new();
let reservations = Self::all_future(db).await;
for reservation in reservations {
let key = format!(
"{}-{}-{}-{}-{}",
reservation.reservation.start_date,
reservation.reservation.end_date,
reservation.reservation.time_desc,
reservation.reservation.usage,
reservation.user_applicant.name
);
grouped_reservations
.entry(key)
.or_default()
.push(reservation);
}
grouped_reservations
}
pub async fn create(
db: &SqlitePool,
boatreservation: BoatReservationToAdd<'_>,
) -> Result<(), String> {
if Self::boat_reserved_between_dates(
db,
boatreservation.boat,
&boatreservation.start_date,
&boatreservation.end_date,
)
.await
{
return Err("Boot in diesem Zeitraum bereits reserviert.".into());
}
Log::create(db, format!("New boat reservation: {boatreservation:?}")).await;
sqlx::query!(
"INSERT INTO boat_reservation(boat_id, start_date, end_date, time_desc, usage, user_id_applicant) VALUES (?,?,?,?,?,?)",
boatreservation.boat.id,
boatreservation.start_date,
boatreservation.end_date,
boatreservation.time_desc,
boatreservation.usage,
boatreservation.user_applicant.id,
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
let board =
User::all_with_role(db, &Role::find_by_name(db, "Vorstand").await.unwrap()).await;
for user in board {
let date = if boatreservation.start_date == boatreservation.end_date {
format!("am {}", boatreservation.start_date)
} else {
format!(
"von {} bis {}",
boatreservation.start_date, boatreservation.end_date
)
};
Notification::create(
db,
&user,
&format!(
"{} hat eine neue Bootsreservierung für Boot '{}' {} angelegt. Zeit: {}; Zweck: {}",
boatreservation.user_applicant.name,
boatreservation.boat.name,
date,
boatreservation.time_desc,
boatreservation.usage
),
"Neue Bootsreservierung",
None,None
)
.await;
}
Ok(())
}
pub async fn boat_reserved_between_dates(
db: &SqlitePool,
boat: &Boat,
start_date: &NaiveDate,
end_date: &NaiveDate,
) -> bool {
sqlx::query!(
"SELECT COUNT(*) AS reservation_count
FROM boat_reservation
WHERE boat_id = ?
AND start_date <= ? AND end_date >= ?;",
boat.id,
end_date,
start_date
)
.fetch_one(db)
.await
.unwrap()
.reservation_count
> 0
}
pub async fn update(&self, db: &SqlitePool, data: ReservationEditForm) {
let time_desc = data.time_desc.trim();
let usage = data.usage.trim();
sqlx::query!(
"UPDATE boat_reservation SET time_desc = ?, usage = ? where id = ?",
time_desc,
usage,
self.id
)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a User of a valid id
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM boat_reservation WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Boat of a valid id
}
}

441
src/model/event.rs Normal file
View File

@ -0,0 +1,441 @@
use std::io::Write;
use chrono::NaiveDate;
use ics::{
properties::{DtStart, Summary},
ICalendar,
};
use serde::Serialize;
use sqlx::{FromRow, Row, SqlitePool};
use super::{notification::Notification, tripdetails::TripDetails, triptype::TripType, user::User};
#[derive(Serialize, Clone, FromRow, Debug, PartialEq)]
pub struct Event {
pub id: i64,
pub name: String,
pub(crate) planned_amount_cox: i64,
trip_details_id: i64,
pub planned_starting_time: String,
pub(crate) max_people: i64,
pub day: String,
pub notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>,
pub(crate) always_show: bool,
pub(crate) is_locked: bool,
}
#[derive(Serialize, Debug)]
pub struct EventWithUserAndTriptype {
#[serde(flatten)]
pub event: Event,
trip_type: Option<TripType>,
cox_needed: bool,
cox: Vec<Registration>,
rower: Vec<Registration>,
}
//TODO: move to appropriate place
#[derive(Serialize, Debug)]
pub struct Registration {
pub name: String,
pub registered_at: String,
pub is_guest: bool,
pub is_real_guest: bool,
}
impl Registration {
pub async fn all_rower(db: &SqlitePool, trip_details_id: i64) -> Vec<Registration> {
sqlx::query(
&format!(
r#"
SELECT
(SELECT name FROM user WHERE user_trip.user_id = user.id) as "name?",
user_note,
user_id,
(SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at,
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'scheckbuch'))) as is_guest
FROM user_trip WHERE trip_details_id = {}
"#,trip_details_id),
)
.fetch_all(db)
.await
.unwrap()
.into_iter()
.map(|r|
Registration {
name: r.get::<Option<String>, usize>(0).or(r.get::<Option<String>, usize>(1)).unwrap(), //Ok, either name or user_note needs to be set
registered_at: r.get::<String,usize>(3),
is_guest: r.get::<bool, usize>(4),
is_real_guest: r.get::<Option<i64>, usize>(2).is_none(),
})
.collect()
}
pub async fn all_cox(db: &SqlitePool, event_id: i64) -> Vec<Registration> {
//TODO: switch to join
sqlx::query!(
"
SELECT
(SELECT name FROM user WHERE cox_id = id) as name,
(SELECT created_at FROM user WHERE cox_id = id) as registered_at
FROM trip WHERE planned_event_id = ?
",
event_id
)
.fetch_all(db)
.await
.unwrap()
.into_iter()
.map(|r| Registration {
name: r.name,
registered_at: r.registered_at,
is_guest: false,
is_real_guest: false,
})
.collect() //Okay, as Event can only be created with proper DB backing
}
}
#[derive(Debug)]
pub struct EventUpdate<'a> {
pub name: &'a str,
pub planned_amount_cox: i32,
pub max_people: i32,
pub notes: Option<&'a str>,
pub always_show: bool,
pub is_locked: bool,
pub trip_type_id: Option<i64>,
}
impl Event {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT
planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE planned_event.id like ?
",
id
)
.fetch_one(db)
.await
.ok()
}
pub async fn get_pinned_for_day(
db: &SqlitePool,
day: NaiveDate,
) -> Vec<EventWithUserAndTriptype> {
let mut events = Self::get_for_day(db, day).await;
events.retain(|e| e.event.always_show);
events
}
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithUserAndTriptype> {
let day = format!("{day}");
let events = sqlx::query_as!(
Event,
"SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE day=?",
day
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut ret = Vec::new();
for event in events {
let cox = Registration::all_cox(db, event.id).await;
let mut trip_type = None;
if let Some(trip_type_id) = event.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await;
}
ret.push(EventWithUserAndTriptype {
cox_needed: event.planned_amount_cox > cox.len() as i64,
cox,
rower: Registration::all_rower(db, event.trip_details_id).await,
event,
trip_type,
});
}
ret
}
pub async fn all(db: &SqlitePool) -> Vec<Event> {
sqlx::query_as!(
Event,
"SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
}
//TODO: add tests
pub async fn is_rower_registered(&self, db: &SqlitePool, user: &User) -> bool {
let is_rower = sqlx::query!(
"SELECT count(*) as amount
FROM user_trip
WHERE trip_details_id =
(SELECT trip_details_id FROM planned_event WHERE id = ?)
AND user_id = ?",
self.id,
user.id
)
.fetch_one(db)
.await
.unwrap(); //Okay, bc planned_event can only be created with proper DB backing
is_rower.amount > 0
}
pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE trip_details.id=?
",
tripdetails_id
)
.fetch_one(db)
.await
.ok()
}
pub async fn create(
db: &SqlitePool,
name: &str,
planned_amount_cox: i32,
trip_details: &TripDetails,
) {
sqlx::query!(
"INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
name,
planned_amount_cox,
trip_details.id,
)
.execute(db)
.await
.unwrap(); //Okay, as TripDetails can only be created with proper DB backing
}
//TODO: create unit test
pub async fn update(&self, db: &SqlitePool, update: &EventUpdate<'_>) {
sqlx::query!(
"UPDATE planned_event SET name = ?, planned_amount_cox = ? WHERE id = ?",
update.name,
update.planned_amount_cox,
self.id
)
.execute(db)
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.max_people == 0;
sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?",
update.max_people,
update.notes,
update.always_show,
update.is_locked,
update.trip_type_id,
self.trip_details_id
)
.execute(db)
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
if update.max_people == 0 && !was_already_cancelled {
let coxes = Registration::all_cox(db, self.id).await;
for user in coxes {
if let Some(user) = User::find_by_name(db, &user.name).await {
let notes = match update.notes {
Some(n) if !n.is_empty() => format!("Grund der Absage: {n}"),
_ => String::from(""),
};
Notification::create(
db,
&user,
&format!(
"Die Ausfahrt {} am {} um {} wurde abgesagt. {}",
self.name, self.day, self.planned_starting_time, notes
),
"Absage Ausfahrt",
None,
Some(&format!("remove_trip_by_event:{}", self.id)),
)
.await;
}
}
let rower = Registration::all_rower(db, self.trip_details_id).await;
for user in rower {
if let Some(user) = User::find_by_name(db, &user.name).await {
let notes = match update.notes {
Some(n) if !n.is_empty() => format!("Grund der Absage: {n}"),
_ => String::from(""),
};
Notification::create(
db,
&user,
&format!(
"Die Ausfahrt {} am {} um {} wurde abgesagt. {}",
self.name, self.day, self.planned_starting_time, notes
),
"Absage Ausfahrt",
None,
Some(&format!(
"remove_user_trip_with_trip_details_id:{}",
tripdetails.id
)),
)
.await;
}
}
}
if update.max_people > 0 && was_already_cancelled {
Notification::delete_by_action(
db,
&format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id),
)
.await;
Notification::delete_by_action(db, &format!("remove_trip_by_event:{}", self.id)).await;
}
}
pub async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
if !Registration::all_rower(db, self.trip_details_id)
.await
.is_empty()
{
return Err(
"Event kann nicht gelöscht werden, weil mind. 1 Ruderer angemeldet ist.".into(),
);
}
if !Registration::all_cox(db, self.trip_details_id)
.await
.is_empty()
{
return Err(
"Event kann nicht gelöscht werden, weil mind. 1 Steuerperson angemeldet ist."
.into(),
);
}
sqlx::query!("DELETE FROM planned_event WHERE id = ?", self.id)
.execute(db)
.await
.unwrap(); //Okay, as Event can only be created with proper DB backing
Ok(())
}
pub fn is_cancelled(&self) -> bool {
self.max_people == 0
}
pub async fn get_ics_feed(db: &SqlitePool) -> String {
let mut calendar = ICalendar::new("2.0", "ics-rs");
let events = Event::all(db).await;
for event in events {
let mut vevent =
ics::Event::new(format!("{}@rudernlinz.at", event.id), "19900101T180000");
vevent.push(DtStart::new(format!(
"{}T{}00",
event.day.replace('-', ""),
event.planned_starting_time.replace(':', "")
)));
let tripdetails = event.trip_details(db).await;
let mut name = String::new();
if event.is_cancelled() {
name.push_str("ABGESAGT");
if let Some(notes) = &tripdetails.notes {
if !notes.is_empty() {
name.push_str(&format!(" (Grund: {notes})"))
}
}
name.push_str("! :-( ");
}
name.push_str(&format!("{} ", event.name));
if let Some(triptype) = tripdetails.triptype(db).await {
name.push_str(&format!("{} ", triptype.name))
}
vevent.push(Summary::new(name));
calendar.add_event(vevent);
}
let mut buf = Vec::new();
write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap()
}
pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails {
TripDetails::find_by_id(db, self.trip_details_id)
.await
.unwrap() //ok, not null in db
}
}
#[cfg(test)]
mod test {
use crate::{model::tripdetails::TripDetails, testdb};
use super::Event;
use chrono::NaiveDate;
use sqlx::SqlitePool;
#[sqlx::test]
fn test_get_day() {
let pool = testdb!();
let res = Event::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 1);
}
#[sqlx::test]
fn test_create() {
let pool = testdb!();
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
Event::create(&pool, "new-event".into(), 2, &trip_details).await;
let res = Event::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 2);
}
#[sqlx::test]
fn test_delete() {
let pool = testdb!();
let planned_event = Event::find_by_id(&pool, 1).await.unwrap();
planned_event.delete(&pool).await.unwrap();
let res = Event::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 0);
}
#[sqlx::test]
fn test_ics() {
let pool = testdb!();
let actual = Event::get_ics_feed(&pool).await;
assert_eq!("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:19700101T100000\r\nSUMMARY:test-planned-event \r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", actual);
}
}

83
src/model/family.rs Normal file
View File

@ -0,0 +1,83 @@
use serde::Serialize;
use sqlx::{sqlite::SqliteQueryResult, FromRow, SqlitePool};
use super::user::User;
#[derive(FromRow, Serialize, Clone)]
pub struct Family {
id: i64,
}
#[derive(Serialize, Clone)]
pub struct FamilyWithMembers {
id: i64,
names: Option<String>,
}
impl Family {
pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!(Self, "SELECT id FROM role")
.fetch_all(db)
.await
.unwrap()
}
pub async fn insert(db: &SqlitePool) -> i64 {
let result: SqliteQueryResult = sqlx::query("INSERT INTO family DEFAULT VALUES")
.execute(db)
.await
.unwrap();
result.last_insert_rowid()
}
pub async fn all_with_members(db: &SqlitePool) -> Vec<FamilyWithMembers> {
sqlx::query_as!(
FamilyWithMembers,
"
SELECT
family.id as id,
GROUP_CONCAT(user.name, ', ') as names
FROM family
LEFT JOIN
user ON family.id = user.family_id
GROUP BY family.id;"
)
.fetch_all(db)
.await
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id FROM family WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_opt_id(db: &SqlitePool, id: Option<i64>) -> Option<Self> {
if let Some(id) = id {
Self::find_by_id(db, id).await
} else {
None
}
}
pub async fn amount_family_members(&self, db: &SqlitePool) -> i32 {
sqlx::query!(
"SELECT COUNT(*) as count FROM user WHERE family_id = ?",
self.id
)
.fetch_one(db)
.await
.unwrap()
.count
}
pub async fn members(&self, db: &SqlitePool) -> Vec<User> {
sqlx::query_as!(User, "SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id FROM user WHERE family_id = ?", self.id)
.fetch_all(db)
.await
.unwrap()
}
}

View File

@ -1,11 +1,13 @@
use std::ops::DerefMut;
use chrono::{Datelike, NaiveDateTime, Utc};
use chrono::{Datelike, Local, NaiveDateTime};
use rocket::FromForm;
use serde::Serialize;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{boat::Boat, log::Log, rower::Rower, user::User};
use super::{
boat::Boat, log::Log, notification::Notification, role::Role, rower::Rower, user::User,
};
#[derive(FromRow, Serialize, Clone, Debug)]
pub struct Logbook {
@ -102,6 +104,7 @@ pub enum LogbookUpdateError {
SteeringPersonNotInRowers,
UserNotAllowedToUseBoat,
OnlyAllowedToEndTripsEndingToday,
TooFast(i64, i64),
}
#[derive(Debug, PartialEq)]
@ -116,7 +119,7 @@ pub enum LogbookCreateError {
BoatLocked,
BoatNotFound,
TooManyRowers(usize, usize),
RowerAlreadyOnWater(User),
RowerAlreadyOnWater(Box<User>),
RowerCreateError(i64, String),
ArrivalNotAfterDeparture,
SteeringPersonNotInRowers,
@ -124,6 +127,8 @@ pub enum LogbookCreateError {
NotYourEntry,
ArrivalSetButNotRemainingTwo,
OnlyAllowedToEndTripsEndingToday,
CantChangeHandoperatableStatusForThisBoat,
TooFast(i64, i64),
}
impl From<LogbookUpdateError> for LogbookCreateError {
@ -147,6 +152,7 @@ impl From<LogbookUpdateError> for LogbookCreateError {
LogbookUpdateError::OnlyAllowedToEndTripsEndingToday => {
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
}
LogbookUpdateError::TooFast(km, min) => LogbookCreateError::TooFast(km, min),
}
}
}
@ -226,8 +232,44 @@ ORDER BY departure DESC
ret
}
pub async fn completed_with_user(
db: &SqlitePool,
user: &User,
) -> Vec<LogbookWithBoatAndRowers> {
let logs = sqlx::query_as(
&format!("
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
FROM logbook
JOIN rower ON logbook.id = rower.logbook_id
WHERE arrival is not null AND rower_id = {}
ORDER BY departure DESC
", user.id)
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut ret = Vec::new();
for log in logs {
ret.push(LogbookWithBoatAndRowers {
rowers: Rower::for_log(db, &log).await,
boat: Boat::find_by_id(db, log.boat_id as i32).await.unwrap(),
shipmaster_user: User::find_by_id(db, log.shipmaster as i32).await.unwrap(),
steering_user: User::find_by_id(db, log.steering_person as i32)
.await
.unwrap(),
logbook: log,
});
}
ret
}
pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> {
let year = chrono::Utc::now().year();
let year = chrono::Local::now().year();
Self::completed_in_year(db, year).await
}
pub async fn completed_in_year(db: &SqlitePool, year: i32) -> Vec<LogbookWithBoatAndRowers> {
let logs = sqlx::query_as(
&format!("
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
@ -259,20 +301,28 @@ ORDER BY departure DESC
db: &SqlitePool,
mut log: LogToAdd,
created_by_user: &User,
) -> Result<(), LogbookCreateError> {
) -> Result<String, LogbookCreateError> {
let Some(boat) = Boat::find_by_id(db, log.boat_id).await else {
return Err(LogbookCreateError::BoatNotFound);
};
if log.shipmaster_only_steering != boat.default_shipmaster_only_steering
&& !boat.convert_handoperated_possible
{
return Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat);
}
if boat.amount_seats == 1 && log.rowers.is_empty() {
log.rowers = vec![created_by_user.id];
}
if boat.amount_seats == 1 {
log.shipmaster = Some(log.rowers[0]);
log.steering_person = Some(log.rowers[0]);
}
if let Ok(log_to_finalize) = TryInto::<LogToFinalize>::try_into(log.clone()) {
//TODO: fix clone() above
if !boat.shipmaster_allowed(created_by_user).await {
if !boat.shipmaster_allowed(db, created_by_user).await {
return Err(LogbookCreateError::UserNotAllowedToUseBoat);
}
@ -304,7 +354,7 @@ ORDER BY departure DESC
{
Ok(_) => {
tx.commit().await.unwrap();
Ok(())
Ok(String::new())
}
Err(a) => Err(a.into()),
};
@ -339,11 +389,11 @@ ORDER BY departure DESC
let user = User::find_by_id(db, *rower as i32).await.unwrap();
if user.on_water(db).await {
return Err(LogbookCreateError::RowerAlreadyOnWater(user));
return Err(LogbookCreateError::RowerAlreadyOnWater(Box::new(user)));
}
}
if !boat.shipmaster_allowed(created_by_user).await {
if !boat.shipmaster_allowed(db, created_by_user).await {
return Err(LogbookCreateError::UserNotAllowedToUseBoat);
}
@ -376,7 +426,15 @@ ORDER BY departure DESC
tx.commit().await.unwrap();
Ok(())
let mut ret = String::new();
for rower in &log.rowers {
let user = User::find_by_id(db, *rower as i32).await.unwrap();
if let Some(msg) = user.close_thousands_trip(db).await {
ret.push_str(&format!("{msg}"));
}
}
Ok(ret)
}
pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> {
@ -434,7 +492,7 @@ ORDER BY departure DESC
mut log: LogToFinalize,
) -> Result<(), LogbookUpdateError> {
//TODO: extract common tests with `create()`
if user.id != self.shipmaster {
if !user.has_role_tx(db, "Vorstand").await && user.id != self.shipmaster {
return Err(LogbookUpdateError::NotYourEntry);
}
@ -452,7 +510,7 @@ ORDER BY departure DESC
return Err(LogbookUpdateError::SteeringPersonNotInRowers);
}
if !boat.shipmaster_allowed(user).await && self.shipmaster != user.id {
if !boat.shipmaster_allowed_tx(db, user).await && self.shipmaster != user.id {
//second part: shipmaster has entered a different user, then the user should be able to
//`home` it
return Err(LogbookUpdateError::UserNotAllowedToUseBoat);
@ -467,11 +525,27 @@ ORDER BY departure DESC
let dep = NaiveDateTime::parse_from_str(&log.departure, "%Y-%m-%dT%H:%M").unwrap();
let arr = NaiveDateTime::parse_from_str(&log.arrival, "%Y-%m-%dT%H:%M").unwrap();
if arr.timestamp() <= dep.timestamp() {
if arr.and_utc().timestamp() < dep.and_utc().timestamp() {
return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
}
let today = Utc::now().date_naive();
if arr.date() != today && !user.is_admin {
let duration_in_mins = (arr.and_utc().timestamp() - dep.and_utc().timestamp()) / 60;
// Not possible to row < 1 min / 500 m = < 2 min / km
let possible_distance_km = duration_in_mins / 2;
if log.distance_in_km > possible_distance_km {
return Err(LogbookUpdateError::TooFast(
log.distance_in_km,
duration_in_mins,
));
}
let today = Local::now().date_naive();
let day_diff = today - arr.date();
let day_diff = day_diff.num_days();
if day_diff >= 7 && !user.has_role_tx(db, "admin").await {
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
}
if day_diff < 0 && !user.has_role_tx(db, "admin").await {
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
}
@ -482,6 +556,24 @@ ORDER BY departure DESC
Rower::create(db, self.id, *rower)
.await
.map_err(|e| LogbookUpdateError::RowerCreateError(*rower, e.to_string()))?;
let user = User::find_by_id_tx(db, *rower as i32).await.unwrap();
Notification::create_with_tx(
db,
&user,
&format!(
"Ausfahrt am {}.{}.{}; Ziel: {} ({} km)",
dep.day(),
dep.month(),
dep.year(),
log.destination,
log.distance_in_km
),
"Neuer Logbucheintrag",
None,
None,
)
.await;
}
sqlx::query!(
@ -500,13 +592,41 @@ ORDER BY departure DESC
.execute(db.deref_mut())
.await.unwrap(); //TODO: fixme
let duration = arr - dep;
if duration.num_days() > 0 {
let vorstand = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
Notification::create_for_role_tx(
db,
&vorstand,
&format!("'{}' hat eine mehrtägige Ausfahrt vom {} bis {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,log.departure, log.arrival, log.distance_in_km, log.destination, log.comments.clone().unwrap_or("".into())),
"Mehrtägige Ausfahrt eingetragen",
None,None
).await;
}
if boat.external {
let vorstand = Role::find_by_name_tx(db, "Vorstand").await.unwrap();
Notification::create_for_role_tx(
db,
&vorstand,
&format!("'{}' hat eine Ausfahrt mit externem Boot '{}' am {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,boat.name,log.departure,log.distance_in_km, log.destination, log.comments.unwrap_or("".into())),
"Ausfahrt mit externem Boot eingetragen",
None,None,
).await;
}
Ok(())
}
pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> {
Log::create(db, format!("{user:?} deleted trip: {self:?}")).await;
Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await;
if user.is_admin || user.id == self.shipmaster {
if user.has_role(db, "admin").await
|| user.has_role(db, "Vorstand").await
|| user.id == self.shipmaster
{
sqlx::query!("DELETE FROM logbook WHERE id=?", self.id)
.execute(db)
.await
@ -523,6 +643,7 @@ mod test {
use crate::model::user::User;
use crate::testdb;
use chrono::Duration;
use sqlx::SqlitePool;
#[sqlx::test]
@ -574,7 +695,7 @@ mod test {
fn test_succ_create() {
let pool = testdb!();
Logbook::create(
let msg = Logbook::create(
&pool,
LogToAdd {
boat_id: 3,
@ -592,7 +713,62 @@ mod test {
&User::find_by_id(&pool, 4).await.unwrap(),
)
.await
.unwrap()
.unwrap();
assert_eq!(msg, String::from(""));
}
#[sqlx::test]
fn test_succ_create_with_thousands_msg() {
let pool = testdb!();
let logbook = Logbook::find_by_id(&pool, 1).await.unwrap();
let user = User::find_by_id(&pool, 2).await.unwrap();
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let start_date = chrono::Local::now() - Duration::days(3);
let start_date = start_date.format("%Y-%m-%d").to_string();
logbook
.home(
&pool,
&user,
super::LogToFinalize {
destination: "new-destination".into(),
distance_in_km: 995,
comments: Some("Perfect water".into()),
logtype: None,
rowers: vec![2],
shipmaster: Some(2),
steering_person: Some(2),
shipmaster_only_steering: false,
departure: format!("{}T10:00", start_date),
arrival: format!("{}T12:00", current_date),
},
)
.await
.unwrap();
let msg = Logbook::create(
&pool,
LogToAdd {
boat_id: 3,
shipmaster: Some(2),
steering_person: Some(2),
shipmaster_only_steering: false,
departure: "2128-05-20T12:00".into(),
arrival: None,
destination: None,
distance_in_km: None,
comments: None,
logtype: None,
rowers: vec![2],
},
&User::find_by_id(&pool, 1).await.unwrap(),
)
.await
.unwrap();
assert_eq!(
msg,
String::from(" • rower braucht nur mehr 5 km bis die 1000 km voll sind 🤑")
);
}
#[sqlx::test]

354
src/model/mail.rs Normal file
View File

@ -0,0 +1,354 @@
use std::{error::Error, fs};
use lettre::{
message::{header::ContentType, Attachment, MultiPart, SinglePart},
transport::smtp::authentication::Credentials,
Message, SmtpTransport, Transport,
};
use sqlx::SqlitePool;
use crate::tera::admin::mail::MailToSend;
use super::{family::Family, log::Log, role::Role, user::User};
pub struct Mail {}
impl Mail {
pub async fn send_single(
db: &SqlitePool,
to: &str,
subject: &str,
body: String,
smtp_pw: &str,
) -> Result<(), String> {
let mut email = Message::builder()
.from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap(),
)
.reply_to(
"ASKÖ Ruderverein Donau Linz <info@rudernlinz.at>"
.parse()
.unwrap(),
)
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap());
let splitted = to.split(',');
for single_rec in splitted {
match single_rec.parse() {
Ok(new_bcc_mail) => email = email.bcc(new_bcc_mail),
Err(_) => {
Log::create(
db,
format!("Mail not sent to {single_rec}, because it could not be parsed"),
)
.await;
return Err(format!(
"Mail nicht versandt, da '{single_rec}' keine gültige Mailadresse ist."
));
}
}
}
let email = email
.subject(subject)
.header(ContentType::TEXT_PLAIN)
.body(body)
.unwrap();
let creds = Credentials::new("no-reply@rudernlinz.at".to_owned(), smtp_pw.into());
let mailer = SmtpTransport::relay("mail.your-server.de")
.unwrap()
.credentials(creds)
.build();
// Send the email
mailer.send(&email).unwrap();
Ok(())
}
pub async fn send(db: &SqlitePool, data: MailToSend<'_>, smtp_pw: String) -> bool {
let mut email = Message::builder()
.from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap(),
)
.reply_to(
"ASKÖ Ruderverein Donau Linz <info@rudernlinz.at>"
.parse()
.unwrap(),
)
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap());
let role = Role::find_by_id(db, data.role_id).await.unwrap();
for rec in role.mails_from_role(db).await {
let splitted = rec.split(',');
for single_rec in splitted {
match single_rec.parse() {
Ok(new_bcc_mail) => email = email.bcc(new_bcc_mail),
Err(_) => {
Log::create(
db,
format!("Mail not sent to {rec}, because it could not be parsed"),
)
.await;
}
}
}
}
let mut multipart = MultiPart::mixed().singlepart(SinglePart::plain(data.body));
for temp_file in &data.files {
let content = fs::read(temp_file.path().unwrap()).unwrap();
let media_type = format!("{}", temp_file.content_type().unwrap().media_type());
let content_type = ContentType::parse(&media_type).unwrap();
if let Some(name) = temp_file.name() {
let attachment = Attachment::new(format!(
"{}.{}",
name,
temp_file.content_type().unwrap().extension().unwrap()
))
.body(content, content_type);
multipart = multipart.singlepart(attachment);
}
}
let email = email.subject(data.subject).multipart(multipart).unwrap();
let creds = Credentials::new("no-reply@rudernlinz.at".to_owned(), smtp_pw);
let mailer = SmtpTransport::relay("mail.your-server.de")
.unwrap()
.credentials(creds)
.build();
// Send the email
match mailer.send(&email) {
Ok(_) => return true,
Err(e) => println!("{:?}", e.source()),
};
false
}
pub async fn fees(db: &SqlitePool, smtp_pw: String) {
let users = User::all_payer_groups(db).await;
for user in users {
if !user.has_role(db, "paid").await {
let mut is_family = false;
let mut send_to = String::new();
match Family::find_by_opt_id(db, user.family_id).await {
Some(family) => {
is_family = true;
for member in family.members(db).await {
if let Some(mail) = member.mail {
send_to.push_str(&format!("{mail},"))
}
}
}
None => {
if let Some(mail) = &user.mail {
send_to.push_str(mail)
}
}
}
let fees = user.fee(db).await;
if let Some(fees) = fees {
let mut content = format!(
"Liebes Vereinsmitglied, \n\n\
dein Vereinsbeitrag für das aktuelle Jahr beträgt {}",
fees.sum_in_cents / 100,
);
if fees.parts.len() == 1 {
content.push_str(&format!(" ({}).\n", fees.parts[0].0))
} else {
content.push_str(". Dieser setzt sich aus folgenden Teilen zusammen: \n");
for (desc, fee_in_cents) in fees.parts {
content.push_str(&format!("- {}: {}\n", desc, fee_in_cents / 100))
}
}
if is_family {
content.push_str(&format!(
"Dieser gilt für die gesamte Familie ({}).\n",
fees.name
))
}
content.push_str("\nBitte überweise diesen auf folgendes Konto: IBAN: AT13 1200 0804 1300 1200. Auf https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.\n\n\
Falls die Berechnung nicht stimmt (korrekte Preise findest du unter https://rudernlinz.at/unser-verein/gebuhren/) melde dich bitte bei it@rudernlinz.at. @Studenten: Bitte die aktuelle Studienbestätigung an it@rudernlinz.at schicken.\n\n\
Wenn du die Vereinsgebühren schon bezahlt hast, kannst du diese Mail einfach ignorieren.\n\n
Beste Grüße\n\
Der Vorstand
");
let mut email = Message::builder()
.from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap(),
)
.reply_to(
"ASKÖ Ruderverein Donau Linz <it@rudernlinz.at>"
.parse()
.unwrap(),
)
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap());
let splitted = send_to.split(',');
let mut send_mail = false;
for single_rec in splitted {
let single_rec = single_rec.trim();
match single_rec.parse() {
Ok(val) => {
email = email.bcc(val);
send_mail = true;
}
Err(_) => {
println!("Error in mail: {single_rec}");
}
}
}
if send_mail {
let email = email
.subject("ASKÖ Ruderverein Donau Linz | Vereinsgebühren")
.header(ContentType::TEXT_PLAIN)
.body(content)
.unwrap();
let creds =
Credentials::new("no-reply@rudernlinz.at".to_owned(), smtp_pw.clone());
let mailer = SmtpTransport::relay("mail.your-server.de")
.unwrap()
.credentials(creds)
.build();
// Send the email
mailer.send(&email).unwrap();
}
}
}
}
}
pub async fn fees_final(db: &SqlitePool, smtp_pw: String) {
let users = User::all_payer_groups(db).await;
for user in users {
if let Some(fee) = user.fee(db).await {
if !fee.paid {
let mut is_family = false;
let mut send_to = String::new();
match Family::find_by_opt_id(db, user.family_id).await {
Some(family) => {
is_family = true;
for member in family.members(db).await {
if let Some(mail) = member.mail {
send_to.push_str(&format!("{mail},"))
}
}
}
None => {
if let Some(mail) = &user.mail {
send_to.push_str(mail)
}
}
}
let fees = user.fee(db).await;
if let Some(fees) = fees {
let mut content = format!(
"Liebes Vereinsmitglied, \n\n\
wir möchten darauf hinweisen, dass wir deinen Mitgliedsbeitrag für das laufende Jahr bislang nicht verbuchen konnten. Es besteht die Möglichkeit, dass es sich hierbei um ein Versehen unsererseits handelt. Solltest du den Betrag bereits überwiesen haben, bitte kurz auf diese E-Mail antworten, damit wir es richtigstellen können.
Falls die Zahlung noch nicht erfolgt ist, bitten wir um umgehende Überweisung des ausstehenden Betrags, spätestens jedoch bis zum 31. März, auf unser Bankkonto.\n\n\
Dein Vereinsbeitrag für das aktuelle Jahr beträgt {}",
fees.sum_in_cents / 100,
);
if fees.parts.len() == 1 {
content.push_str(&format!(" ({}).\n", fees.parts[0].0))
} else {
content
.push_str(". Dieser setzt sich aus folgenden Teilen zusammen: \n");
for (desc, fee_in_cents) in fees.parts {
content.push_str(&format!("- {}: {}\n", desc, fee_in_cents / 100))
}
}
if is_family {
content.push_str(&format!(
"Dieser gilt für die gesamte Familie ({}).\n",
fees.name
))
}
content.push_str("\n\
Gemäß § 7 Abs. 3 lit. c unseres Status behalten wir uns vor, bei ausbleibender Zahlung die Mitgliedschaft zu beenden. Dies möchten wir vermeiden und hoffen auf deine Unterstützung.\n\n\
Bei Fragen oder Problemen stehen wir gerne zur Verfügung.
Bankverbindung: IBAN: AT13 1200 0804 1300 1200 (Unter https://app.rudernlinz.at/planned findest du einen QR Code, den du mit deiner Bankapp scannen kannst um alle Eingaben bereits ausgefüllt zu haben.)
Mit freundlichen Grüßen,\n\
Der Vorstand");
let mut email = Message::builder()
.from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap(),
)
.reply_to(
"ASKÖ Ruderverein Donau Linz <it@rudernlinz.at>"
.parse()
.unwrap(),
)
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap());
let splitted = send_to.split(',');
let mut send_mail = false;
for single_rec in splitted {
let single_rec = single_rec.trim();
match single_rec.parse() {
Ok(val) => {
email = email.bcc(val);
send_mail = true;
}
Err(_) => {
println!("Error in mail: {single_rec}");
}
}
}
if send_mail {
let email = email
.subject("Mahnung Vereinsgebühren | ASKÖ Ruderverein Donau Linz")
.header(ContentType::TEXT_PLAIN)
.body(content)
.unwrap();
let creds = Credentials::new(
"no-reply@rudernlinz.at".to_owned(),
smtp_pw.clone(),
);
let mailer = SmtpTransport::relay("mail.your-server.de")
.unwrap()
.credentials(creds)
.build();
// Send the email
mailer.send(&email).unwrap();
}
}
}
}
}
}
}

View File

@ -1,33 +1,48 @@
use chrono::NaiveDate;
use serde::Serialize;
use sqlx::SqlitePool;
use waterlevel::WaterlevelDay;
use self::{
planned_event::{PlannedEvent, PlannedEventWithUserAndTriptype},
event::{Event, EventWithUserAndTriptype},
trip::{Trip, TripWithUserAndType},
waterlevel::Waterlevel,
weather::Weather,
};
pub mod boat;
pub mod boatdamage;
pub mod boathouse;
pub mod boatreservation;
pub mod event;
pub mod family;
pub mod location;
pub mod log;
pub mod logbook;
pub mod logtype;
pub mod planned_event;
pub mod mail;
pub mod notification;
pub mod role;
pub mod rower;
pub mod stat;
pub mod trailer;
pub mod trailerreservation;
pub mod trip;
pub mod tripdetails;
pub mod triptype;
pub mod user;
pub mod usertrip;
pub mod waterlevel;
pub mod weather;
#[derive(Serialize)]
#[derive(Serialize, Debug)]
pub struct Day {
day: NaiveDate,
planned_events: Vec<PlannedEventWithUserAndTriptype>,
events: Vec<EventWithUserAndTriptype>,
trips: Vec<TripWithUserAndType>,
is_pinned: bool,
max_waterlevel: Option<WaterlevelDay>,
weather: Option<Weather>,
}
impl Day {
@ -35,23 +50,27 @@ impl Day {
if is_pinned {
Self {
day,
planned_events: PlannedEvent::get_pinned_for_day(db, day).await,
events: Event::get_pinned_for_day(db, day).await,
trips: Trip::get_pinned_for_day(db, day).await,
is_pinned,
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
weather: Weather::find_by_day(db, day).await,
}
} else {
Self {
day,
planned_events: PlannedEvent::get_for_day(db, day).await,
events: Event::get_for_day(db, day).await,
trips: Trip::get_for_day(db, day).await,
is_pinned,
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
weather: Weather::find_by_day(db, day).await,
}
}
}
pub async fn new_guest(db: &SqlitePool, day: NaiveDate, is_pinned: bool) -> Self {
let mut day = Self::new(db, day, is_pinned).await;
day.planned_events.retain(|e| e.planned_event.allow_guests);
day.events.retain(|e| e.event.allow_guests);
day.trips.retain(|t| t.trip.allow_guests);
day

View File

@ -1,36 +1,295 @@
use chrono::{DateTime, Local, NaiveDateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use std::ops::DerefMut;
#[derive(FromRow, Debug, Serialize, Deserialize)]
use chrono::NaiveDateTime;
use regex::Regex;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{role::Role, user::User};
#[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
pub struct Notification {
pub id: i64,
pub user_id: i64,
pub message: String,
pub read_at: NaiveDateTime,
pub read_at: Option<NaiveDateTime>,
pub created_at: NaiveDateTime,
pub category: String,
pub link: Option<String>,
pub action_after_reading: Option<String>,
}
impl Notification {
//pub async fn create(db: &SqlitePool, msg: String) -> bool {
// sqlx::query!("INSERT INTO log(msg) VALUES (?)", msg,)
// .execute(db)
// .await
// .is_ok()
//}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, user_id, message, read_at, created_at, category, link, action_after_reading FROM notification WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn create_with_tx(
db: &mut Transaction<'_, Sqlite>,
user: &User,
message: &str,
category: &str,
link: Option<&str>,
action_after_reading: Option<&str>,
) {
sqlx::query!(
"INSERT INTO notification(user_id, message, category, link, action_after_reading) VALUES (?, ?, ?, ?, ?)",
user.id,
message,
category,
link,
action_after_reading
)
.execute(db.deref_mut())
.await
.unwrap();
}
async fn for_user(db: &SqlitePool, user: &User) -> Vec<Self> {
sqlx::query_as!(
Log,
pub async fn create(
db: &SqlitePool,
user: &User,
message: &str,
category: &str,
link: Option<&str>,
action_after_reading: Option<&str>,
) {
let mut tx = db.begin().await.unwrap();
Self::create_with_tx(&mut tx, user, message, category, link, action_after_reading).await;
tx.commit().await.unwrap();
}
pub async fn create_for_role_tx(
db: &mut Transaction<'_, Sqlite>,
role: &Role,
message: &str,
category: &str,
link: Option<&str>,
action_after_reading: Option<&str>,
) {
let users = User::all_with_role_tx(db, role).await;
for user in users {
Self::create_with_tx(db, &user, message, category, link, action_after_reading).await;
}
}
pub async fn create_for_role(
db: &SqlitePool,
role: &Role,
message: &str,
category: &str,
link: Option<&str>,
action_after_reading: Option<&str>,
) {
let mut tx = db.begin().await.unwrap();
Self::create_for_role_tx(&mut tx, role, message, category, link, action_after_reading)
.await;
tx.commit().await.unwrap();
}
pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<Self> {
let rows = sqlx::query!(
"
SELECT id, user_id, message, read_at, category
FROM notification
WHERE user_id = {}
",
SELECT id, user_id, message, read_at, datetime(created_at, 'localtime') as created_at, category, link, action_after_reading FROM notification
WHERE
user_id = ?
AND (
read_at IS NULL
OR read_at >= datetime('now', '-14 days')
)
AND created_at is not NULL
ORDER BY read_at DESC, created_at DESC;
",
user.id
)
.fetch_all(db)
.await
.unwrap()
.unwrap();
rows.into_iter()
.map(|rec| Notification {
id: rec.id,
user_id: rec.user_id,
message: rec.message,
read_at: rec.read_at,
created_at: NaiveDateTime::parse_from_str(
&rec.created_at.unwrap(),
"%Y-%m-%d %H:%M:%S",
)
.unwrap(),
category: rec.category,
link: rec.link,
action_after_reading: rec.action_after_reading,
})
.collect()
}
pub async fn mark_read(self, db: &SqlitePool) {
sqlx::query!(
"UPDATE notification SET read_at=CURRENT_TIMESTAMP WHERE id=?",
self.id
)
.execute(db)
.await
.unwrap();
if let Some(action) = self.action_after_reading.as_ref() {
// User read notification about cancelled trip/event
let re = Regex::new(r"^remove_user_trip_with_trip_details_id:(\d+)$").unwrap();
if let Some(caps) = re.captures(action) {
if let Some(matched) = caps.get(1) {
if let Ok(number) = matched.as_str().parse::<i32>() {
let _ = sqlx::query!(
"DELETE FROM user_trip WHERE user_id = ? AND trip_details_id = ?",
self.user_id,
number
)
.execute(db)
.await
.unwrap();
}
}
}
// Cox read notification about cancelled event
let re = Regex::new(r"^remove_trip_by_event:(\d+)$").unwrap();
if let Some(caps) = re.captures(action) {
if let Some(matched) = caps.get(1) {
if let Ok(number) = matched.as_str().parse::<i32>() {
let _ = sqlx::query!(
"DELETE FROM trip WHERE cox_id = ? AND planned_event_id = ?",
self.user_id,
number
)
.execute(db)
.await
.unwrap();
}
}
}
}
}
pub(crate) async fn delete_by_action(db: &sqlx::Pool<Sqlite>, action: &str) {
sqlx::query!(
"DELETE FROM notification WHERE action_after_reading=? and read_at is null",
action
)
.execute(db)
.await
.unwrap();
}
}
#[cfg(test)]
mod test {
use crate::{
model::{
event::{Event, EventUpdate, Registration},
notification::Notification,
trip::Trip,
tripdetails::{TripDetails, TripDetailsToAdd},
user::{CoxUser, User},
usertrip::UserTrip,
},
testdb,
};
use sqlx::SqlitePool;
#[sqlx::test]
fn event_canceled() {
let pool = testdb!();
// Create event
let add_tripdetails = TripDetailsToAdd {
planned_starting_time: "10:00",
max_people: 4,
day: "1970-02-01".into(),
notes: None,
trip_type: None,
allow_guests: false,
always_show: false,
};
let tripdetails_id = TripDetails::create(&pool, add_tripdetails).await;
let trip_details = TripDetails::find_by_id(&pool, tripdetails_id)
.await
.unwrap();
Event::create(&pool, "new-event".into(), 2, &trip_details).await;
let event = Event::find_by_trip_details(&pool, trip_details.id)
.await
.unwrap();
// Rower + Cox joins
let rower = User::find_by_name(&pool, "rower").await.unwrap();
UserTrip::create(&pool, &rower, &trip_details, None)
.await
.unwrap();
let cox = CoxUser::new(&pool, User::find_by_name(&pool, "cox").await.unwrap())
.await
.unwrap();
Trip::new_join(&pool, &cox, &event).await.unwrap();
// Cancel Event
let cancel_update = EventUpdate {
name: &event.name,
planned_amount_cox: event.planned_amount_cox as i32,
max_people: 0,
notes: event.notes.as_deref(),
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
};
event.update(&pool, &cancel_update).await;
// Rower received notification
let notifications = Notification::for_user(&pool, &rower).await;
let rower_notification = notifications[0].clone();
assert_eq!(rower_notification.category, "Absage Ausfahrt");
assert_eq!(
rower_notification.action_after_reading.as_deref(),
Some("remove_user_trip_with_trip_details_id:3")
);
// Cox received notification
let notifications = Notification::for_user(&pool, &cox.user).await;
let cox_notification = notifications[0].clone();
assert_eq!(cox_notification.category, "Absage Ausfahrt");
assert_eq!(
cox_notification.action_after_reading.as_deref(),
Some("remove_trip_by_event:2")
);
// Notification removed if cancellation is cancelled
let update = EventUpdate {
name: &event.name,
planned_amount_cox: event.planned_amount_cox as i32,
max_people: 3,
notes: event.notes.as_deref(),
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
};
event.update(&pool, &update).await;
assert!(Notification::for_user(&pool, &rower).await.is_empty());
assert!(Notification::for_user(&pool, &cox.user).await.is_empty());
// Cancel event again
event.update(&pool, &cancel_update).await;
// Rower is removed if notification is accepted
assert!(event.is_rower_registered(&pool, &rower).await);
rower_notification.mark_read(&pool).await;
assert!(!event.is_rower_registered(&pool, &rower).await);
// Cox is removed if notification is accepted
let registration = Registration::all_cox(&pool, event.id).await;
assert_eq!(registration.len(), 1);
assert_eq!(registration[0].name, "cox");
cox_notification.mark_read(&pool).await;
let registration = Registration::all_cox(&pool, event.id).await;
assert!(registration.is_empty());
}
}

View File

@ -1,323 +0,0 @@
use std::io::Write;
use chrono::NaiveDate;
use ics::{
properties::{DtStart, Summary},
Event, ICalendar,
};
use serde::Serialize;
use sqlx::{FromRow, SqlitePool};
use super::{tripdetails::TripDetails, triptype::TripType, user::User};
#[derive(Serialize, Clone, FromRow, Debug, PartialEq)]
pub struct PlannedEvent {
pub id: i64,
pub name: String,
planned_amount_cox: i64,
trip_details_id: i64,
pub planned_starting_time: String,
max_people: i64,
pub day: String,
pub notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>,
always_show: bool,
is_locked: bool,
}
#[derive(Serialize, Debug)]
pub struct PlannedEventWithUserAndTriptype {
#[serde(flatten)]
pub planned_event: PlannedEvent,
trip_type: Option<TripType>,
cox_needed: bool,
cox: Vec<Registration>,
rower: Vec<Registration>,
}
//TODO: move to appropriate place
#[derive(Serialize, Debug)]
pub struct Registration {
pub name: String,
pub registered_at: String,
pub is_guest: bool,
pub is_real_guest: bool,
}
impl Registration {
pub async fn all_rower(db: &SqlitePool, trip_details_id: i64) -> Vec<Registration> {
sqlx::query!(
r#"
SELECT
(SELECT name FROM user WHERE user_trip.user_id = user.id) as "name?",
user_note,
user_id,
(SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at,
(SELECT is_guest FROM user WHERE user_trip.user_id = user.id) as is_guest
FROM user_trip WHERE trip_details_id = ?
"#,
trip_details_id,
)
.fetch_all(db)
.await
.unwrap()
.into_iter()
.map(|r| Registration {
name: r.name.or(r.user_note).unwrap(), //Ok, either name or user_note needs to be set
registered_at: r.registered_at,
is_guest: r.is_guest,
is_real_guest: r.user_id.is_none(),
})
.collect()
}
pub async fn all_cox(db: &SqlitePool, trip_details_id: i64) -> Vec<Registration> {
//TODO: switch to join
sqlx::query!(
"
SELECT
(SELECT name FROM user WHERE cox_id = id) as name,
(SELECT created_at FROM user WHERE cox_id = id) as registered_at,
(SELECT is_guest FROM user WHERE cox_id = id) as is_guest
FROM trip WHERE planned_event_id = ?
",
trip_details_id
)
.fetch_all(db)
.await
.unwrap()
.into_iter()
.map(|r| Registration {
name: r.name,
registered_at: r.registered_at,
is_guest: r.is_guest,
is_real_guest: false,
})
.collect() //Okay, as PlannedEvent can only be created with proper DB backing
}
}
impl PlannedEvent {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT
planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE planned_event.id like ?
",
id
)
.fetch_one(db)
.await
.ok()
}
pub async fn get_pinned_for_day(
db: &SqlitePool,
day: NaiveDate,
) -> Vec<PlannedEventWithUserAndTriptype> {
let mut events = Self::get_for_day(db, day).await;
events.retain(|e| e.planned_event.always_show);
events
}
pub async fn get_for_day(
db: &SqlitePool,
day: NaiveDate,
) -> Vec<PlannedEventWithUserAndTriptype> {
let day = format!("{day}");
let events = sqlx::query_as!(
PlannedEvent,
"SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE day=?",
day
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut ret = Vec::new();
for event in events {
let cox = Registration::all_cox(db, event.trip_details_id).await;
let mut trip_type = None;
if let Some(trip_type_id) = event.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await;
}
ret.push(PlannedEventWithUserAndTriptype {
cox_needed: event.planned_amount_cox > cox.len() as i64,
cox,
rower: Registration::all_rower(db, event.trip_details_id).await,
planned_event: event,
trip_type,
});
}
ret
}
pub async fn all(db: &SqlitePool) -> Vec<PlannedEvent> {
sqlx::query_as!(
PlannedEvent,
"SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, always_show, max_people, day, notes, allow_guests, trip_type_id, is_locked
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
}
//TODO: add tests
pub async fn is_rower_registered(&self, db: &SqlitePool, user: &User) -> bool {
let is_rower = sqlx::query!(
"SELECT count(*) as amount
FROM user_trip
WHERE trip_details_id =
(SELECT trip_details_id FROM planned_event WHERE id = ?)
AND user_id = ?",
self.id,
user.id
)
.fetch_one(db)
.await
.unwrap(); //Okay, bc planned_event can only be created with proper DB backing
is_rower.amount > 0
}
pub async fn create(
db: &SqlitePool,
name: &str,
planned_amount_cox: i32,
trip_details: TripDetails,
) {
sqlx::query!(
"INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
name,
planned_amount_cox,
trip_details.id,
)
.execute(db)
.await
.unwrap(); //Okay, as TripDetails can only be created with proper DB backing
}
//TODO: create unit test
pub async fn update(
&self,
db: &SqlitePool,
planned_amount_cox: i32,
max_people: i32,
notes: Option<&str>,
always_show: bool,
is_locked: bool,
) {
sqlx::query!(
"UPDATE planned_event SET planned_amount_cox = ? WHERE id = ?",
planned_amount_cox,
self.id
)
.execute(db)
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ? WHERE id = ?",
max_people,
notes,
always_show,
is_locked,
self.trip_details_id
)
.execute(db)
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM planned_event WHERE id = ?", self.id)
.execute(db)
.await
.unwrap(); //Okay, as PlannedEvent can only be created with proper DB backing
}
pub async fn get_ics_feed(db: &SqlitePool) -> String {
let mut calendar = ICalendar::new("2.0", "ics-rs");
let events = PlannedEvent::all(db).await;
for event in events {
let mut vevent = Event::new(format!("{}@rudernlinz.at", event.id), "19900101T180000");
vevent.push(DtStart::new(format!(
"{}T{}00",
event.day.replace('-', ""),
event.planned_starting_time.replace(':', "")
)));
vevent.push(Summary::new(event.name));
calendar.add_event(vevent);
}
let mut buf = Vec::new();
write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap()
}
pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails {
TripDetails::find_by_id(db, self.trip_details_id)
.await
.unwrap() //ok, not null in db
}
}
#[cfg(test)]
mod test {
use crate::{model::tripdetails::TripDetails, testdb};
use super::PlannedEvent;
use chrono::NaiveDate;
use sqlx::SqlitePool;
#[sqlx::test]
fn test_get_day() {
let pool = testdb!();
let res =
PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 1);
}
#[sqlx::test]
fn test_create() {
let pool = testdb!();
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
PlannedEvent::create(&pool, "new-event".into(), 2, trip_details).await;
let res =
PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 2);
}
#[sqlx::test]
fn test_delete() {
let pool = testdb!();
let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap();
planned_event.delete(&pool).await;
let res =
PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;
assert_eq!(res.len(), 0);
}
#[sqlx::test]
fn test_ics() {
let pool = testdb!();
let actual = PlannedEvent::get_ics_feed(&pool).await;
assert_eq!("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:ics-rs\r\nBEGIN:VEVENT\r\nUID:1@rudernlinz.at\r\nDTSTAMP:19900101T180000\r\nDTSTART:19700101T100000\r\nSUMMARY:test-planned-event\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", actual);
}
}

90
src/model/role.rs Normal file
View File

@ -0,0 +1,90 @@
use std::ops::DerefMut;
use serde::Serialize;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Serialize, Clone)]
pub struct Role {
pub(crate) id: i64,
pub(crate) name: String,
}
impl Role {
pub async fn all(db: &SqlitePool) -> Vec<Role> {
sqlx::query_as!(Role, "SELECT id, name FROM role")
.fetch_all(db)
.await
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, name: i32) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT id, name
FROM role
WHERE id like ?
",
name
)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_name(db: &SqlitePool, name: &str) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT id, name
FROM role
WHERE name like ?
",
name
)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_name_tx(db: &mut Transaction<'_, Sqlite>, name: &str) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT id, name
FROM role
WHERE name like ?
",
name
)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn names_from_role(&self, db: &SqlitePool) -> Vec<String> {
let query = format!(
"SELECT u.name
FROM user u
JOIN user_role ur ON u.id = ur.user_id
JOIN role r ON ur.role_id = r.id
WHERE r.id = {} AND deleted=0;",
self.id
);
sqlx::query_scalar(&query).fetch_all(db).await.unwrap()
}
pub async fn mails_from_role(&self, db: &SqlitePool) -> Vec<String> {
let query = format!(
"SELECT u.mail
FROM user u
JOIN user_role ur ON u.id = ur.user_id
JOIN role r ON ur.role_id = r.id
WHERE r.id = {} AND deleted=0;",
self.id
);
sqlx::query_scalar(&query).fetch_all(db).await.unwrap()
}
}

View File

@ -16,7 +16,7 @@ impl Rower {
sqlx::query_as!(
User,
"
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id
FROM user
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
",

View File

@ -1,56 +1,188 @@
use std::collections::HashMap;
use crate::model::user::User;
use chrono::Datelike;
use serde::Serialize;
use sqlx::{FromRow, Row, SqlitePool};
#[derive(FromRow, Serialize, Clone)]
pub struct Stat {
name: String,
rowed_km: i32,
use super::boat::Boat;
#[derive(Serialize, Clone)]
pub struct BoatStat {
pot_years: Vec<i32>,
boats: Vec<SingleBoatStat>,
}
impl 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)
sqlx::query(&format!(
#[derive(Serialize, Clone)]
pub struct SingleBoatStat {
name: String,
location: String,
owner: String,
years: HashMap<String, i32>,
}
impl BoatStat {
pub async fn get(db: &SqlitePool) -> BoatStat {
let mut years = Vec::new();
let mut boat_stats_map: HashMap<String, SingleBoatStat> = HashMap::new();
let rows = sqlx::query(
"
SELECT (SELECT name FROM boat WHERE id=logbook.boat_id) as name, CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km
FROM logbook
WHERE arrival LIKE '{year}-%'
GROUP BY boat_id
ORDER BY rowed_km DESC;
")
SELECT
boat.id,
location.name AS location,
CAST(strftime('%Y', COALESCE(arrival, 'now')) AS INTEGER) AS year,
CAST(SUM(COALESCE(distance_in_km, 0)) AS INTEGER) AS rowed_km
FROM
boat
LEFT JOIN
logbook ON boat.id = logbook.boat_id AND logbook.arrival IS NOT NULL
LEFT JOIN
location ON boat.location_id = location.id
WHERE
not boat.external
GROUP BY
boat.id, year
ORDER BY
boat.name, year DESC;
",
)
.fetch_all(db)
.await
.unwrap();
for row in rows {
let id: i32 = row.get("id");
let boat = Boat::find_by_id(db, id).await.unwrap();
let owner = if let Some(owner) = boat.owner(db).await {
owner.name
} else {
String::from("Verein")
};
let name = boat.name.clone();
let location: String = row.get("location");
let year: i32 = row.get("year");
if year == 0 {
continue; // Boat still on water
}
if !years.contains(&year) {
years.push(year);
}
let year: String = format!("{year}");
let rowed_km: i32 = row.get("rowed_km");
let boat_stat = boat_stats_map
.entry(name.clone())
.or_insert(SingleBoatStat {
name,
location,
owner,
years: HashMap::new(),
});
boat_stat.years.insert(year, rowed_km);
}
BoatStat {
pot_years: years,
boats: boat_stats_map.into_values().collect(),
}
}
}
#[derive(FromRow, Serialize, Clone)]
pub struct Stat {
name: String,
pub(crate) rowed_km: i32,
}
impl Stat {
pub async fn guest(db: &SqlitePool, year: Option<i32>) -> Stat {
let year = match year {
Some(year) => year,
None => chrono::Local::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
let rowed_km = sqlx::query(&format!(
"
SELECT SUM((b.amount_seats - COALESCE(m.member_count, 0)) * l.distance_in_km) as total_guest_km
FROM logbook l
JOIN boat b ON l.boat_id = b.id
LEFT JOIN (
SELECT logbook_id, COUNT(*) as member_count
FROM rower
GROUP BY logbook_id
) m ON l.id = m.logbook_id
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND not b.external;
"
))
.fetch_one(db)
.await
.unwrap()
.into_iter()
.map(|row| Stat {
name: row.get("name"),
rowed_km: row.get("rowed_km"),
})
.collect()
.get::<i64, usize>(0) as i32;
let rowed_km_guests = sqlx::query(&format!(
"
SELECT CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
FROM user u
INNER JOIN rower r ON u.id = r.rower_id
INNER JOIN logbook l ON r.logbook_id = l.id
WHERE u.id NOT IN (
SELECT ur.user_id
FROM user_role ur
INNER JOIN role ro ON ur.role_id = ro.id
WHERE ro.name = 'Donau Linz'
)
AND l.distance_in_km IS NOT NULL
AND l.arrival LIKE '{year}-%'
AND u.name != 'Externe Steuerperson';
"
))
.fetch_one(db)
.await
.unwrap()
.get::<i64, usize>(0) as i32;
Stat {
name: "Gäste".into(),
rowed_km: rowed_km + rowed_km_guests,
}
}
pub async fn sum_people(db: &SqlitePool, year: Option<i32>) -> i32 {
let stats = Self::people(db, year).await;
let mut sum = 0;
for stat in stats {
sum += stat.rowed_km;
}
sum
}
pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
let year = match year {
Some(year) => year,
None => chrono::Utc::now().year(),
None => chrono::Local::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
sqlx::query(&format!(
"
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
FROM user u
FROM (
SELECT * FROM user
WHERE id IN (
SELECT user_id FROM user_role
JOIN role ON user_role.role_id = role.id
WHERE role.name = 'Donau Linz'
)
) u
INNER JOIN rower r ON u.id = r.rower_id
INNER JOIN logbook l ON r.logbook_id = l.id
WHERE u.is_guest = 0 AND l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%'
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND u.name != 'Externe Steuerperson'
GROUP BY u.name
ORDER BY rowed_km DESC;
ORDER BY rowed_km DESC, u.name;
"
))
.fetch_all(db)
@ -63,6 +195,34 @@ ORDER BY rowed_km DESC;
})
.collect()
}
pub async fn person(db: &SqlitePool, year: Option<i32>, user: &User) -> Stat {
let year = match year {
Some(year) => year,
None => chrono::Local::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
let row = sqlx::query(&format!(
"
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
FROM (
SELECT * FROM user
WHERE id={}
) u
INNER JOIN rower r ON u.id = r.rower_id
INNER JOIN logbook l ON r.logbook_id = l.id
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%';
",
user.id
))
.fetch_one(db)
.await
.unwrap();
Stat {
name: row.get("name"),
rowed_km: row.get("rowed_km"),
}
}
}
#[derive(Debug, Serialize)]
@ -86,7 +246,7 @@ FROM (
LEFT JOIN
rower r ON l.id = r.logbook_id
WHERE
l.shipmaster = {0} OR r.rower_id = {0}
r.rower_id = {}
GROUP BY
departure_date
) as subquery

31
src/model/trailer.rs Normal file
View File

@ -0,0 +1,31 @@
use std::ops::DerefMut;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Trailer {
pub id: i64,
pub name: String,
}
impl Trailer {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM trailer WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM trailer WHERE id like ?", id)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM trailer")
.fetch_all(db)
.await
.unwrap()
}
}

View File

@ -0,0 +1,233 @@
use std::collections::HashMap;
use chrono::NaiveDate;
use chrono::NaiveDateTime;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use super::log::Log;
use super::notification::Notification;
use super::role::Role;
use super::trailer::Trailer;
use super::user::User;
use crate::tera::trailerreservation::ReservationEditForm;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TrailerReservation {
pub id: i64,
pub trailer_id: i64,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: String,
pub usage: String,
pub user_id_applicant: i64,
pub user_id_confirmation: Option<i64>,
pub created_at: NaiveDateTime,
}
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TrailerReservationWithDetails {
#[serde(flatten)]
reservation: TrailerReservation,
trailer: Trailer,
user_applicant: User,
user_confirmation: Option<User>,
}
#[derive(Debug)]
pub struct TrailerReservationToAdd<'r> {
pub trailer: &'r Trailer,
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub time_desc: &'r str,
pub usage: &'r str,
pub user_applicant: &'r User,
}
impl TrailerReservation {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, trailer_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM trailer_reservation
WHERE id like ?",
id
)
.fetch_one(db)
.await
.ok()
}
pub async fn all_future(db: &SqlitePool) -> Vec<TrailerReservationWithDetails> {
let trailerreservations = sqlx::query_as!(
Self,
"
SELECT id, trailer_id, start_date, end_date, time_desc, usage, user_id_applicant, user_id_confirmation, created_at
FROM trailer_reservation
WHERE end_date >= CURRENT_DATE ORDER BY end_date
"
)
.fetch_all(db)
.await
.unwrap(); //TODO: fixme
let mut res = Vec::new();
for reservation in trailerreservations {
let user_confirmation = match reservation.user_id_confirmation {
Some(id) => {
let user = User::find_by_id(db, id as i32).await;
Some(user.unwrap())
}
None => None,
};
let user_applicant = User::find_by_id(db, reservation.user_id_applicant as i32)
.await
.unwrap();
let trailer = Trailer::find_by_id(db, reservation.trailer_id as i32)
.await
.unwrap();
res.push(TrailerReservationWithDetails {
reservation,
trailer,
user_applicant,
user_confirmation,
});
}
res
}
pub async fn all_future_with_groups(
db: &SqlitePool,
) -> HashMap<String, Vec<TrailerReservationWithDetails>> {
let mut grouped_reservations: HashMap<String, Vec<TrailerReservationWithDetails>> =
HashMap::new();
let reservations = Self::all_future(db).await;
for reservation in reservations {
let key = format!(
"{}-{}-{}-{}-{}",
reservation.reservation.start_date,
reservation.reservation.end_date,
reservation.reservation.time_desc,
reservation.reservation.usage,
reservation.user_applicant.name
);
grouped_reservations
.entry(key)
.or_default()
.push(reservation);
}
grouped_reservations
}
pub async fn create(
db: &SqlitePool,
trailerreservation: TrailerReservationToAdd<'_>,
) -> Result<(), String> {
if Self::trailer_reserved_between_dates(
db,
trailerreservation.trailer,
&trailerreservation.start_date,
&trailerreservation.end_date,
)
.await
{
return Err("Hänger in diesem Zeitraum bereits reserviert.".into());
}
Log::create(
db,
format!("New trailer reservation: {trailerreservation:?}"),
)
.await;
sqlx::query!(
"INSERT INTO trailer_reservation(trailer_id, start_date, end_date, time_desc, usage, user_id_applicant) VALUES (?,?,?,?,?,?)",
trailerreservation.trailer.id,
trailerreservation.start_date,
trailerreservation.end_date,
trailerreservation.time_desc,
trailerreservation.usage,
trailerreservation.user_applicant.id,
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
let board =
User::all_with_role(db, &Role::find_by_name(db, "Vorstand").await.unwrap()).await;
for user in board {
let date = if trailerreservation.start_date == trailerreservation.end_date {
format!("am {}", trailerreservation.start_date)
} else {
format!(
"von {} bis {}",
trailerreservation.start_date, trailerreservation.end_date
)
};
Notification::create(
db,
&user,
&format!(
"{} hat eine neue Hängerreservierung für Hänger '{}' {} angelegt. Zeit: {}; Zweck: {}",
trailerreservation.user_applicant.name,
trailerreservation.trailer.name,
date,
trailerreservation.time_desc,
trailerreservation.usage
),
"Neue Hängerreservierung",
None,None
)
.await;
}
Ok(())
}
pub async fn trailer_reserved_between_dates(
db: &SqlitePool,
trailer: &Trailer,
start_date: &NaiveDate,
end_date: &NaiveDate,
) -> bool {
sqlx::query!(
"SELECT COUNT(*) AS reservation_count
FROM trailer_reservation
WHERE trailer_id = ?
AND start_date <= ? AND end_date >= ?;",
trailer.id,
end_date,
start_date
)
.fetch_one(db)
.await
.unwrap()
.reservation_count
> 0
}
pub async fn update(&self, db: &SqlitePool, data: ReservationEditForm) {
let time_desc = data.time_desc.trim();
let usage = data.usage.trim();
sqlx::query!(
"UPDATE trailer_reservation SET time_desc = ?, usage = ? where id = ?",
time_desc,
usage,
self.id
)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a User of a valid id
}
pub async fn delete(&self, db: &SqlitePool) {
sqlx::query!("DELETE FROM trailer_reservation WHERE id=?", self.id)
.execute(db)
.await
.unwrap(); //Okay, because we can only create a Boat of a valid id
}
}

View File

@ -3,21 +3,22 @@ use serde::Serialize;
use sqlx::SqlitePool;
use super::{
planned_event::{PlannedEvent, Registration},
event::{Event, Registration},
notification::Notification,
tripdetails::TripDetails,
triptype::TripType,
user::CoxUser,
user::{CoxUser, User},
};
#[derive(Serialize, Clone, Debug)]
pub struct Trip {
id: i64,
cox_id: i64,
pub cox_id: i64,
cox_name: String,
trip_details_id: Option<i64>,
planned_starting_time: String,
pub max_people: i64,
day: String,
pub day: String,
pub notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>,
@ -25,14 +26,38 @@ pub struct Trip {
is_locked: bool,
}
#[derive(Serialize)]
#[derive(Serialize, Debug)]
pub struct TripWithUserAndType {
#[serde(flatten)]
pub trip: Trip,
rower: Vec<Registration>,
pub rower: Vec<Registration>,
trip_type: Option<TripType>,
}
pub struct TripUpdate<'a> {
pub cox: &'a CoxUser,
pub trip: &'a Trip,
pub max_people: i32,
pub notes: Option<&'a str>,
pub trip_type: Option<i64>, //TODO: Move to `TripType`
pub always_show: bool,
pub is_locked: bool,
}
impl TripWithUserAndType {
pub async fn from(db: &SqlitePool, trip: Trip) -> Self {
let mut trip_type = None;
if let Some(trip_type_id) = trip.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await;
}
Self {
rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await,
trip,
trip_type,
}
}
}
impl Trip {
/// Cox decides to create own trip.
pub async fn new_own(db: &SqlitePool, cox: &CoxUser, trip_details: TripDetails) {
@ -43,13 +68,59 @@ impl Trip {
)
.execute(db)
.await;
let same_starting_datetime = TripDetails::find_by_startingdatetime(
db,
trip_details.day,
trip_details.planned_starting_time,
)
.await;
if same_starting_datetime.len() > 1 {
for notify in same_starting_datetime {
if notify.id != trip_details.id {
// notify everyone except oneself
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
let user = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&user,
&format!(
"{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
cox.user.name, trip.day, trip.planned_starting_time
),
"Neue Ausfahrt zur selben Zeit",
None,
None,
)
.await;
}
}
}
}
}
pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, trip_details.notes, allow_guests, trip_type_id, always_show, is_locked
FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id
WHERE trip_details.id=?
",
tripdetails_id
)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show, is_locked
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, trip_details.notes, allow_guests, trip_type_id, always_show, is_locked
FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id
@ -62,28 +133,28 @@ WHERE trip.id=?
.ok()
}
/// Cox decides to help in a planned event.
/// Cox decides to help in a event.
pub async fn new_join(
db: &SqlitePool,
cox: &CoxUser,
planned_event: &PlannedEvent,
event: &Event,
) -> Result<(), CoxHelpError> {
if planned_event.is_rower_registered(db, cox).await {
if event.is_rower_registered(db, cox).await {
return Err(CoxHelpError::AlreadyRegisteredAsRower);
}
if planned_event.trip_details(db).await.is_locked {
if event.trip_details(db).await.is_locked {
return Err(CoxHelpError::DetailsLocked);
}
if planned_event.is_rower_registered(db, cox).await {
return Err(CoxHelpError::AlreadyRegisteredAsRower);
if event.max_people == 0 {
return Err(CoxHelpError::CanceledEvent);
}
match sqlx::query!(
"INSERT INTO trip (cox_id, planned_event_id) VALUES(?, ?)",
cox.id,
planned_event.id
event.id
)
.execute(db)
.await
@ -98,7 +169,7 @@ WHERE trip.id=?
let trips = sqlx::query_as!(
Trip,
"
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show, is_locked
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, trip_details.notes, allow_guests, trip_type_id, always_show, is_locked
FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id
@ -112,15 +183,7 @@ WHERE day=?
let mut ret = Vec::new();
for trip in trips {
let mut trip_type = None;
if let Some(trip_type_id) = trip.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await;
}
ret.push(TripWithUserAndType {
rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await,
trip,
trip_type,
});
ret.push(TripWithUserAndType::from(db, trip).await);
}
ret
}
@ -128,42 +191,82 @@ WHERE day=?
/// Cox decides to update own trip.
pub async fn update_own(
db: &SqlitePool,
cox: &CoxUser,
trip: &Trip,
max_people: i32,
notes: Option<&str>,
trip_type: Option<i64>, //TODO: Move to `TripType`
always_show: bool,
is_locked: bool,
update: &TripUpdate<'_>,
) -> Result<(), TripUpdateError> {
if !trip.is_trip_from_user(cox.id) {
if !update.trip.is_trip_from_user(update.cox.id) {
return Err(TripUpdateError::NotYourTrip);
}
let trip_details = sqlx::query!(
"SELECT trip_details_id as id FROM trip WHERE id = ?",
trip.id
)
.fetch_one(db)
.await
.unwrap(); //Okay, as trip can only be created with proper DB backing
let Some(trip_details_id) = trip_details.id else {
let Some(trip_details_id) = update.trip.trip_details_id else {
return Err(TripUpdateError::TripDetailsDoesNotExist); //TODO: Remove?
};
let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
let was_already_cancelled = tripdetails.max_people == 0;
sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, always_show = ?, is_locked = ? WHERE id = ?",
max_people,
notes,
trip_type,
always_show,
is_locked,
update.max_people,
update.notes,
update.trip_type,
update.always_show,
update.is_locked,
trip_details_id
)
.execute(db)
.await
.unwrap(); //Okay, as trip_details can only be created with proper DB backing
if update.max_people == 0 && !was_already_cancelled {
let rowers = TripWithUserAndType::from(db, update.trip.clone())
.await
.rower;
for user in rowers {
if let Some(user) = User::find_by_name(db, &user.name).await {
let notes = match update.notes {
Some(n) if !n.is_empty() => format!("Grund der Absage: {n}"),
_ => String::from(""),
};
Notification::create(
db,
&user,
&format!(
"Die Ausfahrt von {} am {} um {} wurde abgesagt. {}",
update.cox.user.name,
update.trip.day,
update.trip.planned_starting_time,
notes
),
"Absage Ausfahrt",
None,
Some(&format!(
"remove_user_trip_with_trip_details_id:{}",
trip_details_id
)),
)
.await;
}
}
} else {
Notification::delete_by_action(
db,
&format!("remove_user_trip_with_trip_details_id:{}", trip_details_id),
)
.await;
}
if update.max_people > 0 && was_already_cancelled {
Notification::delete_by_action(
db,
&format!("remove_user_trip_with_trip_details_id:{}", trip_details_id),
)
.await;
}
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
trip_details.check_free_spaces(db).await;
Ok(())
}
@ -177,16 +280,16 @@ WHERE day=?
pub async fn delete_by_planned_event(
db: &SqlitePool,
cox: &CoxUser,
planned_event: &PlannedEvent,
event: &Event,
) -> Result<(), TripHelpDeleteError> {
if planned_event.trip_details(db).await.is_locked {
if event.trip_details(db).await.is_locked {
return Err(TripHelpDeleteError::DetailsLocked);
}
let affected_rows = sqlx::query!(
"DELETE FROM trip WHERE cox_id = ? AND planned_event_id = ?",
cox.id,
planned_event.id
event.id
)
.execute(db)
.await
@ -245,6 +348,7 @@ pub enum CoxHelpError {
AlreadyRegisteredAsRower,
AlreadyRegisteredAsCox,
DetailsLocked,
CanceledEvent,
}
#[derive(Debug, PartialEq)]
@ -269,8 +373,8 @@ pub enum TripUpdateError {
mod test {
use crate::{
model::{
planned_event::PlannedEvent,
trip::TripDeleteError,
event::Event,
trip::{self, TripDeleteError},
tripdetails::TripDetails,
user::{CoxUser, User},
usertrip::UserTrip,
@ -287,11 +391,12 @@ mod test {
fn test_new_own() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
@ -312,13 +417,14 @@ mod test {
fn test_new_succ_join() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox2".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox2".into()).await.unwrap(),
)
.await
.unwrap();
let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap();
let planned_event = Event::find_by_id(&pool, 1).await.unwrap();
assert!(Trip::new_join(&pool, &cox, &planned_event).await.is_ok());
}
@ -327,13 +433,14 @@ mod test {
fn test_new_failed_join_already_cox() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox2".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox2".into()).await.unwrap(),
)
.await
.unwrap();
let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap();
let planned_event = Event::find_by_id(&pool, 1).await.unwrap();
Trip::new_join(&pool, &cox, &planned_event).await.unwrap();
assert!(Trip::new_join(&pool, &cox, &planned_event).await.is_err());
@ -343,19 +450,26 @@ mod test {
fn test_succ_update_own() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!(
Trip::update_own(&pool, &cox, &trip, 10, None, None, false, false)
.await
.is_ok()
);
let update = trip::TripUpdate {
cox: &cox,
trip: &trip,
max_people: 10,
notes: None,
trip_type: None,
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert_eq!(trip.max_people, 10);
@ -365,19 +479,25 @@ mod test {
fn test_succ_update_own_with_triptype() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!(
Trip::update_own(&pool, &cox, &trip, 10, None, Some(1), false, false)
.await
.is_ok()
);
let update = trip::TripUpdate {
cox: &cox,
trip: &trip,
max_people: 10,
notes: None,
trip_type: Some(1),
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert_eq!(trip.max_people, 10);
@ -388,19 +508,25 @@ mod test {
fn test_fail_update_own_not_your_trip() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox2".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox2".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!(
Trip::update_own(&pool, &cox, &trip, 10, None, None, false, false)
.await
.is_err()
);
let update = trip::TripUpdate {
cox: &cox,
trip: &trip,
max_people: 10,
notes: None,
trip_type: None,
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_err());
assert_eq!(trip.max_people, 1);
}
@ -408,13 +534,14 @@ mod test {
fn test_succ_delete_by_planned_event() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap();
let planned_event = Event::find_by_id(&pool, 1).await.unwrap();
Trip::new_join(&pool, &cox, &planned_event).await.unwrap();
@ -430,11 +557,12 @@ mod test {
fn test_succ_delete() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
@ -447,11 +575,12 @@ mod test {
fn test_fail_delete_diff_cox() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox2".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox2".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();
@ -468,11 +597,12 @@ mod test {
fn test_fail_delete_someone_registered() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let trip = Trip::find_by_id(&pool, 1).await.unwrap();

View File

@ -4,6 +4,12 @@ use rocket::FromForm;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use super::{
notification::Notification,
trip::{Trip, TripWithUserAndType},
triptype::TripType,
};
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TripDetails {
pub id: i64,
@ -46,6 +52,93 @@ WHERE id like ?
.ok()
}
pub async fn triptype(&self, db: &SqlitePool) -> Option<TripType> {
match self.trip_type_id {
None => None,
Some(id) => TripType::find_by_id(db, id).await,
}
}
pub async fn find_by_startingdatetime(
db: &SqlitePool,
day: String,
planned_starting_time: String,
) -> Vec<Self> {
sqlx::query_as!(
Self,
"
SELECT id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id, always_show, is_locked
FROM trip_details
WHERE day = ? AND planned_starting_time = ?
"
, day, planned_starting_time
)
.fetch_all(db)
.await.unwrap()
}
/// This function is called when a person registers to a trip or when the cox changes the
/// amount of free places.
pub async fn check_free_spaces(&self, db: &SqlitePool) {
if !self.is_full(db).await {
// We still have space for new people, no need to do anything
return;
}
if self.max_people == 0 {
// Cox cancelled event, thus it's probably bad weather. Don't bother with sending
// notifications
return;
}
if Trip::find_by_trip_details(db, self.id).await.is_none() {
// This trip_details belongs to a planned_event, no need to do anything
return;
};
let other_trips_same_time = Self::find_by_startingdatetime(
db,
self.day.clone(),
self.planned_starting_time.clone(),
)
.await;
for trip in &other_trips_same_time {
if !trip.is_full(db).await {
// There are trips on the same time, with open places
return;
}
}
// We just got fully booked and there are no other trips with remaining rower places. Send
// notification to all coxes which are registered as non-cox.
for trip_details in other_trips_same_time {
let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await else {
// This trip_details belongs to a planned_event, no need to do anything
continue;
};
let pot_coxes = TripWithUserAndType::from(db, trip.clone()).await;
let pot_coxes = pot_coxes.rower;
for user in pot_coxes {
let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
let Some(user) = User::find_by_name(db, &user.name).await else {
// User is a guest, no need to bother.
continue;
};
if !user.has_role(db, "cox").await {
// User is no cox, no need to bother
continue;
}
if user.id == cox.id {
// User already offers a trip, no need to bother
continue;
}
Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None, None).await;
}
}
}
/// Creates a new entry in `trip_details` and returns its id.
pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 {
let query = sqlx::query!(
@ -120,7 +213,7 @@ ORDER BY day;",
pub(crate) async fn user_allowed_to_change(&self, db: &SqlitePool, user: &User) -> bool {
if self.belongs_to_event(db).await {
user.is_admin
user.has_role(db, "planned_event").await
} else {
self.user_is_cox(db, user).await != CoxAtTrip::No
}

View File

@ -4,7 +4,7 @@ use sqlx::{FromRow, SqlitePool};
#[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
pub struct TripType {
pub id: i64,
name: String,
pub name: String,
desc: String,
question: String,
icon: String,

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
use sqlx::SqlitePool;
use super::{tripdetails::TripDetails, user::User};
use super::{notification::Notification, trip::Trip, tripdetails::TripDetails, user::User};
use crate::model::tripdetails::{Action, CoxAtTrip::Yes};
pub struct UserTrip {}
@ -11,7 +11,7 @@ impl UserTrip {
user: &User,
trip_details: &TripDetails,
user_note: Option<String>,
) -> Result<(), UserTripError> {
) -> Result<String, UserTripError> {
if trip_details.is_full(db).await {
return Err(UserTripError::EventAlreadyFull);
}
@ -20,14 +20,14 @@ impl UserTrip {
return Err(UserTripError::DetailsLocked);
}
if user.is_guest && !trip_details.allow_guests {
if user.has_role(db, "scheckbuch").await && !trip_details.allow_guests {
return Err(UserTripError::GuestNotAllowedForThisEvent);
}
//TODO: Check if user sees the event (otherwise she could forge trip_details_id)
let is_cox = trip_details.user_is_cox(db, user).await;
if user_note.is_none() {
let name_newly_registered_person = if user_note.is_none() {
if let Yes(action) = is_cox {
match action {
Action::Helping => return Err(UserTripError::AlreadyRegisteredAsCox),
@ -47,6 +47,8 @@ impl UserTrip {
.execute(db)
.await
.unwrap();
user.name.clone()
} else {
if !trip_details.user_allowed_to_change(db, user).await {
return Err(UserTripError::NotAllowedToAddGuest);
@ -59,9 +61,29 @@ impl UserTrip {
.execute(db)
.await
.unwrap();
user_note.unwrap()
};
if let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await {
let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&cox,
&format!(
"{} hat sich für deine Ausfahrt am {} registriert",
name_newly_registered_person, trip.day
),
"Registrierung bei deiner Ausfahrt",
None,
None,
)
.await;
trip_details.check_free_spaces(db).await;
}
Ok(())
Ok(name_newly_registered_person)
}
pub async fn delete(
@ -128,7 +150,7 @@ pub enum UserTripDeleteError {
mod test {
use crate::{
model::{
planned_event::PlannedEvent, trip::Trip, tripdetails::TripDetails, user::CoxUser,
event::Event, trip::Trip, tripdetails::TripDetails, user::CoxUser,
usertrip::UserTripError,
},
testdb,
@ -211,14 +233,15 @@ mod test {
fn test_fail_create_is_cox_planned_event() {
let pool = testdb!();
let cox: CoxUser = User::find_by_name(&pool, "cox".into())
.await
.unwrap()
.try_into()
.unwrap();
let cox = CoxUser::new(
&pool,
User::find_by_name(&pool, "cox".into()).await.unwrap(),
)
.await
.unwrap();
let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap();
Trip::new_join(&pool, &cox, &planned_event).await.unwrap();
let event = Event::find_by_id(&pool, 1).await.unwrap();
Trip::new_join(&pool, &cox, &event).await.unwrap();
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
let result = UserTrip::create(&pool, &cox, &trip_details, None)

93
src/model/waterlevel.rs Normal file
View File

@ -0,0 +1,93 @@
use std::ops::DerefMut;
use chrono::NaiveDate;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Waterlevel {
pub id: i64,
pub day: NaiveDate,
pub time: String,
pub max: i64,
pub min: i64,
pub mittel: i64,
pub tumax: i64,
pub tumin: i64,
pub tumittel: i64,
}
#[derive(Debug, Serialize)]
pub struct WaterlevelDay {
pub day: NaiveDate,
pub avg: i64,
pub fluctuation: i64,
}
pub struct Create {
pub day: NaiveDate,
pub time: String,
pub max: i64,
pub min: i64,
pub mittel: i64,
pub tumax: i64,
pub tumin: i64,
pub tumittel: i64,
}
impl Waterlevel {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn create(db: &mut Transaction<'_, Sqlite>, create: &Create) -> Result<(), String> {
sqlx::query!(
"INSERT INTO waterlevel(day, time, max, min, mittel, tumax, tumin, tumittel) VALUES (?,?,?,?,?,?,?,?)",
create.day, create.time, create.max, create.min, create.mittel, create.tumax, create.tumin, create.tumittel
)
.execute(db.deref_mut())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn max_waterlevel_for_day(db: &SqlitePool, day: NaiveDate) -> Option<WaterlevelDay> {
let waterlevel = sqlx::query_as!(
Waterlevel,
"SELECT id, day, time, max, min, mittel, tumax, tumin, tumittel FROM waterlevel WHERE day = ? ORDER BY mittel DESC LIMIT 1",
day
)
.fetch_optional(db)
.await.unwrap();
if let Some(waterlevel) = waterlevel {
let max_diff = (waterlevel.mittel - waterlevel.max).abs();
let min_diff = (waterlevel.mittel - waterlevel.min).abs();
let fluctuation = max_diff.max(min_diff);
return Some(WaterlevelDay {
day: waterlevel.day,
avg: waterlevel.mittel,
fluctuation,
});
}
None
}
pub async fn delete_all(db: &mut Transaction<'_, Sqlite>) {
sqlx::query!("DELETE FROM waterlevel;")
.execute(db.deref_mut())
.await
.unwrap();
}
}

56
src/model/weather.rs Normal file
View File

@ -0,0 +1,56 @@
use std::ops::DerefMut;
use chrono::NaiveDate;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Weather {
pub id: i64,
pub day: NaiveDate,
pub max_temp: f64,
pub wind_gust: f64,
pub rain_mm: f64,
}
impl Weather {
pub async fn find_by_day(db: &SqlitePool, day: NaiveDate) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM weather WHERE day = ?", day)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, day: NaiveDate) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM weather WHERE day = ?", day)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn create(
db: &mut Transaction<'_, Sqlite>,
day: NaiveDate,
max_temp: f64,
wind_gust: f64,
rain_mm: f64,
) -> Result<(), String> {
sqlx::query!(
"INSERT INTO weather(day, max_temp, wind_gust, rain_mm) VALUES (?,?,?,?)",
day,
max_temp,
wind_gust,
rain_mm
)
.execute(db.deref_mut())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn delete_all(db: &mut Transaction<'_, Sqlite>) {
sqlx::query!("DELETE FROM weather;")
.execute(db.deref_mut())
.await
.unwrap();
}
}

View File

@ -1,4 +1,4 @@
use rocket::{form::Form, fs::FileServer, post, routes, Build, FromForm, Rocket, State};
use rocket::{form::Form, post, routes, Build, FromForm, Rocket, State};
use serde_json::json;
use sqlx::SqlitePool;
@ -27,7 +27,7 @@ async fn login(login: Form<LoginForm<'_>>, db: &State<SqlitePool>) -> String {
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
rocket
.mount("/", FileServer::from("svelte/build").rank(0))
//.mount("/", FileServer::from("svelte/build").rank(0))
.mount("/api/login", routes![login])
}

43
src/scheduled/mod.rs Normal file
View File

@ -0,0 +1,43 @@
mod waterlevel;
mod weather;
use std::time::Duration;
use job_scheduler_ng::{Job, JobScheduler};
use rocket::tokio::{self, task, time};
use sqlx::SqlitePool;
use crate::tera::Config;
pub fn schedule(db: &SqlitePool, config: &Config) {
let db = db.clone();
let openweathermap_key = config.openweathermap_key.clone();
tokio::task::spawn(async {
waterlevel::update(&db).await.unwrap();
weather::update(&db, &openweathermap_key).await.unwrap();
let mut sched = JobScheduler::new();
// Every hour
sched.add(Job::new("0 0 * * * * *".parse().unwrap(), move || {
let db_clone = db.clone();
// Use block_in_place to run async code in the synchronous function; TODO: Make it
// nicer one's rust (stable) support async closures
task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
waterlevel::update(&db_clone).await.unwrap();
weather::update(&db_clone, &openweathermap_key)
.await
.unwrap();
});
});
}));
let mut interval = time::interval(Duration::from_secs(60));
loop {
sched.tick();
interval.tick().await;
}
});
}

118
src/scheduled/waterlevel.rs Normal file
View File

@ -0,0 +1,118 @@
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime};
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use crate::model::waterlevel::{self, Waterlevel};
pub async fn update(db: &SqlitePool) -> Result<(), String> {
let mut tx = db.begin().await.unwrap();
// 1. Delete water levels starting from yesterday
Waterlevel::delete_all(&mut tx).await;
// 2. Fetch
let station = fetch()?;
for d in station.data {
let (Some(max), Some(min), Some(mittel), Some(tumax), Some(tumin), Some(tumittel)) =
(d.max, d.min, d.mittel, d.tumax, d.tumin, d.tumittel)
else {
println!("Ignored invalid values: {d:?}");
continue;
};
let Ok(datetime): Result<DateTime<FixedOffset>, _> = d.timestamp.parse() else {
return Err("Failed to parse datetime from hydro json".into());
};
let date: NaiveDate = datetime.naive_utc().date();
// Extract time component and format as string
let time: NaiveTime = datetime.naive_utc().time();
let time_str = time.format("%H:%M").to_string();
let create = waterlevel::Create {
day: date,
time: time_str,
max,
min,
mittel,
tumax,
tumin,
tumittel,
};
Waterlevel::create(&mut tx, &create).await?
}
// 3. Save in DB
tx.commit().await.unwrap();
Ok(())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Station {
station_no: String,
station_latitude: String,
station_longitude: String,
parametertype_name: String,
ts_shortname: String,
ts_name: String,
ts_unitname: String,
ts_unitsymbol: String,
ts_precision: String,
rows: String,
columns: String,
data: Vec<Data>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Data {
timestamp: String,
max: Option<i64>,
min: Option<i64>,
mittel: Option<i64>,
tumax: Option<i64>,
tumin: Option<i64>,
tumittel: Option<i64>,
}
fn fetch() -> Result<Station, String> {
let url = "https://hydro.ooe.gv.at/daten/internet/stations/OG/207068/S/forecast.json";
match ureq::get(url).call() {
Ok(response) => {
let forecast: Result<Vec<Station>, _> = response.into_json();
if let Ok(data) = forecast {
if data.len() == 1 {
Ok(data[0].clone())
} else {
Err(format!(
"Expected 1 station (Linz); got {} while fetching from {url}. Maybe the hydro data format changed?",
data.len()
))
}
} else {
Err(format!(
"Failed to parse the json received by {url}: {}",
forecast.err().unwrap()
))
}
}
Err(_) => Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?"
)),
}
}
//#[cfg(test)]
//mod test {
// use crate::testdb;
//
// use super::*;
// #[sqlx::test]
// fn test_fetch_succ() {
// let pool = testdb!();
// fetch();
// }
//}

118
src/scheduled/weather.rs Normal file
View File

@ -0,0 +1,118 @@
use chrono::DateTime;
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use crate::model::weather::Weather;
pub async fn update(db: &SqlitePool, api_key: &str) -> Result<(), String> {
let mut tx = db.begin().await.unwrap();
// 1. Delete weather data
Weather::delete_all(&mut tx).await;
// 2. Fetch
let data = fetch(api_key)?;
for d in data.daily {
let Some(date) = DateTime::from_timestamp(d.dt, 0) else {
println!("Skipping {} because convertion to datetime failed", d.dt);
continue;
};
let max_temp = d.temp.max;
let wind_gust = d.wind_gust;
let rain_mm = d.rain.unwrap_or(0.);
Weather::create(
&mut tx,
date.naive_utc().into(),
max_temp,
wind_gust,
rain_mm,
)
.await?
}
// 3. Save in DB
tx.commit().await.unwrap();
Ok(())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Data {
lat: f64,
lon: f64,
timezone: String,
timezone_offset: i64,
daily: Vec<Daily>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Daily {
dt: i64,
sunrise: i64,
sunset: i64,
moonrise: i64,
moonset: i64,
moon_phase: f64,
summary: String,
temp: Temp,
feels_like: FeelsLike,
pressure: i64,
humidity: i64,
dew_point: f64,
wind_speed: f64,
wind_deg: i64,
wind_gust: f64,
weather: Vec<DailyWeather>,
clouds: i64,
pop: f64,
rain: Option<f64>,
uvi: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Temp {
day: f64,
min: f64,
max: f64,
night: f64,
eve: f64,
morn: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct FeelsLike {
day: f64,
night: f64,
eve: f64,
morn: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct DailyWeather {
id: i64,
main: String,
description: String,
icon: String,
}
fn fetch(api_key: &str) -> Result<Data, String> {
let url = format!("https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}");
match ureq::get(&url).call() {
Ok(response) => {
let data: Result<Data, _> = response.into_json();
if let Ok(data) = data {
Ok(data)
} else {
Err(format!(
"Failed to parse the json received by {url}: {}",
data.err().unwrap()
))
}
}
Err(_) => Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?"
)),
}
}

View File

@ -1,7 +1,8 @@
use crate::model::{
boat::{Boat, BoatToAdd, BoatToUpdate},
location::Location,
user::{AdminUser, User},
log::Log,
user::{AdminUser, User, UserWithDetails},
};
use rocket::{
form::Form,
@ -30,14 +31,19 @@ async fn index(
context.insert("boats", &boats);
context.insert("locations", &locations);
context.insert("users", &users);
context.insert("loggedin_user", &admin.user);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.user, db).await,
);
Template::render("admin/boat/index", context.into_json())
}
#[get("/boat/<boat>/delete")]
async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boat: i32) -> Flash<Redirect> {
async fn delete(db: &State<SqlitePool>, admin: AdminUser, boat: i32) -> Flash<Redirect> {
let boat = Boat::find_by_id(db, boat).await;
Log::create(db, format!("{} deleted boat: {boat:?}", admin.user.name)).await;
match boat {
Some(boat) => {
boat.delete(db).await;

View File

@ -8,14 +8,14 @@ use serde::Serialize;
use sqlx::SqlitePool;
use crate::model::{
planned_event::PlannedEvent,
event::{self, Event},
tripdetails::{TripDetails, TripDetailsToAdd},
user::AdminUser,
user::EventUser,
};
//TODO: add constraints (e.g. planned_amount_cox > 0)
#[derive(FromForm, Serialize)]
struct AddPlannedEventForm<'r> {
struct AddEventForm<'r> {
name: &'r str,
planned_amount_cox: i32,
tripdetails: TripDetailsToAdd<'r>,
@ -24,8 +24,8 @@ struct AddPlannedEventForm<'r> {
#[post("/planned-event", data = "<data>")]
async fn create(
db: &State<SqlitePool>,
data: Form<AddPlannedEventForm<'_>>,
_admin: AdminUser,
data: Form<AddEventForm<'_>>,
_admin: EventUser,
) -> Flash<Redirect> {
let data = data.into_inner();
@ -34,54 +34,57 @@ async fn create(
//just created
//the object
PlannedEvent::create(db, data.name, data.planned_amount_cox, trip_details).await;
Event::create(db, data.name, data.planned_amount_cox, &trip_details).await;
Flash::success(Redirect::to("/"), "Event hinzugefügt")
Flash::success(Redirect::to("/planned"), "Event hinzugefügt")
}
//TODO: add constraints (e.g. planned_amount_cox > 0)
#[derive(FromForm)]
struct UpdatePlannedEventForm<'r> {
#[derive(FromForm, Debug)]
struct UpdateEventForm<'r> {
id: i64,
name: &'r str,
planned_amount_cox: i32,
max_people: i32,
notes: Option<&'r str>,
always_show: bool,
is_locked: bool,
trip_type: Option<i64>,
}
#[put("/planned-event", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<UpdatePlannedEventForm<'_>>,
_admin: AdminUser,
data: Form<UpdateEventForm<'_>>,
_admin: EventUser,
) -> Flash<Redirect> {
match PlannedEvent::find_by_id(db, data.id).await {
let update = event::EventUpdate {
name: data.name,
planned_amount_cox: data.planned_amount_cox,
max_people: data.max_people,
notes: data.notes,
always_show: data.always_show,
is_locked: data.is_locked,
trip_type_id: data.trip_type,
};
match Event::find_by_id(db, data.id).await {
Some(planned_event) => {
planned_event
.update(
db,
data.planned_amount_cox,
data.max_people,
data.notes,
data.always_show,
data.is_locked,
)
.await;
Flash::success(Redirect::to("/"), "Successfully edited the event")
planned_event.update(db, &update).await;
Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet")
}
None => Flash::error(Redirect::to("/"), "Planned event id not found"),
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),
}
}
#[get("/planned-event/<id>/delete")]
async fn delete(db: &State<SqlitePool>, id: i64, _admin: AdminUser) -> Flash<Redirect> {
match PlannedEvent::find_by_id(db, id).await {
Some(planned_event) => {
planned_event.delete(db).await;
Flash::success(Redirect::to("/"), "Event gelöscht")
}
None => Flash::error(Redirect::to("/"), "PlannedEvent does not exist"),
async fn delete(db: &State<SqlitePool>, id: i64, _admin: EventUser) -> Flash<Redirect> {
let Some(event) = Event::find_by_id(db, id).await else {
return Flash::error(Redirect::to("/planned"), "Event does not exist");
};
match event.delete(db).await {
Ok(()) => Flash::success(Redirect::to("/planned"), "Event gelöscht"),
Err(e) => Flash::error(Redirect::to("/planned"), e),
}
}
@ -104,7 +107,7 @@ mod test {
fn test_delete() {
let db = testdb!();
let _ = PlannedEvent::find_by_id(&db, 1).await.unwrap();
let _ = Event::find_by_id(&db, 1).await.unwrap();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
@ -120,7 +123,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -129,7 +132,7 @@ mod test {
assert_eq!(flash_cookie.value(), "7:successEvent gelöscht");
let event = PlannedEvent::find_by_id(&db, 1).await;
let event = Event::find_by_id(&db, 1).await;
assert_eq!(event, None);
}
@ -151,23 +154,23 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "5:errorPlannedEvent does not exist");
assert_eq!(flash_cookie.value(), "5:errorEvent does not exist");
let _ = PlannedEvent::find_by_id(&db, 1).await.unwrap();
let _ = Event::find_by_id(&db, 1).await.unwrap();
}
#[sqlx::test]
fn test_update() {
let db = testdb!();
let event = PlannedEvent::find_by_id(&db, 1).await.unwrap();
let event = Event::find_by_id(&db, 1).await.unwrap();
assert_eq!(event.notes, Some("trip_details for a planned event".into()));
let rocket = rocket::build().manage(db.clone());
@ -183,11 +186,11 @@ mod test {
let req = client
.put("/admin/planned-event")
.header(ContentType::Form) // Set the content type to form
.body("id=1&planned_amount_cox=2&max_people=3&notes=new-planned-event-text"); // Add the form data to the request body;
.body("id=1&planned_amount_cox=2&max_people=3&notes=new-planned-event-text&name=test"); // Add the form data to the request body;
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -196,10 +199,10 @@ mod test {
assert_eq!(
flash_cookie.value(),
"7:successSuccessfully edited the event"
"7:successEvent erfolgreich bearbeitet"
);
let event = PlannedEvent::find_by_id(&db, 1).await.unwrap();
let event = Event::find_by_id(&db, 1).await.unwrap();
assert_eq!(event.notes, Some("new-planned-event-text".into()));
}
@ -220,11 +223,13 @@ mod test {
let req = client
.put("/admin/planned-event")
.header(ContentType::Form) // Set the content type to form
.body("id=1337&planned_amount_cox=2&max_people=3&notes=new-planned-event-text"); // Add the form data to the request body;
.body(
"id=1337&planned_amount_cox=2&max_people=3&notes=new-planned-event-text&name=test",
); // Add the form data to the request body;
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -255,7 +260,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -264,7 +269,7 @@ mod test {
assert_eq!(flash_cookie.value(), "7:successEvent hinzugefügt");
let event = PlannedEvent::find_by_id(&db, 2).await.unwrap();
let event = Event::find_by_id(&db, 2).await.unwrap();
assert_eq!(event.name, "my-cool-new-event");
}
}

86
src/tera/admin/mail.rs Normal file
View File

@ -0,0 +1,86 @@
use rocket::form::Form;
use rocket::fs::TempFile;
use rocket::response::{Flash, Redirect};
use rocket::{get, request::FlashMessage, routes, Route, State};
use rocket::{post, FromForm};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
use crate::model::log::Log;
use crate::model::mail::Mail;
use crate::model::role::Role;
use crate::model::user::AdminUser;
use crate::model::user::UserWithDetails;
use crate::tera::Config;
#[get("/mail")]
async fn index(
db: &State<SqlitePool>,
admin: AdminUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
let roles = Role::all(db).await;
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.user, db).await,
);
context.insert("roles", &roles);
Template::render("admin/mail", context.into_json())
}
#[get("/mail/fee")]
async fn fee(db: &State<SqlitePool>, admin: AdminUser, config: &State<Config>) -> &'static str {
Log::create(db, format!("{admin:?} trying to send fee")).await;
Mail::fees(db, config.smtp_pw.clone()).await;
"SUCC"
}
#[get("/mail/fee-final")]
async fn fee_final(
db: &State<SqlitePool>,
admin: AdminUser,
config: &State<Config>,
) -> &'static str {
Log::create(db, format!("{admin:?} trying to send fee_final")).await;
Mail::fees_final(db, config.smtp_pw.clone()).await;
"SUCC"
}
#[derive(FromForm, Debug)]
pub struct MailToSend<'a> {
pub(crate) role_id: i32,
pub(crate) subject: String,
pub(crate) body: String,
pub(crate) files: Vec<TempFile<'a>>,
}
#[post("/mail", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<MailToSend<'_>>,
config: &State<Config>,
admin: AdminUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(db, format!("{admin:?} trying to send this mail: {d:?}")).await;
if Mail::send(db, d, config.smtp_pw.clone()).await {
Log::create(db, "Mail successfully sent".into()).await;
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
} else {
Log::create(db, "Error sending the mail".into()).await;
Flash::error(Redirect::to("/admin/mail"), "Fehler")
}
}
pub fn routes() -> Vec<Route> {
routes![index, update, fee, fee_final]
}
#[cfg(test)]
mod test {}

View File

@ -1,13 +1,18 @@
use rocket::{get, routes, Route, State};
use csv::ReaderBuilder;
use rocket::{form::Form, get, post, routes, FromForm, Route, State};
use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool;
use crate::{
model::{log::Log, user::AdminUser},
model::{log::Log, role::Role, user::AdminUser},
tera::Config,
};
pub mod boat;
pub mod planned_event;
pub mod event;
pub mod mail;
pub mod notification;
pub mod schnupper;
pub mod user;
#[get("/rss?<key>")]
@ -24,11 +29,58 @@ async fn show_rss(db: &State<SqlitePool>, _admin: AdminUser) -> String {
Log::show(db).await
}
#[get("/list")]
async fn show_list(_admin: AdminUser) -> Template {
Template::render("admin/list/index", context!())
}
#[derive(FromForm)]
struct ListForm {
list: String,
}
#[post("/list", data = "<list_form>")]
async fn list(db: &State<SqlitePool>, _admin: AdminUser, list_form: Form<ListForm>) -> Template {
let role = Role::find_by_name(db, "Donau Linz").await.unwrap();
let acceptable_users = role.names_from_role(db).await;
let mut rdr = ReaderBuilder::new()
.has_headers(true)
.delimiter(b';')
.from_reader(list_form.list.trim().as_bytes());
let mut names_not_in_acceptable_users = Vec::new();
for result in rdr.records() {
println!("{result:?}");
let record = result.unwrap();
// Concatenate Vorname and Nachname
let vorname = record.get(2).unwrap_or_default().trim();
let nachname = record.get(3).unwrap_or_default().trim();
let full_name = format!("{} {}", vorname, nachname);
// Check if the concatenated name is not in the acceptable_users vector
if !acceptable_users.contains(&full_name) {
names_not_in_acceptable_users.push(full_name);
}
}
let context = context! {
result: names_not_in_acceptable_users
};
Template::render("admin/list/result", context)
}
pub fn routes() -> Vec<Route> {
let mut ret = Vec::new();
ret.append(&mut user::routes());
ret.append(&mut schnupper::routes());
ret.append(&mut boat::routes());
ret.append(&mut planned_event::routes());
ret.append(&mut routes![rss, show_rss]);
ret.append(&mut notification::routes());
ret.append(&mut mail::routes());
ret.append(&mut event::routes());
ret.append(&mut routes![rss, show_rss, show_list, list]);
ret
}

View File

@ -0,0 +1,116 @@
use crate::model::{
log::Log,
notification::Notification,
role::Role,
user::{AdminUser, User, UserWithDetails},
};
use itertools::Itertools;
use rocket::{
form::Form,
get, post,
request::FlashMessage,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
#[get("/notification")]
async fn index(
db: &State<SqlitePool>,
user: AdminUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.user, db).await,
);
let users: Vec<User> = User::all(db)
.await
.into_iter()
.filter(|u| u.last_access.is_some()) // Not useful to send notifications to people who are
// not logging in
.sorted_by_key(|u| u.name.clone())
.collect();
context.insert("roles", &Role::all(db).await);
context.insert("users", &users);
Template::render("admin/notification", context.into_json())
}
#[derive(FromForm, Debug)]
pub struct NotificationToSendGroup {
pub(crate) role_id: i32,
pub(crate) category: String,
pub(crate) message: String,
}
#[derive(FromForm, Debug)]
pub struct NotificationToSendUser {
pub(crate) user_id: i32,
pub(crate) category: String,
pub(crate) message: String,
}
#[post("/notification/group", data = "<data>")]
async fn send_group(
db: &State<SqlitePool>,
data: Form<NotificationToSendGroup>,
admin: AdminUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(
db,
format!("{admin:?} trying to send this notification: {d:?}"),
)
.await;
let Some(role) = Role::find_by_id(db, d.role_id).await else {
return Flash::error(Redirect::to("/admin/notification"), "Rolle gibt's ned");
};
for user in User::all_with_role(db, &role).await {
Notification::create(db, &user, &d.message, &d.category, None, None).await;
}
Log::create(db, "Notification successfully sent".into()).await;
Flash::success(
Redirect::to("/admin/notification"),
"Nachricht ausgeschickt",
)
}
#[post("/notification/user", data = "<data>")]
async fn send_user(
db: &State<SqlitePool>,
data: Form<NotificationToSendUser>,
admin: AdminUser,
) -> Flash<Redirect> {
let d = data.into_inner();
Log::create(
db,
format!("{admin:?} trying to send this notification: {d:?}"),
)
.await;
let Some(user) = User::find_by_id(db, d.user_id).await else {
return Flash::error(Redirect::to("/admin/notification"), "User gibt's ned");
};
Notification::create(db, &user, &d.message, &d.category, None, None).await;
Log::create(db, "Notification successfully sent".into()).await;
Flash::success(
Redirect::to("/admin/notification"),
"Nachricht ausgeschickt",
)
}
pub fn routes() -> Vec<Route> {
routes![index, send_user, send_group]
}

View File

@ -0,0 +1,40 @@
use crate::model::{
role::Role,
user::{SchnupperBetreuerUser, User, UserWithDetails},
};
use futures::future::join_all;
use rocket::{get, request::FlashMessage, routes, Route, State};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
#[get("/schnupper")]
async fn index(
db: &State<SqlitePool>,
user: SchnupperBetreuerUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
let user_futures: Vec<_> = User::all_with_role(db, &schnupperant)
.await
.into_iter()
.map(|u| async move { UserWithDetails::from_user(u, db).await })
.collect();
let users: Vec<UserWithDetails> = join_all(user_futures).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("schnupperanten", &users);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("admin/schnupper/index", context.into_json())
}
pub fn routes() -> Vec<Route> {
routes![index]
}

View File

@ -1,37 +1,240 @@
use crate::model::user::{AdminUser, User};
use std::collections::HashMap;
use crate::{
model::{
family::Family,
log::Log,
logbook::Logbook,
role::Role,
user::{
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
VorstandUser,
},
},
tera::Config,
};
use futures::future::join_all;
use rocket::{
form::Form,
get, post,
request::FlashMessage,
fs::TempFile,
get,
http::{ContentType, Status},
post,
request::{FlashMessage, FromRequest, Outcome},
response::{Flash, Redirect},
routes, FromForm, Route, State,
routes, FromForm, Request, Route, State,
};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
// Custom request guard to extract the Referer header
struct Referer(String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for Referer {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match request.headers().get_one("Referer") {
Some(referer) => Outcome::Success(Referer(referer.to_string())),
None => Outcome::Error((Status::BadRequest, ())),
}
}
}
#[get("/user")]
async fn index(
db: &State<SqlitePool>,
admin: AdminUser,
user: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let users = User::all(db).await;
let user_futures: Vec<_> = User::all(db)
.await
.into_iter()
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
.collect();
let user: User = user.into();
let allowed_to_edit = user.has_role(db, "admin").await;
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
let roles = Role::all(db).await;
let families = Family::all_with_members(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("allowed_to_edit", &allowed_to_edit);
context.insert("users", &users);
context.insert("loggedin_user", &admin.user);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("admin/user/index", context.into_json())
}
#[get("/user", rank = 2)]
async fn index_admin(
db: &State<SqlitePool>,
user: AdminUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let user_futures: Vec<_> = User::all(db)
.await
.into_iter()
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
.collect();
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
let user: User = user.user;
let allowed_to_edit = user.has_role(db, "admin").await;
let roles = Role::all(db).await;
let families = Family::all_with_members(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("allowed_to_edit", &allowed_to_edit);
context.insert("users", &users);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("admin/user/index", context.into_json())
}
#[get("/user/fees")]
async fn fees(
db: &State<SqlitePool>,
admin: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
let users = User::all_payer_groups(db).await;
let mut fees = Vec::new();
for user in users {
if let Some(fee) = user.fee(db).await {
fees.push(fee);
}
}
context.insert("fees", &fees);
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await,
);
Template::render("admin/user/fees", context.into_json())
}
#[get("/user/scheckbuch")]
async fn scheckbuch(
db: &State<SqlitePool>,
user: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
let scheckbooks = Role::find_by_name(db, "scheckbuch").await.unwrap();
let scheckbooks = User::all_with_role(db, &scheckbooks).await;
let mut scheckbooks_with_roles = Vec::new();
for s in scheckbooks {
scheckbooks_with_roles.push((
Logbook::completed_with_user(db, &s).await,
UserWithDetails::from_user(s, db).await,
))
}
context.insert("scheckbooks", &scheckbooks_with_roles);
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("admin/user/scheckbuch", context.into_json())
}
#[get("/user/fees/paid?<user_ids>")]
async fn fees_paid(
db: &State<SqlitePool>,
admin: AdminUser,
user_ids: Vec<i32>,
referer: Referer,
) -> Flash<Redirect> {
let mut res = String::new();
for user_id in user_ids {
let user = User::find_by_id(db, user_id).await.unwrap();
res.push_str(&format!("{} + ", user.name));
if user.has_role(db, "paid").await {
Log::create(
db,
format!("{} set fees NOT paid for '{}'", admin.user.name, user.name),
)
.await;
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
.await;
} else {
Log::create(
db,
format!("{} set fees paid for '{}'", admin.user.name, user.name),
)
.await;
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())
.await;
}
}
res.truncate(res.len() - 3); // remove ' + ' from the end
Flash::success(
Redirect::to(referer.0),
format!("Zahlungsstatus von {} erfolgreich geändert", res),
)
}
#[get("/user/<user>/send-welcome-mail")]
async fn send_welcome_mail(
db: &State<SqlitePool>,
_admin: AdminUser,
config: &State<Config>,
user: i32,
) -> Flash<Redirect> {
let Some(user) = User::find_by_id(db, user).await else {
return Flash::error(Redirect::to("/admin/user"), "User does not exist");
};
match user.send_welcome_email(db, &config.smtp_pw).await {
Ok(()) => Flash::success(
Redirect::to("/admin/user"),
format!("Willkommens-Email wurde an {} versandt.", user.name),
),
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
}
}
#[get("/user/<user>/reset-pw")]
async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
async fn resetpw(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await;
match user {
Some(user) => {
Log::create(
db,
format!("{} has resetted the pw for {}", admin.user.name, user.name),
)
.await;
user.reset_pw(db).await;
Flash::success(
Redirect::to("/admin/user"),
@ -43,8 +246,9 @@ async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<
}
#[get("/user/<user>/delete")]
async fn delete(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
async fn delete(db: &State<SqlitePool>, admin: AdminUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await;
Log::create(db, format!("{} deleted user: {user:?}", admin.user.name)).await;
match user {
Some(user) => {
user.delete(db).await;
@ -57,25 +261,36 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<R
}
}
#[derive(FromForm)]
pub struct UserEditForm {
#[derive(FromForm, Debug)]
pub struct UserEditForm<'a> {
pub(crate) id: i32,
pub(crate) is_guest: bool,
pub(crate) is_cox: bool,
pub(crate) is_admin: bool,
pub(crate) is_tech: bool,
pub(crate) dob: Option<String>,
pub(crate) weight: Option<String>,
pub(crate) sex: Option<String>,
pub(crate) roles: HashMap<String, String>,
pub(crate) member_since_date: Option<String>,
pub(crate) birthdate: Option<String>,
pub(crate) mail: Option<String>,
pub(crate) nickname: Option<String>,
pub(crate) notes: Option<String>,
pub(crate) phone: Option<String>,
pub(crate) address: Option<String>,
pub(crate) family_id: Option<i64>,
pub(crate) membership_pdf: Option<TempFile<'a>>,
}
#[post("/user", data = "<data>")]
#[post("/user", data = "<data>", format = "multipart/form-data")]
async fn update(
db: &State<SqlitePool>,
data: Form<UserEditForm>,
_admin: AdminUser,
data: Form<UserEditForm<'_>>,
admin: AdminUser,
) -> Flash<Redirect> {
let user = User::find_by_id(db, data.id).await;
Log::create(
db,
format!("{} updated user from {user:?} to {data:?}", admin.user.name),
)
.await;
let Some(user) = user else {
return Flash::error(
Redirect::to("/admin/user"),
@ -88,19 +303,43 @@ async fn update(
Flash::success(Redirect::to("/admin/user"), "Successfully updated user")
}
#[derive(FromForm)]
#[get("/user/<user>/membership")]
async fn download_membership_pdf(
db: &State<SqlitePool>,
admin: AdminUser,
user: i32,
) -> (ContentType, Vec<u8>) {
let user = User::find_by_id(db, user).await.unwrap();
let user = UserWithMembershipPdf::from(db, user).await;
Log::create(
db,
format!(
"{} downloaded membership application for user: {}",
admin.user.name, user.user.name
),
)
.await;
(ContentType::PDF, user.membership_pdf.unwrap())
}
#[derive(FromForm, Debug)]
struct UserAddForm<'r> {
name: &'r str,
is_guest: bool,
}
#[post("/user/new", data = "<data>")]
async fn create(
db: &State<SqlitePool>,
data: Form<UserAddForm<'_>>,
_admin: AdminUser,
admin: AdminUser,
) -> Flash<Redirect> {
if User::create(db, data.name, data.is_guest).await {
if User::create(db, data.name).await {
Log::create(
db,
format!("{} created new user: {data:?}", admin.user.name),
)
.await;
Flash::success(Redirect::to("/admin/user"), "Successfully created user")
} else {
Flash::error(
@ -111,5 +350,17 @@ async fn create(
}
pub fn routes() -> Vec<Route> {
routes![index, resetpw, update, create, delete]
routes![
index,
index_admin,
resetpw,
update,
create,
delete,
fees,
fees_paid,
scheckbuch,
download_membership_pdf,
send_welcome_mail
]
}

View File

@ -39,7 +39,6 @@ struct LoginForm<'r> {
password: &'r str,
}
#[derive(Debug)]
pub struct UserAgent(String);
#[rocket::async_trait]
@ -83,13 +82,21 @@ async fn login(
Log::create(
db,
format!(
"Succ login of {} with this useragent: {:?}",
login.name, agent
"Succ login of {} with this useragent: {}",
login.name, agent.0
),
)
.await;
Flash::success(Redirect::to("/"), "Login erfolgreich")
// Check for redirect_url cookie and redirect accordingly
match cookies.get_private("redirect_url") {
Some(redirect_cookie) => {
let redirect_url = redirect_cookie.value().to_string();
cookies.remove_private(redirect_cookie); // Remove the cookie after using it
Flash::success(Redirect::to(redirect_url), "Login erfolgreich")
}
None => Flash::success(Redirect::to("/"), "Login erfolgreich"),
}
}
#[get("/set-pw/<userid>")]

View File

@ -0,0 +1,92 @@
use crate::model::{
boat::Boat,
boathouse::Boathouse,
user::{AdminUser, UserWithDetails, VorstandUser},
};
use rocket::{
form::Form,
get, post,
request::FlashMessage,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
#[get("/boathouse")]
async fn index(
db: &State<SqlitePool>,
admin: VorstandUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
let boats = Boat::all_for_boatshouse(db).await;
let mut final_boats = Vec::new();
for boat in boats {
if boat.boat.boathouse(db).await.is_none() && !boat.boat.external {
final_boats.push(boat);
}
}
context.insert("boats", &final_boats);
let boathouse = Boathouse::get(db).await;
context.insert("boathouse", &boathouse);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(admin.into(), db).await,
);
Template::render("board/boathouse", context.into_json())
}
#[derive(FromForm)]
pub struct FormBoathouseToAdd {
pub boat_id: i32,
pub aisle: String,
pub side: String,
pub level: i32,
}
#[post("/boathouse", data = "<data>")]
async fn new<'r>(
db: &State<SqlitePool>,
data: Form<FormBoathouseToAdd>,
_admin: AdminUser,
) -> Flash<Redirect> {
match Boathouse::create(db, data.into_inner()).await {
Ok(_) => Flash::success(Redirect::to("/board/boathouse"), "Boot hinzugefügt"),
Err(e) => Flash::error(Redirect::to("/board/boathouse"), e),
}
}
#[get("/boathouse/<boathouse_id>/delete")]
async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boathouse_id: i32) -> Flash<Redirect> {
let boat = Boathouse::find_by_id(db, boathouse_id).await;
match boat {
Some(boat) => {
boat.delete(db).await;
Flash::success(Redirect::to("/board/boathouse"), "Bootsplatz gelöscht")
}
None => Flash::error(Redirect::to("/board/boathouse"), "Boatplace does not exist"),
}
}
//#[post("/boat/new", data = "<data>")]
//async fn create(
// db: &State<SqlitePool>,
// data: Form<BoatToAdd<'_>>,
// _admin: AdminUser,
//) -> Flash<Redirect> {
// match Boat::create(db, data.into_inner()).await {
// Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot hinzugefügt"),
// Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
// }
//}
pub fn routes() -> Vec<Route> {
routes![index, new, delete]
}

9
src/tera/board/mod.rs Normal file
View File

@ -0,0 +1,9 @@
use rocket::Route;
pub mod boathouse;
pub fn routes() -> Vec<Route> {
let mut ret = Vec::new();
ret.append(&mut boathouse::routes());
ret
}

View File

@ -13,7 +13,7 @@ use crate::{
model::{
boat::Boat,
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
user::{CoxUser, NonGuestUser, TechUser, User},
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithDetails},
},
tera::log::KioskCookie,
};
@ -45,7 +45,7 @@ async fn index_kiosk(
async fn index(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: NonGuestUser,
user: DonauLinzUser,
) -> Template {
let boatdamages = BoatDamage::all(db).await;
let boats = Boat::all(db).await;
@ -57,7 +57,10 @@ async fn index(
context.insert("boatdamages", &boatdamages);
context.insert("boats", &boats);
context.insert("loggedin_user", &user.user);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("boatdamages", context.into_json())
}
@ -73,13 +76,14 @@ pub struct FormBoatDamageToAdd<'r> {
async fn create<'r>(
db: &State<SqlitePool>,
data: Form<FormBoatDamageToAdd<'r>>,
user: NonGuestUser,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user: User = user.into();
let boatdamage_to_add = BoatDamageToAdd {
boat_id: data.boat_id,
desc: data.desc,
lock_boat: data.lock_boat,
user_id_created: user.user.id as i32,
user_id_created: user.id as i32,
};
match BoatDamage::create(db, boatdamage_to_add).await {
Ok(_) => Flash::success(

223
src/tera/boatreservation.rs Normal file
View File

@ -0,0 +1,223 @@
use chrono::NaiveDate;
use rocket::{
form::Form,
get, post,
request::FlashMessage,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use rocket_dyn_templates::Template;
use sqlx::SqlitePool;
use tera::Context;
use crate::{
model::{
boat::Boat,
boatreservation::{BoatReservation, BoatReservationToAdd},
log::Log,
user::{DonauLinzUser, User, UserWithDetails},
},
tera::log::KioskCookie,
};
#[get("/")]
async fn index_kiosk(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
_kiosk: KioskCookie,
) -> Template {
let boatreservations = BoatReservation::all_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
let linz_boats = Boat::all_for_boatshouse(db).await;
let mut boats = Vec::new();
for boat in linz_boats {
if boat.boat.owner.is_none() {
boats.push(boat);
}
}
context.insert("boatreservations", &boatreservations);
context.insert("boats", &boats);
context.insert("user", &User::all(db).await);
context.insert("show_kiosk_header", &true);
Template::render("boatreservations", context.into_json())
}
#[get("/", rank = 2)]
async fn index(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: DonauLinzUser,
) -> Template {
let boatreservations = BoatReservation::all_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
let linz_boats = Boat::all_for_boatshouse(db).await;
let mut boats = Vec::new();
for boat in linz_boats {
if boat.boat.owner.is_none() {
boats.push(boat);
}
}
context.insert("boatreservations", &boatreservations);
context.insert("boats", &boats);
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("boatreservations", context.into_json())
}
#[derive(Debug, FromForm)]
pub struct FormBoatReservationToAdd<'r> {
pub boat_id: i64,
pub start_date: &'r str,
pub end_date: &'r str,
pub time_desc: &'r str,
pub usage: &'r str,
pub user_id_applicant: Option<i64>,
}
#[post("/new", data = "<data>", rank = 2)]
async fn create<'r>(
db: &State<SqlitePool>,
data: Form<FormBoatReservationToAdd<'r>>,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user_applicant: User = user.into();
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
let boatreservation_to_add = BoatReservationToAdd {
boat: &boat,
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
time_desc: data.time_desc,
usage: data.usage,
user_applicant: &user_applicant,
};
match BoatReservation::create(db, boatreservation_to_add).await {
Ok(_) => Flash::success(
Redirect::to("/boatreservation"),
"Reservierung erfolgreich hinzugefügt",
),
Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
}
}
#[post("/new", data = "<data>")]
async fn create_from_kiosk<'r>(
db: &State<SqlitePool>,
data: Form<FormBoatReservationToAdd<'r>>,
_kiosk: KioskCookie,
) -> Flash<Redirect> {
let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
.await
.unwrap();
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
let boatreservation_to_add = BoatReservationToAdd {
boat: &boat,
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
time_desc: data.time_desc,
usage: data.usage,
user_applicant: &user_applicant,
};
match BoatReservation::create(db, boatreservation_to_add).await {
Ok(_) => Flash::success(
Redirect::to("/boatreservation"),
"Reservierung erfolgreich hinzugefügt",
),
Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
}
}
#[derive(FromForm, Debug)]
pub struct ReservationEditForm {
pub(crate) id: i32,
pub(crate) time_desc: String,
pub(crate) usage: String,
}
#[post("/", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<ReservationEditForm>,
user: User,
) -> Flash<Redirect> {
let Some(reservation) = BoatReservation::find_by_id(db, data.id).await else {
return Flash::error(
Redirect::to("/boatreservation"),
format!("Reservation with ID {} does not exist!", data.id),
);
};
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
return Flash::error(
Redirect::to("/boatreservation"),
"Not allowed to update reservation (only admins + creator do so).".to_string(),
);
}
Log::create(
db,
format!(
"{} updated reservation from {reservation:?} to {data:?}",
user.name
),
)
.await;
reservation.update(db, data.into_inner()).await;
Flash::success(
Redirect::to("/boatreservation"),
"Reservierung erfolgreich bearbeitet",
)
}
#[get("/<reservation_id>/delete")]
async fn delete<'r>(
db: &State<SqlitePool>,
reservation_id: i32,
user: DonauLinzUser,
) -> Flash<Redirect> {
let reservation = BoatReservation::find_by_id(db, reservation_id)
.await
.unwrap();
if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
reservation.delete(db).await;
Flash::success(
Redirect::to("/boatreservation"),
"Reservierung erfolgreich gelöscht",
)
} else {
Flash::error(
Redirect::to("/boatreservation"),
"Nur der Reservierer darf die Reservierung löschen.".to_string(),
)
}
}
pub fn routes() -> Vec<Route> {
routes![
index,
index_kiosk,
create,
create_from_kiosk,
delete,
update
]
}

View File

@ -7,9 +7,9 @@ use rocket::{
use sqlx::SqlitePool;
use crate::model::{
event::Event,
log::Log,
planned_event::PlannedEvent,
trip::{CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError},
trip::{self, CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError},
tripdetails::{TripDetails, TripDetailsToAdd},
user::CoxUser,
};
@ -34,7 +34,7 @@ async fn create(
//)
//.await;
Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich erstellt.")
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
}
#[derive(FromForm)]
@ -54,34 +54,35 @@ async fn update(
cox: CoxUser,
) -> Flash<Redirect> {
if let Some(trip) = Trip::find_by_id(db, trip_id).await {
match Trip::update_own(
db,
&cox,
&trip,
data.max_people,
data.notes,
data.trip_type,
data.always_show,
data.is_locked,
)
.await
{
Ok(_) => Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich aktualisiert."),
let update = trip::TripUpdate {
cox: &cox,
trip: &trip,
max_people: data.max_people,
notes: data.notes,
trip_type: data.trip_type,
always_show: data.always_show,
is_locked: data.is_locked,
};
match Trip::update_own(db, &update).await {
Ok(_) => Flash::success(
Redirect::to("/planned"),
"Ausfahrt erfolgreich aktualisiert.",
),
Err(TripUpdateError::NotYourTrip) => {
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
Flash::error(Redirect::to("/planned"), "Nicht deine Ausfahrt!")
}
Err(TripUpdateError::TripDetailsDoesNotExist) => {
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
}
}
} else {
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
}
}
#[get("/join/<planned_event_id>")]
async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Flash<Redirect> {
if let Some(planned_event) = PlannedEvent::find_by_id(db, planned_event_id).await {
if let Some(planned_event) = Event::find_by_id(db, planned_event_id).await {
match Trip::new_join(db, &cox, &planned_event).await {
Ok(_) => {
Log::create(
@ -92,21 +93,24 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Fl
),
)
.await;
Flash::success(Redirect::to("/"), "Danke für's helfen!")
Flash::success(Redirect::to("/planned"), "Danke für's helfen!")
}
Err(CoxHelpError::CanceledEvent) => {
Flash::error(Redirect::to("/planned"), "Die Ausfahrt wurde leider abgesagt...")
}
Err(CoxHelpError::AlreadyRegisteredAsCox) => {
Flash::error(Redirect::to("/"), "Du hilfst bereits aus!")
Flash::error(Redirect::to("/planned"), "Du hilfst bereits aus!")
}
Err(CoxHelpError::AlreadyRegisteredAsRower) => Flash::error(
Redirect::to("/"),
Redirect::to("/planned"),
"Du hast dich bereits als Ruderer angemeldet!",
),
Err(CoxHelpError::DetailsLocked) => {
Flash::error(Redirect::to("/"), "Boot ist bereits eingeteilt.")
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.")
}
}
} else {
Flash::error(Redirect::to("/"), "Event gibt's nicht")
Flash::error(Redirect::to("/planned"), "Event gibt's nicht")
}
}
@ -114,18 +118,18 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Fl
async fn remove_trip(db: &State<SqlitePool>, trip_id: i64, cox: CoxUser) -> Flash<Redirect> {
let trip = Trip::find_by_id(db, trip_id).await;
match trip {
None => Flash::error(Redirect::to("/"), "Trip gibt's nicht!"),
None => Flash::error(Redirect::to("/planned"), "Trip gibt's nicht!"),
Some(trip) => match trip.delete(db, &cox).await {
Ok(_) => {
Log::create(db, format!("Cox {} deleted trip.id={}", cox.name, trip_id)).await;
Flash::success(Redirect::to("/"), "Erfolgreich gelöscht!")
Flash::success(Redirect::to("/planned"), "Erfolgreich gelöscht!")
}
Err(TripDeleteError::SomebodyAlreadyRegistered) => Flash::error(
Redirect::to("/"),
Redirect::to("/planned"),
"Ausfahrt kann nicht gelöscht werden, da bereits jemand registriert ist!",
),
Err(TripDeleteError::NotYourTrip) => {
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
Flash::error(Redirect::to("/planned"), "Nicht deine Ausfahrt!")
}
},
}
@ -133,7 +137,7 @@ async fn remove_trip(db: &State<SqlitePool>, trip_id: i64, cox: CoxUser) -> Flas
#[get("/remove/<planned_event_id>")]
async fn remove(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) -> Flash<Redirect> {
if let Some(planned_event) = PlannedEvent::find_by_id(db, planned_event_id).await {
if let Some(planned_event) = Event::find_by_id(db, planned_event_id).await {
match Trip::delete_by_planned_event(db, &cox, &planned_event).await {
Ok(_) => {
Log::create(
@ -145,17 +149,17 @@ async fn remove(db: &State<SqlitePool>, planned_event_id: i64, cox: CoxUser) ->
)
.await;
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
}
Err(TripHelpDeleteError::DetailsLocked) => {
Flash::error(Redirect::to("/"), "Boot bereits eingeteilt")
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!")
}
Err(TripHelpDeleteError::CoxNotHelping) => {
Flash::error(Redirect::to("/"), "Steuermann hilft nicht aus...")
Flash::error(Redirect::to("/planned"), "Steuermann hilft nicht aus...")
}
}
} else {
Flash::error(Redirect::to("/"), "Planned_event does not exist.")
Flash::error(Redirect::to("/planned"), "Planned_event does not exist.")
}
}
@ -202,7 +206,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -250,7 +254,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -288,7 +292,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -326,7 +330,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -354,7 +358,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -367,7 +371,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -391,14 +395,14 @@ mod test {
.body("name=cox&password=cox"); // Add the form data to the request body;
login.dispatch().await;
let req = client.get("/join/1");
let req = client.get("/planned/join/1");
let _ = req.dispatch().await;
let req = client.get("/cox/join/1");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -429,7 +433,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -470,7 +474,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -498,7 +502,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
@ -526,7 +530,7 @@ mod test {
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()

View File

@ -18,7 +18,7 @@ use tera::Context;
use crate::model::{
log::Log,
user::{AdminUser, User},
user::{AdminUser, User, UserWithDetails},
};
#[derive(Serialize)]
@ -51,7 +51,7 @@ async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
Template::render(
"ergo.final",
context!(loggedin_user: &_user.user, thirty, dozen),
context!(loggedin_user: &UserWithDetails::from_user(_user.user, db).await, thirty, dozen),
)
}
@ -120,7 +120,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("loggedin_user", &user);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("users", &users);
context.insert("thirty", &thirty);
context.insert("dozen", &dozen);

View File

@ -17,16 +17,17 @@ use tera::Context;
use crate::model::{
boat::Boat,
boatreservation::BoatReservation,
log::Log,
logbook::{
LogToAdd, LogToFinalize, Logbook, LogbookCreateError, LogbookDeleteError,
LogbookUpdateError,
},
logtype::LogType,
user::{NonGuestUser, User, UserWithWaterStatus},
user::{AdminUser, DonauLinzUser, User, UserWithDetails},
};
pub struct KioskCookie(String);
pub struct KioskCookie(());
#[rocket::async_trait]
impl<'r> FromRequest<'r> for KioskCookie {
@ -34,7 +35,7 @@ impl<'r> FromRequest<'r> for KioskCookie {
async fn from_request(request: &'r Request<'_>) -> request::Outcome<KioskCookie, Self::Error> {
match request.cookies().get_private("kiosk") {
Some(cookie) => request::Outcome::Success(KioskCookie(cookie.value().to_string())),
Some(_) => request::Outcome::Success(KioskCookie(())),
None => request::Outcome::Forward(rocket::http::Status::SeeOther),
}
}
@ -44,24 +45,32 @@ impl<'r> FromRequest<'r> for KioskCookie {
async fn index(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: NonGuestUser,
user: DonauLinzUser,
) -> Template {
let boats = Boat::for_user(db, &user.user).await;
let boats = Boat::for_user(db, &user).await;
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
let mut coxes: Vec<UserWithDetails> = futures::future::join_all(
User::cox(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
let users: Vec<UserWithWaterStatus> = futures::future::join_all(
coxes.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let mut users: Vec<UserWithDetails> = futures::future::join_all(
User::all(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
users.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
@ -73,21 +82,41 @@ async fn index(
}
context.insert("boats", &boats);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
);
context.insert("coxes", &coxes);
context.insert("users", &users);
context.insert("logtypes", &logtypes);
context.insert("loggedin_user", &user.user);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
context.insert("on_water", &on_water);
context.insert("distances", &distances);
Template::render("log", context.into_json())
}
#[get("/show", rank = 2)]
async fn show(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
#[get("/show", rank = 3)]
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
let logs = Logbook::completed(db).await;
Template::render("log.completed", context!(logs, loggedin_user: &user.user))
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into(), db).await),
)
}
#[get("/show?<year>", rank = 2)]
async fn show_for_year(db: &State<SqlitePool>, user: AdminUser, year: i32) -> Template {
let logs = Logbook::completed_in_year(db, year).await;
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithDetails::from_user(user.user, db).await),
)
}
#[get("/show")]
@ -119,23 +148,33 @@ async fn new_kiosk(
async fn kiosk(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
kiosk: KioskCookie,
_kiosk: KioskCookie,
) -> Template {
let boats = Boat::all_at_location(db, kiosk.0).await;
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
let boats = Boat::all(db).await;
let mut coxes: Vec<UserWithDetails> = futures::future::join_all(
User::cox(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
let users: Vec<UserWithWaterStatus> = futures::future::join_all(
coxes.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let mut users: Vec<UserWithDetails> = futures::future::join_all(
User::all(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
users.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
@ -147,6 +186,10 @@ async fn kiosk(
}
context.insert("boats", &boats);
context.insert(
"reservations",
&BoatReservation::all_future_with_groups(db).await,
);
context.insert("coxes", &coxes);
context.insert("users", &users);
context.insert("logtypes", &logtypes);
@ -160,16 +203,16 @@ async fn kiosk(
async fn create_logbook(
db: &SqlitePool,
data: Form<LogToAdd>,
user: &NonGuestUser,
user: &DonauLinzUser,
) -> Flash<Redirect> {
match Logbook::create(
db,
data.into_inner(),
&user.user
user
)
.await
{
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt erfolgreich hinzugefügt"),
Ok(msg) => Flash::success(Redirect::to("/log"), format!("Ausfahrt erfolgreich hinzugefügt{msg}")),
Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Boot schon am Wasser"),
Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)),
Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"),"Boot gesperrt"),
@ -182,8 +225,9 @@ async fn create_logbook(
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::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"),
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "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(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die in der letzten Woche enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
Err(LogbookCreateError::CantChangeHandoperatableStatusForThisBoat) => Flash::error(Redirect::to("/log"), "Handsteuer-Status dieses Boots kann nicht verändert werden."),
Err(LogbookCreateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
}
}
@ -191,14 +235,11 @@ async fn create_logbook(
async fn create(
db: &State<SqlitePool>,
data: Form<LogToAdd>,
user: NonGuestUser,
user: DonauLinzUser,
) -> Flash<Redirect> {
Log::create(
db,
format!(
"User {} tries to create log entry={:?}",
user.user.name, data
),
format!("User {} tries to create log entry={:?}", &user.name, data),
)
.await;
@ -221,7 +262,13 @@ async fn create_kiosk(
} else if let Some(shipmaster) = data.shipmaster {
User::find_by_id(db, shipmaster as i32).await.unwrap()
} else {
User::find_by_id(db, data.rowers[0] as i32).await.unwrap()
let Some(rower) = data.rowers.first() else {
return Flash::error(
Redirect::to("/log"),
"Ausfahrt ohne Benutzer kann nicht angelegt werden.",
);
};
User::find_by_id(db, *rower as i32).await.unwrap()
};
Log::create(
db,
@ -232,14 +279,14 @@ async fn create_kiosk(
)
.await;
create_logbook(db, data, &NonGuestUser::try_from(creator).unwrap()).await //TODO: fixme
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme
}
async fn home_logbook(
db: &SqlitePool,
data: Form<LogToFinalize>,
logbook_id: i32,
user: &NonGuestUser,
user: &DonauLinzUser,
) -> Flash<Redirect> {
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
let Some(logbook) = logbook else {
@ -249,10 +296,11 @@ async fn home_logbook(
);
};
match logbook.home(db, &user.user, data.into_inner()).await {
match logbook.home(db,user, data.into_inner()).await {
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::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "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(LogbookUpdateError::TooFast(km, min)) => Flash::error(Redirect::to("/log"), format!("KM zu groß für die eingegebene Dauer ({km} km in {min} Minuten). Bitte überprüfe deine Start- und Endzeit und versuche es erneut.")),
Err(e) => Flash::error(
Redirect::to("/log"),
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
@ -279,12 +327,11 @@ async fn home_kiosk(
db,
data,
logbook_id,
&NonGuestUser::try_from(
&DonauLinzUser(
User::find_by_id(db, logbook.shipmaster as i32)
.await
.unwrap(), //TODO: fixme
)
.unwrap(),
.unwrap(),
), //TODO: fixme
)
.await
}
@ -294,13 +341,13 @@ async fn home(
db: &State<SqlitePool>,
data: Form<LogToFinalize>,
logbook_id: i32,
user: NonGuestUser,
user: DonauLinzUser,
) -> Flash<Redirect> {
Log::create(
db,
format!(
"User {} tries to finish log entry {logbook_id} {data:?}",
user.user.name
&user.name
),
)
.await;
@ -309,12 +356,12 @@ async fn home(
}
#[get("/<logbook_id>/delete", rank = 2)]
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: User) -> Flash<Redirect> {
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) -> Flash<Redirect> {
let logbook = Logbook::find_by_id(db, logbook_id).await;
if let Some(logbook) = logbook {
Log::create(
db,
format!("User {} tries to delete log entry {logbook_id}", user.name),
format!("User {} tries to delete log entry {logbook_id}", &user.name),
)
.await;
match logbook.delete(db, &user).await {
@ -376,6 +423,7 @@ pub fn routes() -> Vec<Route> {
new_kiosk,
show,
show_kiosk,
show_for_year,
delete,
delete_kiosk
]
@ -419,7 +467,7 @@ mod test {
assert!(text.contains("Logbuch"));
assert!(text.contains("Neue Ausfahrt"));
assert!(!text.contains("Ottensheim Boot"));
//assert!(!text.contains("Ottensheim Boot"));
}
#[sqlx::test]
@ -605,7 +653,7 @@ mod test {
assert!(text.contains("private_boat_from_rower"));
//Doesn't see the one's in Ottensheim
assert!(!text.contains("Ottensheim Boot"));
//assert!(!text.contains("Ottensheim Boot"));
}
#[sqlx::test]

View File

@ -1,12 +1,12 @@
use rocket::{get, http::ContentType, routes, Route, State};
use sqlx::SqlitePool;
use crate::model::planned_event::PlannedEvent;
use crate::model::event::Event;
#[get("/cal")]
async fn cal(db: &State<SqlitePool>) -> (ContentType, String) {
//TODO: add unit test once proper functionality is there
(ContentType::Calendar, PlannedEvent::get_ics_feed(db).await)
(ContentType::Calendar, Event::get_ics_feed(db).await)
}
pub fn routes() -> Vec<Route> {

View File

@ -1,33 +1,45 @@
use std::{fs::OpenOptions, io::Write};
use chrono::Local;
use rocket::{
catch, catchers,
fairing::AdHoc,
fairing::{AdHoc, Fairing, Info, Kind},
form::Form,
fs::FileServer,
get, post,
get,
http::Cookie,
post,
request::FlashMessage,
response::{Flash, Redirect},
routes, Build, FromForm, Rocket, State,
routes,
time::{Duration, OffsetDateTime},
Build, Data, FromForm, Request, Rocket, State,
};
use rocket_dyn_templates::{tera::Context, Template};
use rocket_dyn_templates::Template;
use serde::Deserialize;
use sqlx::SqlitePool;
use tera::Context;
use crate::model::{
log::Log,
tripdetails::TripDetails,
triptype::TripType,
user::User,
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
logbook::Logbook,
notification::Notification,
role::Role,
user::{User, UserWithDetails, SCHECKBUCH},
};
pub(crate) mod admin;
mod auth;
pub(crate) mod board;
mod boatdamage;
pub(crate) mod boatreservation;
mod cox;
mod ergo;
mod log;
mod misc;
mod notification;
mod planned;
mod stat;
pub(crate) mod trailerreservation;
#[derive(FromForm, Debug)]
struct LoginForm<'r> {
@ -35,6 +47,58 @@ struct LoginForm<'r> {
password: &'r str,
}
#[get("/")]
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
if user.has_role(db, "scheckbuch").await {
let last_trips = Logbook::completed_with_user(db, &user).await;
context.insert("last_trips", &last_trips);
}
context.insert("notifications", &Notification::for_user(db, &user).await);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("costs_scheckbuch", &SCHECKBUCH);
Template::render("index", context.into_json())
}
#[get("/impressum")]
async fn impressum(db: &State<SqlitePool>, user: Option<User>) -> Template {
let mut context = Context::new();
if let Some(user) = user {
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
}
Template::render("impressum", context.into_json())
}
#[get("/steering")]
async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
let bootskundige =
User::all_with_role(db, &Role::find_by_name(db, "Bootsführer").await.unwrap()).await;
let mut coxes = User::all_with_role(db, &Role::find_by_name(db, "cox").await.unwrap()).await;
coxes.retain(|user| !bootskundige.contains(user)); // Remove bootskundige from coxes list
coxes.retain(|user| user.name != "Externe Steuerperson");
context.insert("coxes", &coxes);
context.insert("bootskundige", &bootskundige);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("steering", context.into_json())
}
#[post("/", data = "<login>")]
async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String {
match User::login(db, login.name, login.password).await {
@ -43,187 +107,110 @@ async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String
}
}
#[get("/")]
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
let mut context = Context::new();
#[catch(401)] //Unauthorized
fn unauthorized_error(req: &Request) -> Redirect {
// Save the URL the user tried to access, to be able to go there once logged in
let mut redirect_cookie = Cookie::new("redirect_url", format!("{}", req.uri()));
println!("{}", req.uri());
redirect_cookie.set_expires(OffsetDateTime::now_utc() + Duration::hours(1));
req.cookies().add_private(redirect_cookie);
if user.is_cox || user.is_admin {
let triptypes = TripType::all(db).await;
context.insert("trip_types", &triptypes);
}
let days = user.get_days(db).await;
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
println!("{user:#?}");
context.insert("loggedin_user", &user);
context.insert("days", &days);
Template::render("index", context.into_json())
}
#[get("/join/<trip_details_id>?<user_note>")]
async fn join(
db: &State<SqlitePool>,
trip_details_id: i64,
user: User,
user_note: Option<String>,
) -> Flash<Redirect> {
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
};
match UserTrip::create(db, &user, &trip_details, user_note).await {
Ok(_) => {
Log::create(
db,
format!(
"User {} registered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
}
Err(UserTripError::EventAlreadyFull) => {
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
}
Err(UserTripError::AlreadyRegistered) => {
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
}
Err(UserTripError::AlreadyRegisteredAsCox) => {
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
}
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
Redirect::to("/"),
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
),
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
Redirect::to("/"),
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
),
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
Redirect::to("/"),
"Du darfst keine Gäste hinzufügen.",
),
Err(UserTripError::DetailsLocked) => Flash::error(
Redirect::to("/"),
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
),
}
}
#[get("/remove/<trip_details_id>/<name>")]
async fn remove_guest(
db: &State<SqlitePool>,
trip_details_id: i64,
user: User,
name: String,
) -> Flash<Redirect> {
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
};
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
Ok(_) => {
Log::create(
db,
format!(
"User {} unregistered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
}
Err(UserTripDeleteError::DetailsLocked) => {
Log::create(
db,
format!(
"User {} tried to unregister for locked trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
}
Err(UserTripDeleteError::GuestNotParticipating) => {
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
}
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
Redirect::to("/"),
"Keine Berechtigung um den Gast zu entfernen.",
),
}
}
#[get("/remove/<trip_details_id>")]
async fn remove(db: &State<SqlitePool>, trip_details_id: i64, user: User) -> Flash<Redirect> {
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
};
match UserTrip::delete(db, &user, &trip_details, None).await {
Ok(_) => {
Log::create(
db,
format!(
"User {} unregistered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
}
Err(UserTripDeleteError::DetailsLocked) => {
Log::create(
db,
format!(
"User {} tried to unregister for locked trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
}
Err(_) => {
panic!("Not possible to be here");
}
}
}
#[catch(401)] //unauthorized
fn unauthorized_error() -> Redirect {
Redirect::to("/auth")
}
#[catch(403)] //forbidden
fn forbidden_error() -> Flash<Redirect> {
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
}
struct Usage {}
#[rocket::async_trait]
impl Fairing for Usage {
fn info(&self) -> Info {
Info {
name: "Usage stats of website",
kind: Kind::Request,
}
}
// Increment the counter for `GET` and `POST` requests.
async fn on_request(&self, req: &mut Request<'_>, _: &mut Data<'_>) {
let timestamp = Local::now().format("%Y-%m-%dT%H:%M:%S");
let user = match req.cookies().get_private("loggedin_user") {
Some(user_id) => match user_id.value().parse::<i32>() {
Ok(user_id) => {
let db = req.rocket().state::<SqlitePool>().unwrap();
if let Some(user) = User::find_by_id(db, user_id).await {
format!("User: {}", user.name)
} else {
format!("USER ID {user_id} NOT EXISTS")
}
}
Err(_) => format!("INVALID USER ID ({user_id})"),
},
None => "NOT LOGGED IN".to_string(),
};
let uri = req.uri().to_string();
if !uri.ends_with(".css")
&& !uri.ends_with(".js")
&& !uri.ends_with(".ico")
&& !uri.ends_with(".json")
&& !uri.ends_with(".png")
{
let config = req.rocket().state::<Config>().unwrap();
let Ok(mut file) = OpenOptions::new()
.append(true)
.open(config.usage_log_path.clone())
else {
eprintln!(
"File {} can't be found, not saving usage logs",
config.usage_log_path.clone()
);
return;
};
if let Err(e) = writeln!(file, "{timestamp};{user};{uri}") {
eprintln!("Couldn't write to file: {}", e);
}
}
}
}
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct Config {
rss_key: String,
smtp_pw: String,
usage_log_path: String,
pub openweathermap_key: String,
}
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
rocket
.mount("/", routes![index, join, remove, remove_guest])
.mount("/", routes![index, steering, impressum])
.mount("/auth", auth::routes())
.mount("/wikiauth", routes![wikiauth])
.mount("/log", log::routes())
.mount("/planned", planned::routes())
.mount("/ergo", ergo::routes())
.mount("/notification", notification::routes())
.mount("/stat", stat::routes())
.mount("/boatdamage", boatdamage::routes())
.mount("/boatreservation", boatreservation::routes())
.mount("/trailerreservation", trailerreservation::routes())
.mount("/cox", cox::routes())
.mount("/admin", admin::routes())
.mount("/board", board::routes())
.mount("/", misc::routes())
.mount("/public", FileServer::from("static/"))
.register("/", catchers![unauthorized_error])
.register("/", catchers![unauthorized_error, forbidden_error])
.attach(Template::fairing())
.attach(AdHoc::config::<Config>())
.attach(Usage {})
}
#[cfg(test)]
@ -255,7 +242,11 @@ mod test {
assert_eq!(response.status(), Status::Ok);
assert!(response.into_string().await.unwrap().contains("Ausfahrten"));
assert!(response
.into_string()
.await
.unwrap()
.contains("Ruderassistent"));
}
#[sqlx::test]
@ -274,75 +265,6 @@ mod test {
assert_eq!(response.headers().get("Location").next(), Some("/auth"));
}
#[sqlx::test]
fn test_join_and_remove() {
let db = testdb!();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=rower&password=rower"); // Add the form data to the request body;
login.dispatch().await;
let req = client.get("/join/1");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
let req = client.get("/remove/1");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
}
#[sqlx::test]
fn test_join_invalid_event() {
let db = testdb!();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=rower&password=rower"); // Add the form data to the request body;
login.dispatch().await;
let req = client.get("/join/9999");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
}
#[sqlx::test]
fn test_public() {
let db = testdb!();

32
src/tera/notification.rs Normal file
View File

@ -0,0 +1,32 @@
use rocket::{
get,
response::{Flash, Redirect},
routes, Route, State,
};
use sqlx::SqlitePool;
use crate::model::{notification::Notification, user::User};
#[get("/<notification_id>/read")]
async fn mark_read(db: &State<SqlitePool>, user: User, notification_id: i64) -> Flash<Redirect> {
let Some(notification) = Notification::find_by_id(db, notification_id).await else {
return Flash::error(
Redirect::to("/"),
format!("Nachricht mit ID {notification_id} nicht gefunden."),
);
};
if notification.user_id == user.id {
notification.mark_read(db).await;
Flash::success(Redirect::to("/"), "Nachricht als gelesen markiert")
} else {
Flash::success(
Redirect::to("/"),
"Du kannst fremde Nachrichten nicht als gelesen markieren.",
)
}
}
pub fn routes() -> Vec<Route> {
routes![mark_read]
}

282
src/tera/planned.rs Normal file
View File

@ -0,0 +1,282 @@
use rocket::{
get,
request::FlashMessage,
response::{Flash, Redirect},
routes, Route, State,
};
use rocket_dyn_templates::Template;
use sqlx::SqlitePool;
use tera::Context;
use crate::model::{
log::Log,
tripdetails::TripDetails,
triptype::TripType,
user::{AllowedForPlannedTripsUser, User, UserWithDetails},
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
};
#[get("/")]
async fn index(
db: &State<SqlitePool>,
user: AllowedForPlannedTripsUser,
flash: Option<FlashMessage<'_>>,
) -> Template {
let user: User = user.into();
let mut context = Context::new();
if user.has_role(db, "cox").await || user.has_role(db, "manage_events").await {
let triptypes = TripType::all(db).await;
context.insert("trip_types", &triptypes);
}
let days = user.get_days(db).await;
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("fee", &user.fee(db).await);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("days", &days);
Template::render("planned", context.into_json())
}
#[get("/join/<trip_details_id>?<user_note>")]
async fn join(
db: &State<SqlitePool>,
trip_details_id: i64,
user: AllowedForPlannedTripsUser,
user_note: Option<String>,
) -> Flash<Redirect> {
let user: User = user.into();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
};
match UserTrip::create(db, &user, &trip_details, user_note).await {
Ok(registered_user) => {
if registered_user == user.name {
Log::create(
db,
format!(
"User {} registered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
}else{
Log::create(
db,
format!(
"User {} registered the guest '{}' for trip_details.id={}",
user.name, registered_user, trip_details_id
),
).await;
}
Flash::success(Redirect::to("/planned"), "Erfolgreich angemeldet!")
}
Err(UserTripError::EventAlreadyFull) => {
Flash::error(Redirect::to("/planned"), "Event bereits ausgebucht!")
}
Err(UserTripError::AlreadyRegistered) => {
Flash::error(Redirect::to("/planned"), "Du nimmst bereits teil!")
}
Err(UserTripError::AlreadyRegisteredAsCox) => {
Flash::error(Redirect::to("/planned"), "Du hilfst bereits als Steuerperson aus!")
}
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
Redirect::to("/planned"),
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
),
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
Redirect::to("/planned"),
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
),
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
Redirect::to("/planned"),
"Du darfst keine Gäste hinzufügen.",
),
Err(UserTripError::DetailsLocked) => Flash::error(
Redirect::to("/planned"),
"Die Bootseinteilung wurde bereits gemacht. Wenn du noch mitrudern möchtest, frag bitte bei einer angemeldeten Steuerperson nach, ob das noch möglich ist.",
),
}
}
#[get("/remove/<trip_details_id>/<name>")]
async fn remove_guest(
db: &State<SqlitePool>,
trip_details_id: i64,
user: AllowedForPlannedTripsUser,
name: String,
) -> Flash<Redirect> {
let user: User = user.into();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
};
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
Ok(_) => {
Log::create(
db,
format!(
"User {} unregistered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
}
Err(UserTripDeleteError::DetailsLocked) => {
Log::create(
db,
format!(
"User {} tried to unregister for locked trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!")
}
Err(UserTripDeleteError::GuestNotParticipating) => {
Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.")
}
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
Redirect::to("/planned"),
"Keine Berechtigung um den Gast zu entfernen.",
),
}
}
#[get("/remove/<trip_details_id>")]
async fn remove(
db: &State<SqlitePool>,
trip_details_id: i64,
user: AllowedForPlannedTripsUser,
) -> Flash<Redirect> {
let user: User = user.into();
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
};
match UserTrip::delete(db, &user, &trip_details, None).await {
Ok(_) => {
Log::create(
db,
format!(
"User {} unregistered for trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
}
Err(UserTripDeleteError::DetailsLocked) => {
Log::create(
db,
format!(
"User {} tried to unregister for locked trip_details.id={}",
user.name, trip_details_id
),
)
.await;
Flash::error(Redirect::to("/planned"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
}
Err(_) => {
panic!("Not possible to be here");
}
}
}
pub fn routes() -> Vec<Route> {
routes![index, join, remove, remove_guest]
}
#[cfg(test)]
mod test {
use rocket::{
http::{ContentType, Status},
local::asynchronous::Client,
};
use sqlx::SqlitePool;
use crate::testdb;
#[sqlx::test]
fn test_join_and_remove() {
let db = testdb!();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=rower&password=rower"); // Add the form data to the request body;
login.dispatch().await;
let req = client.get("/planned/join/1");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
let req = client.get("/planned/remove/1");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
}
#[sqlx::test]
fn test_join_invalid_event() {
let db = testdb!();
let rocket = rocket::build().manage(db.clone());
let rocket = crate::tera::config(rocket);
let client = Client::tracked(rocket).await.unwrap();
let login = client
.post("/auth")
.header(ContentType::Form) // Set the content type to form
.body("name=rower&password=rower"); // Add the form data to the request body;
login.dispatch().await;
let req = client.get("/planned/join/9999");
let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(response.headers().get("Location").next(), Some("/"));
let flash_cookie = response
.cookies()
.get("_flash")
.expect("Expected flash cookie");
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
}
}

View File

@ -3,55 +3,55 @@ use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool;
use crate::model::{
stat::{self, Stat},
user::NonGuestUser,
stat::{self, BoatStat, Stat},
user::{DonauLinzUser, UserWithDetails},
};
use super::log::KioskCookie;
#[get("/boats?<year>", rank = 2)]
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
let stat = Stat::boats(db, year).await;
#[get("/boats", rank = 2)]
async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
let stat = BoatStat::get(db).await;
let kiosk = false;
Template::render(
"stat.boats",
context!(loggedin_user: &user.user, stat, kiosk),
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, kiosk),
)
}
#[get("/boats?<year>")]
async fn index_boat_kiosk(
db: &State<SqlitePool>,
_kiosk: KioskCookie,
year: Option<i32>,
) -> Template {
let stat = Stat::boats(db, year).await;
#[get("/boats")]
async fn index_boat_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
let stat = BoatStat::get(db).await;
let kiosk = true;
Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true))
}
#[get("/?<year>", rank = 2)]
async fn index(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
let stat = Stat::people(db, year).await;
let personal = stat::get_personal(db, &user.user).await;
let club_km = Stat::sum_people(db, year).await;
let guest_km = Stat::guest(db, year).await;
let personal = stat::get_personal(db, &user).await;
let kiosk = false;
Template::render(
"stat.people",
context!(loggedin_user: &user.user, stat, personal, kiosk),
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
)
}
#[get("/?<year>")]
async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie, year: Option<i32>) -> Template {
let stat = Stat::people(db, year).await;
let club_km = Stat::sum_people(db, year).await;
let guest_km = Stat::guest(db, year).await;
let kiosk = true;
Template::render(
"stat.people",
context!(stat, kiosk, show_kiosk_header: true),
context!(stat, kiosk, show_kiosk_header: true, guest_km, club_km),
)
}

View File

@ -0,0 +1,211 @@
use chrono::NaiveDate;
use rocket::{
form::Form,
get, post,
request::FlashMessage,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use rocket_dyn_templates::Template;
use sqlx::SqlitePool;
use tera::Context;
use crate::{
model::{
log::Log,
trailer::Trailer,
trailerreservation::{TrailerReservation, TrailerReservationToAdd},
user::{DonauLinzUser, User, UserWithDetails},
},
tera::log::KioskCookie,
};
#[get("/")]
async fn index_kiosk(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
_kiosk: KioskCookie,
) -> Template {
let trailerreservations = TrailerReservation::all_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("trailerreservations", &trailerreservations);
context.insert("trailers", &Trailer::all(db).await);
context.insert("user", &User::all(db).await);
context.insert("show_kiosk_header", &true);
Template::render("trailerreservations", context.into_json())
}
#[get("/", rank = 2)]
async fn index(
db: &State<SqlitePool>,
flash: Option<FlashMessage<'_>>,
user: DonauLinzUser,
) -> Template {
let trailerreservations = TrailerReservation::all_future(db).await;
let mut context = Context::new();
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert("trailerreservations", &trailerreservations);
context.insert("trailers", &Trailer::all(db).await);
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("trailerreservations", context.into_json())
}
#[derive(Debug, FromForm)]
pub struct FormTrailerReservationToAdd<'r> {
pub trailer_id: i64,
pub start_date: &'r str,
pub end_date: &'r str,
pub time_desc: &'r str,
pub usage: &'r str,
pub user_id_applicant: Option<i64>,
}
#[post("/new", data = "<data>", rank = 2)]
async fn create<'r>(
db: &State<SqlitePool>,
data: Form<FormTrailerReservationToAdd<'r>>,
user: DonauLinzUser,
) -> Flash<Redirect> {
let user_applicant: User = user.into();
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
.await
.unwrap();
let trailerreservation_to_add = TrailerReservationToAdd {
trailer: &trailer,
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
time_desc: data.time_desc,
usage: data.usage,
user_applicant: &user_applicant,
};
match TrailerReservation::create(db, trailerreservation_to_add).await {
Ok(_) => Flash::success(
Redirect::to("/trailerreservation"),
"Reservierung erfolgreich hinzugefügt",
),
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
}
}
#[post("/new", data = "<data>")]
async fn create_from_kiosk<'r>(
db: &State<SqlitePool>,
data: Form<FormTrailerReservationToAdd<'r>>,
_kiosk: KioskCookie,
) -> Flash<Redirect> {
let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
.await
.unwrap();
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
.await
.unwrap();
let trailerreservation_to_add = TrailerReservationToAdd {
trailer: &trailer,
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
time_desc: data.time_desc,
usage: data.usage,
user_applicant: &user_applicant,
};
match TrailerReservation::create(db, trailerreservation_to_add).await {
Ok(_) => Flash::success(
Redirect::to("/trailerreservation"),
"Reservierung erfolgreich hinzugefügt",
),
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
}
}
#[derive(FromForm, Debug)]
pub struct ReservationEditForm {
pub(crate) id: i32,
pub(crate) time_desc: String,
pub(crate) usage: String,
}
#[post("/", data = "<data>")]
async fn update(
db: &State<SqlitePool>,
data: Form<ReservationEditForm>,
user: User,
) -> Flash<Redirect> {
let Some(reservation) = TrailerReservation::find_by_id(db, data.id).await else {
return Flash::error(
Redirect::to("/trailerreservation"),
format!("Reservation with ID {} does not exist!", data.id),
);
};
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
return Flash::error(
Redirect::to("/trailerreservation"),
"Not allowed to update reservation (only admins + creator do so).".to_string(),
);
}
Log::create(
db,
format!(
"{} updated reservation from {reservation:?} to {data:?}",
user.name
),
)
.await;
reservation.update(db, data.into_inner()).await;
Flash::success(
Redirect::to("/trailerreservation"),
"Reservierung erfolgreich bearbeitet",
)
}
#[get("/<reservation_id>/delete")]
async fn delete<'r>(
db: &State<SqlitePool>,
reservation_id: i32,
user: DonauLinzUser,
) -> Flash<Redirect> {
let reservation = TrailerReservation::find_by_id(db, reservation_id)
.await
.unwrap();
if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
reservation.delete(db).await;
Flash::success(
Redirect::to("/trailerreservation"),
"Reservierung erfolgreich gelöscht",
)
} else {
Flash::error(
Redirect::to("/trailerreservation"),
"Nur der Reservierer darf die Reservierung löschen.".to_string(),
)
}
}
pub fn routes() -> Vec<Route> {
routes![
index,
index_kiosk,
create,
create_from_kiosk,
delete,
update
]
}

View File

@ -0,0 +1,5 @@
-- test user
INSERT INTO user(name) VALUES('Marie');
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz'));
INSERT INTO user(name) VALUES('Philipp');
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz'));

1
stats/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
tmp/

6
stats/s.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
rm -rf tmp
mkdir tmp
scp root@128.140.64.118:"/var/log/nginx/access.log*" ./tmp/
zcat -f ./tmp/access.log* | goaccess --log-format=COMBINED

1
svelte/.gitignore vendored
View File

@ -1,6 +1,5 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env

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