181 lines
4.5 KiB
Rust
181 lines
4.5 KiB
Rust
use crate::ctx;
|
|
use crate::login_or_redirect;
|
|
use crate::Error;
|
|
use axum::extract::Extension;
|
|
use axum::extract::Form;
|
|
use axum::extract::OriginalUri;
|
|
use axum::extract::Path;
|
|
use axum::http::StatusCode;
|
|
use axum::response::Html;
|
|
use axum::response::Redirect;
|
|
use axum_sessions::extractors::ReadableSession;
|
|
use num_enum::FromPrimitive;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use sqlx::FromRow;
|
|
use sqlx::{Pool, Postgres};
|
|
use std::sync::Arc;
|
|
use tera::Tera;
|
|
|
|
#[derive(FromRow, Serialize, Deserialize)]
|
|
pub struct RawTask {
|
|
pub id: i32,
|
|
pub title: String,
|
|
pub description: String,
|
|
pub status: i32,
|
|
}
|
|
|
|
#[derive(FromPrimitive, Serialize)]
|
|
#[repr(i32)]
|
|
pub enum Status {
|
|
#[default]
|
|
#[serde(rename = "To Do")]
|
|
ToDo,
|
|
#[serde(rename = "In Progress")]
|
|
InProgress,
|
|
Done,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct Task {
|
|
pub id: i32,
|
|
pub title: String,
|
|
pub description: String,
|
|
pub status: Status,
|
|
}
|
|
|
|
impl From<RawTask> for Task {
|
|
fn from(t: RawTask) -> Self {
|
|
Self {
|
|
id: t.id,
|
|
title: t.title,
|
|
description: t.description,
|
|
status: Status::from(t.status),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn update_form(
|
|
Path(id): Path<i32>,
|
|
Extension(tera): Extension<Arc<Tera>>,
|
|
Extension(pool): Extension<Arc<Pool<Postgres>>>,
|
|
session: ReadableSession,
|
|
OriginalUri(uri): OriginalUri,
|
|
) -> Result<Html<String>, Error> {
|
|
let username = login_or_redirect!(session, "/login");
|
|
|
|
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
|
|
.bind(&username)
|
|
.fetch_one(&*pool)
|
|
.await?;
|
|
|
|
let task: Option<RawTask> =
|
|
sqlx::query_as("select id,title,description,status from tasks where id=$1 and owner=$2")
|
|
.bind(id)
|
|
.bind(user_id)
|
|
.fetch_optional(&*pool)
|
|
.await?;
|
|
|
|
if let Some(task) = task {
|
|
let c = ctx! {
|
|
"task" => task,
|
|
"url" => uri.to_string()
|
|
};
|
|
|
|
let rendered = tera.render("tasks/update.html", &c)?;
|
|
|
|
#[allow(clippy::needless_return)] // it's a compile error, clippy
|
|
return Ok(Html(rendered));
|
|
}
|
|
|
|
Err(StatusCode::NOT_FOUND.into())
|
|
}
|
|
|
|
pub async fn create_form(
|
|
Extension(tera): Extension<Arc<Tera>>,
|
|
OriginalUri(uri): OriginalUri,
|
|
session: ReadableSession,
|
|
) -> Result<Html<String>, Error> {
|
|
let _username = login_or_redirect!(session, "/login");
|
|
|
|
let c = ctx! {
|
|
"url" => uri.to_string()
|
|
};
|
|
|
|
let rendered = tera.render("tasks/create.html", &c)?;
|
|
|
|
Ok(Html(rendered))
|
|
}
|
|
|
|
pub async fn update_backend(
|
|
Path(id): Path<i32>,
|
|
Extension(pool): Extension<Arc<Pool<Postgres>>>,
|
|
Form(data): Form<RawTask>,
|
|
) -> Result<Redirect, Error> {
|
|
sqlx::query("update tasks set title=$1,description=$2,status=$3 where id=$4")
|
|
.bind(&data.title)
|
|
.bind(&data.description)
|
|
.bind(data.status)
|
|
.bind(id)
|
|
.execute(&*pool)
|
|
.await?;
|
|
|
|
Ok(Redirect::to("/"))
|
|
}
|
|
|
|
pub async fn create_backend(
|
|
Extension(pool): Extension<Arc<Pool<Postgres>>>,
|
|
Form(data): Form<RawTask>,
|
|
session: ReadableSession,
|
|
) -> Result<Redirect, Error> {
|
|
let username = login_or_redirect!(session, "/login");
|
|
|
|
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
|
|
.bind(&username)
|
|
.fetch_one(&*pool)
|
|
.await?;
|
|
|
|
sqlx::query("insert into tasks (owner,title, description, status) values ($1, $2, $3, $4)")
|
|
.bind(user_id)
|
|
.bind(&data.title)
|
|
.bind(&data.description)
|
|
.bind(data.status)
|
|
.execute(&*pool)
|
|
.await?;
|
|
|
|
Ok(Redirect::to("/"))
|
|
}
|
|
|
|
pub async fn task_detail(
|
|
Path(id): Path<i32>,
|
|
Extension(tera): Extension<Arc<Tera>>,
|
|
Extension(pool): Extension<Arc<Pool<Postgres>>>,
|
|
session: ReadableSession,
|
|
) -> Result<Html<String>, Error> {
|
|
let username = login_or_redirect!(session, "/login");
|
|
|
|
let (user_id,): (i32,) = sqlx::query_as("select id from users where username=$1")
|
|
.bind(&username)
|
|
.fetch_one(&*pool)
|
|
.await?;
|
|
|
|
let task: Option<RawTask> =
|
|
sqlx::query_as("select id,title,description,status from tasks where id=$1 and owner=$2")
|
|
.bind(id)
|
|
.bind(user_id)
|
|
.fetch_optional(&*pool)
|
|
.await?;
|
|
|
|
if let Some(task) = task.map(Task::from) {
|
|
let c = ctx! {
|
|
"task" => task
|
|
};
|
|
|
|
let rendered = tera.render("tasks/detail.html", &c)?;
|
|
|
|
return Ok(Html(rendered));
|
|
}
|
|
|
|
Err(StatusCode::NOT_FOUND.into())
|
|
}
|