Compare commits

..

2 Commits

14 changed files with 227 additions and 22 deletions

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n event.id AS eventId,\n event.startTimestamp,\n event.endTimestamp,\n event.name,\n event.locationId,\n event.voluntaryWachhabender,\n event.voluntaryFuehrungsassistent,\n event.amountOfPosten,\n event.clothing,\n event.canceled,\n event.note,\n location.id,\n location.name AS locationName,\n location.areaId AS locationAreaId,\n clothing.id AS clothingId,\n clothing.name AS clothingName\n FROM event\n JOIN location ON event.locationId = location.id\n JOIN clothing ON event.clothing = clothing.id\n WHERE starttimestamp::date >= $1\n AND starttimestamp::date <= $2\n AND location.areaId = $3;\n ",
"query": "\n SELECT\n event.id AS eventId,\n event.startTimestamp,\n event.endTimestamp,\n event.name,\n event.locationId,\n event.voluntaryWachhabender,\n event.voluntaryFuehrungsassistent,\n event.amountOfPosten,\n event.clothing,\n event.canceled,\n event.note,\n location.id,\n location.name AS locationName,\n location.areaId AS locationAreaId,\n clothing.id AS clothingId,\n clothing.name AS clothingName\n FROM event\n JOIN location ON event.locationId = location.id\n JOIN clothing ON event.clothing = clothing.id\n WHERE starttimestamp::date >= $1\n AND starttimestamp::date <= $2\n AND location.areaId = $3\n ORDER BY event.starttimestamp;\n ",
"describe": {
"columns": [
{
@ -110,5 +110,5 @@
false
]
},
"hash": "10b4b80f351b66ac5e778a3031288ac5dc66efd0a66b38b7e30f4c954df91bdf"
"hash": "65367483e39e07cd0aa142f9bb76c7a5d6dd0611e6b41edd5a593c9f955b5d04"
}

View File

@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\n FROM availability\n WHERE availability.userId = $1\n AND availability.starttimestamp::date >= $2\n AND availability.endtimestamp::date <= $3;\n ",
"query": "\n SELECT\n availability.id,\n availability.userId,\n availability.startTimestamp,\n availability.endTimestamp,\n availability.comment\n FROM availability\n WHERE availability.userId = $1\n AND availability.starttimestamp::date >= $2\n AND availability.endtimestamp::date <= $3\n ORDER BY availability.starttimestamp;\n ",
"describe": {
"columns": [
{
@ -44,5 +44,5 @@
true
]
},
"hash": "f60053118df6a791d31fa258ee3737881f8f97ca41cbebd92eb22c967292d2ee"
"hash": "66638de4a321ba610206a9f5c237cb17eb5221c1dbe4d4c5e83167b2d2807db4"
}

View File

@ -0,0 +1,59 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n assignment.eventId,\n assignment.availabilityId,\n assignment.function AS \"function: Function\",\n assignment.startTimestamp,\n assignment.endTimestamp\n FROM assignment\n JOIN availability ON assignment.availabilityId = availability.id\n WHERE assignment.starttimestamp::date >= $1\n AND assignment.starttimestamp::date <= $2\n AND availability.userId = $3\n ORDER BY assignment.starttimestamp;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "eventid",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "availabilityid",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "function: Function",
"type_info": {
"Custom": {
"name": "function",
"kind": {
"Enum": [
"posten",
"fuehrungsassistent",
"wachhabender"
]
}
}
}
},
{
"ordinal": 3,
"name": "starttimestamp",
"type_info": "Timestamptz"
},
{
"ordinal": 4,
"name": "endtimestamp",
"type_info": "Timestamptz"
}
],
"parameters": {
"Left": [
"Date",
"Date",
"Int4"
]
},
"nullable": [
false,
false,
false,
false,
false
]
},
"hash": "6788747f70812d6a87833d821c9845f59b4338bc42a40050a68736ba30d937d5"
}

View File

@ -1,4 +1,4 @@
use chrono::NaiveDateTime;
use chrono::{NaiveDate, NaiveDateTime};
use sqlx::{PgPool, query};
use super::{AssignmentChangeset, Function, Result};
@ -101,6 +101,47 @@ impl Assignment {
Ok(assignemnets)
}
pub async fn read_all_by_daterange_and_user(
pool: &PgPool,
date_range: (&NaiveDate, &NaiveDate),
user_id: i32,
) -> Result<Vec<Assignment>> {
let records = query!(
r##"
SELECT
assignment.eventId,
assignment.availabilityId,
assignment.function AS "function: Function",
assignment.startTimestamp,
assignment.endTimestamp
FROM assignment
JOIN availability ON assignment.availabilityId = availability.id
WHERE assignment.starttimestamp::date >= $1
AND assignment.starttimestamp::date <= $2
AND availability.userId = $3
ORDER BY assignment.starttimestamp;
"##,
date_range.0,
date_range.1,
user_id
)
.fetch_all(pool)
.await?;
let assignemnets = records
.iter()
.map(|r| Assignment {
event_id: r.eventid,
availability_id: r.availabilityid,
function: r.function,
start: r.starttimestamp.naive_utc(),
end: r.endtimestamp.naive_utc(),
})
.collect();
Ok(assignemnets)
}
pub async fn read(
pool: &PgPool,
event_id: i32,

View File

@ -357,7 +357,8 @@ impl Availability {
FROM availability
WHERE availability.userId = $1
AND availability.starttimestamp::date >= $2
AND availability.endtimestamp::date <= $3;
AND availability.endtimestamp::date <= $3
ORDER BY availability.starttimestamp;
"##,
user_id,
date_range.0,

View File

@ -124,7 +124,8 @@ impl Event {
JOIN clothing ON event.clothing = clothing.id
WHERE starttimestamp::date >= $1
AND starttimestamp::date <= $2
AND location.areaId = $3;
AND location.areaId = $3
ORDER BY event.starttimestamp;
"#,
date_range.0,
date_range.1,

View File

@ -7,11 +7,11 @@ use crate::{
filters,
utils::{
ApplicationError,
DateTimeFormat::{DayMonthYearHourMinute, HourMinute, WeekdayDayMonthYear},
DateTimeFormat::{HourMinute, WeekdayDayMonthYearHourMinute},
TemplateResponse,
},
};
use brass_db::models::{Assignment, Availability, Event, Function, Role, User};
use brass_db::models::{Assignment, Availability, Event, Role, User};
#[derive(Template)]
#[template(path = "overview.html")]
@ -19,6 +19,7 @@ struct OverviewTemplate {
user: User,
events: Vec<Event>,
availabilities: Vec<Availability>,
assignments: Vec<(Assignment, String)>,
}
#[actix_web::get("/")]
@ -28,22 +29,36 @@ async fn get(
) -> Result<impl Responder, ApplicationError> {
let today = Utc::now().date_naive();
let next_month = today.checked_add_months(Months::new(1)).unwrap();
let date_range = (&today, &next_month);
let events = Event::read_all_by_daterange_and_area_including_location(
pool.get_ref(),
(&today, &next_month),
date_range,
user.area_id,
)
.await?;
let availabilities =
Availability::read_by_user_and_daterange(pool.get_ref(), user.id, (&today, &next_month))
.await?;
Availability::read_by_user_and_daterange(pool.get_ref(), user.id, date_range).await?;
let assignments =
Assignment::read_all_by_daterange_and_user(pool.get_ref(), date_range, user.id)
.await?
.into_iter()
.map(|a| {
if let Some(e) = events.iter().find(|e| e.id == a.event_id) {
(a, e.name.clone())
} else {
(a, String::new())
}
})
.collect();
let template = OverviewTemplate {
user: user.into_inner(),
events,
availabilities,
assignments,
};
Ok(template.to_response()?)

View File

@ -8,6 +8,7 @@ pub enum DateTimeFormat {
HourMinute,
WeekdayDayMonth,
WeekdayDayMonthYear,
WeekdayDayMonthYearHourMinute,
}
impl From<DateTimeFormat> for &'static str {
@ -21,6 +22,7 @@ impl From<DateTimeFormat> for &'static str {
DateTimeFormat::HourMinute => "%H:%M",
DateTimeFormat::WeekdayDayMonth => "%A, %d.%m.",
DateTimeFormat::WeekdayDayMonthYear => "%A, %d.%m.%Y",
DateTimeFormat::WeekdayDayMonthYearHourMinute => "%A, %d.%m.%Y %H:%M",
}
}
}

View File

@ -23,5 +23,5 @@ pub fn get_return_url_for_date(date: &NaiveDate) -> String {
return String::from("/");
}
format!("/?date={}", date)
format!("/calendar?date={}", date)
}

View File

@ -111,7 +111,7 @@
</button>
</div>
<div class="control">
<a class="button is-link is-light" hx-boost="true" href="/?date={{ date }}">
<a class="button is-link is-light" hx-boost="true" href="/calendar?date={{ date }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-left" />
</svg>

View File

@ -8,7 +8,7 @@
<div class="level is-hidden-mobile">
<div class="level-left">
<a hx-boost="true" class="button is-link level-item"
href="/?date={{ date.pred() }}{{ selected_area|show_area_query(false) }}">
href="/calendar?date={{ date.pred() }}{{ selected_area|show_area_query(false) }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-left" />
</svg>
@ -37,7 +37,7 @@
<div class="level-right">
<a hx-boost="true" class="button is-link level-item"
href="/?date={{ date.succ() }}{{ selected_area|show_area_query(false) }}">
href="/calendar?date={{ date.succ() }}{{ selected_area|show_area_query(false) }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-right" />
</svg>
@ -49,7 +49,7 @@
<div class="level is-hidden-tablet is-mobile">
<div class="level-left">
<a hx-boost="true" class="button is-link level-item"
href="/?date={{ date.pred() }}{{ selected_area|show_area_query(false) }}">
href="/calendar?date={{ date.pred() }}{{ selected_area|show_area_query(false) }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-left" />
</svg>
@ -63,7 +63,7 @@
<div class="level-right">
<a hx-boost="true" class="button is-link level-item"
href="/?date={{ date.succ() }}{{ selected_area|show_area_query(false) }}">
href="/calendar?date={{ date.succ() }}{{ selected_area|show_area_query(false) }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-right" />
</svg>

View File

@ -225,7 +225,7 @@
</button>
</div>
<div class="control">
<a class="button is-link is-light" hx-boost="true" href="/?date={{ date }}">
<a class="button is-link is-light" hx-boost="true" href="/calendar?date={{ date }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-left" />
</svg>

View File

@ -64,7 +64,7 @@
</div>
<div class="control">
<a class="button is-link is-light" hx-boost="true" href="/?date={{ event.start.date() }}">
<a class="button is-link is-light" hx-boost="true" href="/calendar?date={{ event.start.date() }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#arrow-left" />
</svg>

View File

@ -1,9 +1,95 @@
{% extends "nav.html" %}
{% block content %}
<section id="progress" class="section">
<section class="section">
<div class="container">
Übersicht
<h1 class="title is-1">Übersicht</h1>
<h3 class="title is-3">geplante Veranstaltungen</h3>
<p class="subtitle is-5">in den nächsten 31 Tagen</p>
{% if events.len() == 0 %}
<div class="notification">
Keine Veranstaltungen für diesen Zeitraum geplant.
</div>
{% else %}
<div class="panel p-2">
{% for e in events %}
<div class="panel-block is-justify-content-space-between">
<span>
<b>{{ e.name }}</b> &nbsp; {{ e.start|fmt_datetime(WeekdayDayMonthYearHourMinute) }} - {{
e.end|fmt_datetime(HourMinute) }}
</span>
<a class="button is-small is-link is-light" href="/calendar?date={{ e.start.date() }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#calendar" />
</svg>
<span>im Kalender anzeigen</span>
</a>
</div>
{% endfor %}
</div>
{% endif %}
<h3 class="title is-3">Deine Verfügbarkeiten</h3>
<p class="subtitle is-5">in den nächsten 31 Tagen</p>
{% if availabilities.len() == 0 %}
<div class="notification">
Keine Verfügbarkeiten für diesen Zeitraum hinterlegt.
</div>
{% else %}
<div class="panel p-2">
{% for a in availabilities %}
<div class="panel-block is-justify-content-space-between">
<span>
{{ a.start|fmt_datetime(WeekdayDayMonthYearHourMinute) }} - {{
a.end|fmt_datetime(HourMinute) }} {% if let Some(comment) = a.comment %}&nbsp; Kommentar: &nbsp; {{ comment }}
{% endif %}
</span>
<div class="buttons are-small">
<a class="button is-primary is-light" href="/availability/edit/{{ a.id }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#edit" />
</svg>
<span>Bearbeiten</span>
</a>
<a class="button is-link is-light" href="/calendar?date={{ a.start.date() }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#calendar" />
</svg>
<span>im Kalender anzeigen</span>
</a>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<h3 class="title is-3">Deine Einteilungen</h3>
<p class="subtitle is-5">in den nächsten 31 Tagen</p>
{% if assignments.len() == 0 %}
<div class="notification">
Keine Einteilungen für diesen Zeitraum vorhanden.
</div>
{% else %}
<div class="panel p-2">
{% for (a, event_name) in assignments %}
<div class="panel-block is-justify-content-space-between">
<span>
{{ a.start|fmt_datetime(WeekdayDayMonthYearHourMinute) }} - {{
a.end|fmt_datetime(HourMinute) }} bei {{ event_name }} als {{ a.function }}
</span>
<a class="button is-small is-link is-light" href="/calendar?date={{ a.start.date() }}">
<svg class="icon">
<use href="/static/feather-sprite.svg#calendar" />
</svg>
<span>im Kalender anzeigen</span>
</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</section>
{% endblock %}