feat: redesign availability input
This commit is contained in:
parent
8b61bb37a8
commit
a6b12d9bf2
@ -1,12 +1,10 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use chrono::NaiveDate;
|
use chrono::{Days, NaiveDate, NaiveTime};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::endpoints::availability::NewOrEditAvailabilityTemplate;
|
use crate::endpoints::availability::NewOrEditAvailabilityTemplate;
|
||||||
use crate::models::{
|
use crate::models::{find_free_date_time_slots, Availability, User};
|
||||||
find_free_date_time_slots, Availability, User,
|
|
||||||
};
|
|
||||||
use crate::utils::{ApplicationError, TemplateResponse};
|
use crate::utils::{ApplicationError, TemplateResponse};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -24,8 +22,8 @@ pub async fn get(
|
|||||||
Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
|
Availability::read_by_user_and_date(pool.get_ref(), user.id, &query.date).await?;
|
||||||
let slot_suggestions = find_free_date_time_slots(&availabilities_from_user);
|
let slot_suggestions = find_free_date_time_slots(&availabilities_from_user);
|
||||||
|
|
||||||
let user_can_create_availabillity = availabilities_from_user.is_empty()
|
let user_can_create_availabillity =
|
||||||
|| !slot_suggestions.is_empty();
|
availabilities_from_user.is_empty() || !slot_suggestions.is_empty();
|
||||||
|
|
||||||
if !user_can_create_availabillity {
|
if !user_can_create_availabillity {
|
||||||
return Ok(HttpResponse::BadRequest().finish());
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
@ -34,11 +32,13 @@ pub async fn get(
|
|||||||
let template = NewOrEditAvailabilityTemplate {
|
let template = NewOrEditAvailabilityTemplate {
|
||||||
user: user.into_inner(),
|
user: user.into_inner(),
|
||||||
date: query.date,
|
date: query.date,
|
||||||
|
enddate: None,
|
||||||
id: None,
|
id: None,
|
||||||
start: None,
|
start: Some(NaiveTime::from_hms_opt(10, 0, 0).unwrap()),
|
||||||
end: None,
|
end: Some(NaiveTime::from_hms_opt(20, 0, 0).unwrap()),
|
||||||
comment: None,
|
comment: None,
|
||||||
slot_suggestions,
|
slot_suggestions,
|
||||||
|
datetomorrow: query.date.checked_add_days(Days::new(1)).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(template.to_response()?)
|
Ok(template.to_response()?)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use chrono::Days;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -33,11 +34,17 @@ pub async fn get(
|
|||||||
let template = NewOrEditAvailabilityTemplate {
|
let template = NewOrEditAvailabilityTemplate {
|
||||||
user: user.into_inner(),
|
user: user.into_inner(),
|
||||||
date: availability.start.date(),
|
date: availability.start.date(),
|
||||||
|
enddate: Some(availability.end.date()),
|
||||||
id: Some(path.id),
|
id: Some(path.id),
|
||||||
start: Some(availability.start.time()),
|
start: Some(availability.start.time()),
|
||||||
end: Some(availability.end),
|
end: Some(availability.end.time()),
|
||||||
comment: availability.comment.as_deref(),
|
comment: availability.comment.as_deref(),
|
||||||
slot_suggestions
|
slot_suggestions,
|
||||||
|
datetomorrow: availability
|
||||||
|
.start
|
||||||
|
.date()
|
||||||
|
.checked_add_days(Days::new(1))
|
||||||
|
.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(template.to_response()?)
|
Ok(template.to_response()?)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
use crate::filters;
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use chrono::{Days, NaiveDate, NaiveDateTime, NaiveTime};
|
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::models::{Availability, AvailabilityChangeset, Role, User};
|
use crate::models::{Availability, AvailabilityChangeset, Role, User};
|
||||||
|
|
||||||
@ -15,11 +17,22 @@ pub mod post_update;
|
|||||||
struct NewOrEditAvailabilityTemplate<'a> {
|
struct NewOrEditAvailabilityTemplate<'a> {
|
||||||
user: User,
|
user: User,
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
|
enddate: Option<NaiveDate>,
|
||||||
id: Option<i32>,
|
id: Option<i32>,
|
||||||
start: Option<NaiveTime>,
|
start: Option<NaiveTime>,
|
||||||
end: Option<NaiveDateTime>,
|
end: Option<NaiveTime>,
|
||||||
comment: Option<&'a str>,
|
comment: Option<&'a str>,
|
||||||
slot_suggestions: Vec<(NaiveDateTime, NaiveDateTime)>,
|
slot_suggestions: Vec<(NaiveDateTime, NaiveDateTime)>,
|
||||||
|
datetomorrow: NaiveDate
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct AvailabillityForm {
|
||||||
|
pub startdate: NaiveDate,
|
||||||
|
pub enddate: NaiveDate,
|
||||||
|
pub starttime: NaiveTime,
|
||||||
|
pub endtime: NaiveTime,
|
||||||
|
pub comment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_adjacend_availability<'a>(
|
fn find_adjacend_availability<'a>(
|
||||||
|
@ -1,23 +1,13 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
|
|
||||||
use garde::Validate;
|
use garde::Validate;
|
||||||
use serde::Deserialize;
|
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::availability::find_adjacend_availability,
|
endpoints::availability::{find_adjacend_availability, AvailabillityForm},
|
||||||
models::{Availability, AvailabilityChangeset, AvailabilityContext, User},
|
models::{Availability, AvailabilityChangeset, AvailabilityContext, User},
|
||||||
utils::{self, ApplicationError},
|
utils::{self, ApplicationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct AvailabillityForm {
|
|
||||||
pub date: NaiveDate,
|
|
||||||
pub from: NaiveTime,
|
|
||||||
pub till: NaiveDateTime,
|
|
||||||
pub comment: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::post("/availabillity/new")]
|
#[actix_web::post("/availabillity/new")]
|
||||||
pub async fn post(
|
pub async fn post(
|
||||||
user: web::ReqData<User>,
|
user: web::ReqData<User>,
|
||||||
@ -25,13 +15,16 @@ pub async fn post(
|
|||||||
form: web::Form<AvailabillityForm>,
|
form: web::Form<AvailabillityForm>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
let existing_availabilities =
|
let existing_availabilities =
|
||||||
Availability::read_by_user_and_date(pool.get_ref(), user.id, &form.date).await?;
|
Availability::read_by_user_and_date(pool.get_ref(), user.id, &form.startdate).await?;
|
||||||
let context = AvailabilityContext {
|
let context = AvailabilityContext {
|
||||||
existing_availabilities: existing_availabilities.clone(),
|
existing_availabilities: existing_availabilities.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let start = form.startdate.and_time(form.starttime);
|
||||||
|
let end = form.enddate.and_time(form.endtime);
|
||||||
|
|
||||||
let mut changeset = AvailabilityChangeset {
|
let mut changeset = AvailabilityChangeset {
|
||||||
time: (form.date.and_time(form.from), form.till),
|
time: (start, end),
|
||||||
comment: form.comment.clone(),
|
comment: form.comment.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,7 +48,7 @@ pub async fn post(
|
|||||||
Availability::create(pool.get_ref(), user.id, changeset).await?;
|
Availability::create(pool.get_ref(), user.id, changeset).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = utils::get_return_url_for_date(&form.date);
|
let url = utils::get_return_url_for_date(&form.startdate);
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, url.clone()))
|
.insert_header((LOCATION, url.clone()))
|
||||||
.insert_header(("HX-LOCATION", url))
|
.insert_header(("HX-LOCATION", url))
|
||||||
|
@ -4,7 +4,7 @@ use sqlx::PgPool;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::{
|
endpoints::{
|
||||||
availability::{find_adjacend_availability, post_new::AvailabillityForm},
|
availability::{find_adjacend_availability, AvailabillityForm},
|
||||||
IdPath,
|
IdPath,
|
||||||
},
|
},
|
||||||
models::{Availability, AvailabilityChangeset, AvailabilityContext, User},
|
models::{Availability, AvailabilityChangeset, AvailabilityContext, User},
|
||||||
@ -37,8 +37,11 @@ pub async fn post(
|
|||||||
existing_availabilities: existing_availabilities.clone(),
|
existing_availabilities: existing_availabilities.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let start = form.startdate.and_time(form.starttime);
|
||||||
|
let end = form.enddate.and_time(form.endtime);
|
||||||
|
|
||||||
let mut changeset = AvailabilityChangeset {
|
let mut changeset = AvailabilityChangeset {
|
||||||
time: (form.date.and_time(form.from), form.till),
|
time: (start, end),
|
||||||
comment: form.comment.clone(),
|
comment: form.comment.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +68,7 @@ pub async fn post(
|
|||||||
Availability::update(pool.get_ref(), availability.id, changeset).await?;
|
Availability::update(pool.get_ref(), availability.id, changeset).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = utils::get_return_url_for_date(&form.date);
|
let url = utils::get_return_url_for_date(&form.startdate);
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, url.clone()))
|
.insert_header((LOCATION, url.clone()))
|
||||||
.insert_header(("HX-LOCATION", url))
|
.insert_header(("HX-LOCATION", url))
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use chrono::{NaiveDate, NaiveDateTime};
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||||
use maud::html;
|
use maud::html;
|
||||||
|
|
||||||
use crate::models::UserFunction;
|
use crate::models::UserFunction;
|
||||||
@ -21,7 +23,10 @@ pub fn cond_show(show: &bool, text: &str) -> askama::Result<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_value(option: &Option<String>) -> askama::Result<String> {
|
pub fn insert_value<T>(option: &Option<T>) -> askama::Result<String>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{
|
||||||
if let Some(val) = option {
|
if let Some(val) = option {
|
||||||
let s = format!(r#"value="{val}""#);
|
let s = format!(r#"value="{val}""#);
|
||||||
return Ok(s);
|
return Ok(s);
|
||||||
@ -30,6 +35,15 @@ pub fn insert_value(option: &Option<String>) -> askama::Result<String> {
|
|||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_time_value(option: &Option<NaiveTime>) -> askama::Result<String> {
|
||||||
|
if let Some(val) = option {
|
||||||
|
let s = val.format(r#"value="%H:%M""#).to_string();
|
||||||
|
return Ok(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(String::new())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_some_and_eq<T>(option: &Option<T>, other: &T) -> askama::Result<bool>
|
pub fn is_some_and_eq<T>(option: &Option<T>, other: &T) -> askama::Result<bool>
|
||||||
where
|
where
|
||||||
T: Eq,
|
T: Eq,
|
||||||
@ -71,6 +85,22 @@ pub fn date_d(v: &NaiveDate) -> askama::Result<String> {
|
|||||||
Ok(v.format("%d.%m.%Y").to_string())
|
Ok(v.format("%d.%m.%Y").to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn date_c(v: &NaiveDate) -> askama::Result<String> {
|
||||||
|
Ok(v.format("%d.%m").to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time(v: &NaiveTime) -> askama::Result<String> {
|
||||||
|
Ok(v.format("%H:%M").to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_opt(v: &Option<NaiveTime>, default: &str) -> askama::Result<String> {
|
||||||
|
if let Some(t) = v {
|
||||||
|
return time(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(default.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dt_t(v: &NaiveDateTime) -> askama::Result<String> {
|
pub fn dt_t(v: &NaiveDateTime) -> askama::Result<String> {
|
||||||
Ok(v.format("%R").to_string())
|
Ok(v.format("%R").to_string())
|
||||||
}
|
}
|
||||||
|
@ -3,87 +3,109 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% if id.is_some() %}
|
{% let is_edit = id.is_some() %}
|
||||||
<form method="post" action="/availabillity/edit/{{ id.unwrap() }}">
|
<form method="post" action="/availabillity/{% if is_edit %}edit/{{ id.unwrap() }}{% else %}new{% endif %}">
|
||||||
<h1 class="title">Bearbeite Vefügbarkeit für den {{ date.format("%d.%m.%Y") }}</h1>
|
<h1 class="title">{% if is_edit %}Bearbeite{% else %}Neue{% endif %} Vefügbarkeit für den {{ date|date_d }}</h1>
|
||||||
{% else %}
|
|
||||||
<form method="post" action="/availabillity/new">
|
|
||||||
<h1 class="title">Neue Vefügbarkeit für den {{ date.format("%d.%m.%Y") }}</h1>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<input type="hidden" name="date" value="{{ date }}">
|
<input type="hidden" name="startdate" value="{{ date }}">
|
||||||
{% let time = "%R" %}
|
<input type="hidden" name="enddate" value="{{ date }}" id="enddate">
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
<label class="label">Dauer Von - Bis</label>
|
<label class="label">Dauer Von - Bis</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input class="input" type="time" id="from" name="from" required {% if let Some(start)=start
|
<input class="input" type="time" name="starttime" required {{ start|insert_time_value|safe }}
|
||||||
%}value="{{start}}" {% endif %}>
|
_="on change put the value of me into #st">
|
||||||
{% if slot_suggestions.len() > 0 %}
|
{% if slot_suggestions.len() > 0 %}
|
||||||
<p class="help">noch mögliche Zeiträume:</p>
|
<p class="help">noch mögliche Zeiträume:</p>
|
||||||
<div class="tags help">
|
<div class="tags help">
|
||||||
{% for (s, e) in slot_suggestions %}
|
{% for (s, e) in slot_suggestions %}
|
||||||
<span class="tag">{{ s.format(time) }} - {{ e.format(time) }}</span>
|
<span class="tag">{{ *s|dt_t }} - {{ *e|dt_t }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
{% endif %}
|
||||||
<input class="input" type="datetime-local" id="till" name="till" required min="{{ date }}T00:00"
|
</div>
|
||||||
max="{{ date.checked_add_days(Days::new(1)).unwrap() }}T23:59" {% if let Some(end)=end %}value="{{end}}"
|
<div class="field">
|
||||||
{% endif %}>
|
<input class="input" type="time" name="endtime" required {{ end|insert_time_value|safe }}
|
||||||
|
_='on change put the value of me into #et then if (value of the previous <input/>) is greater than (value of me) then set the value of #enddate to "{{ datetomorrow }}" then add @disabled to #nextbtn then remove @disabled from #samebtn then put "{{ datetomorrow|date_c }}" into #ed end' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">Verfügbarkeitsende</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-narrow">
|
||||||
|
{% let is_overnight = enddate.is_some() && enddate.as_ref().unwrap() == datetomorrow|ref %}
|
||||||
|
<button id="samebtn" class="button is-small is-success is-light" type="button"
|
||||||
|
{{ is_overnight|invert|ref|cond_show("disabled")|safe }}
|
||||||
|
_='on click set the value of #enddate to "{{ date }}" then toggle @disabled on me then toggle @disabled on #nextbtn then put "{{ date|date_c }}" into #ed'>am
|
||||||
|
selben Tag</button>
|
||||||
|
</div>
|
||||||
|
<div class="field is-narrow">
|
||||||
|
<button id="nextbtn" class="button is-small is-info is-light" type="button"
|
||||||
|
{{ is_overnight|cond_show("disabled")|safe }}
|
||||||
|
_='on click set the value of #enddate to "{{ datetomorrow }}" then toggle @disabled on me then toggle @disabled on #samebtn then put "{{ datetomorrow|date_c }}" into #ed'>am
|
||||||
|
Tag darauf</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">Kommentar</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<textarea class="textarea" name="comment" placeholder="nur Posten, nur Wachhabender, etc..">{{
|
||||||
|
comment.unwrap_or("") }}</textarea>
|
||||||
|
</div>
|
||||||
|
<p class="help is-info">
|
||||||
|
verfügbar von {{ date|date_c }} <span id="st">{{ start|time_opt("10:00")|safe }}</span> Uhr
|
||||||
|
bis <span id="ed">{{ enddate.as_ref().unwrap_or(date)|date_c }}</span>
|
||||||
|
<span id="et">{{ end|time_opt("20:00")|safe }}</span> Uhr
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal">
|
||||||
|
<div class="field-label"></div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-success">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#check-circle" />
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{% if id.is_some() %}
|
||||||
|
Speichern
|
||||||
|
{% else %}
|
||||||
|
Erstellen
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<a class="button is-link is-light" hx-boost="true" href="/?date={{ date }}">
|
||||||
|
<svg class="icon">
|
||||||
|
<use href="/static/feather-sprite.svg#arrow-left" />
|
||||||
|
</svg>
|
||||||
|
<span>Zurück</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
</form>
|
||||||
<div class="field-label">
|
|
||||||
<label class="label">Kommentar</label>
|
|
||||||
</div>
|
|
||||||
<div class="field-body">
|
|
||||||
<div class="field">
|
|
||||||
<div class="control">
|
|
||||||
<textarea class="textarea" name="comment" placeholder="nur Posten, nur Wachhabender, etc..">{{
|
|
||||||
comment.unwrap_or("") }}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-horizontal">
|
|
||||||
<div class="field-label"></div>
|
|
||||||
<div class="field-body">
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<button class="button is-success">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#check-circle" />
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
{% if id.is_some() %}
|
|
||||||
Speichern
|
|
||||||
{% else %}
|
|
||||||
Erstellen
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<a class="button is-link is-light" hx-boost="true" href="/?date={{ date }}">
|
|
||||||
<svg class="icon">
|
|
||||||
<use href="/static/feather-sprite.svg#arrow-left" />
|
|
||||||
</svg>
|
|
||||||
<span>Zurück</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user