From f362fa8c932cf4e78d75110bba60e90fc550466e Mon Sep 17 00:00:00 2001 From: Chris Jean-Marie Date: Tue, 5 Nov 2024 21:35:40 +0000 Subject: [PATCH] Basic giftexchange view funtionality --- ...20241103140734_secret-gift-exchange.up.sql | 19 +- backend/src/main.rs | 4 + backend/src/secret_gift_exchange.rs | 174 +++++++++++++++++- backend/templates/giftexchange.html | 52 ++++++ backend/templates/giftexchanges.html | 29 +++ 5 files changed, 264 insertions(+), 14 deletions(-) create mode 100644 backend/templates/giftexchange.html create mode 100644 backend/templates/giftexchanges.html diff --git a/backend/migrations/20241103140734_secret-gift-exchange.up.sql b/backend/migrations/20241103140734_secret-gift-exchange.up.sql index ce169d2..0d5946a 100644 --- a/backend/migrations/20241103140734_secret-gift-exchange.up.sql +++ b/backend/migrations/20241103140734_secret-gift-exchange.up.sql @@ -3,11 +3,12 @@ CREATE TABLE `gift_exchange` ( `id` integer not null primary key autoincrement, `created_at` INTEGER not null default CURRENT_TIMESTAMP, - `created_by` ineger null, - `updated_at` INTEGER null default CURRENT_TIMESTAMP, - `updated_by` integer null, - `name` varchar(255) null, - `exchange_date` INTEGER null, + `created_by` integer not null default 0, + `updated_at` INTEGER not null default CURRENT_TIMESTAMP, + `updated_by` integer not null default 0, + `name` varchar(255) not null, + `exchange_date` INTEGER not null, + `status` INTEGER not null default 0, unique (`id`) ); @@ -15,12 +16,12 @@ CREATE TABLE `gift_exchange_participants` ( `id` integer not null primary key autoincrement, `created_at` INTEGER not null default CURRENT_TIMESTAMP, - `created_by` ineger null, - `updated_at` INTEGER null default CURRENT_TIMESTAMP, - `updated_by` integer null, + `created_by` integer not null default 0, + `updated_at` INTEGER not null default CURRENT_TIMESTAMP, + `updated_by` integer not null default 0, `exchange_id` INTEGER not null, `participant_id` INTEGER not null, - `gifter_id` INTEGER null, + `gifter_id` INTEGER not null, unique (`id`) ); diff --git a/backend/src/main.rs b/backend/src/main.rs index ca6db55..b646d04 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -2,6 +2,7 @@ use std::net::SocketAddr; use axum::{ middleware, routing::{get, get_service}, Extension, Router }; +use secret_gift_exchange::{giftexchange, giftexchanges}; use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; use sqlx::migrate::Migrator; use tower_http::services::ServeDir; @@ -13,6 +14,7 @@ mod routes; mod user; mod wishlist; mod email; +mod secret_gift_exchange; use error_handling::AppError; use middlewares::inject_user_data; @@ -62,6 +64,8 @@ async fn main() { .route("/userwishlist/add/:user_id", get(user_wishlist_add).post(user_wishlist_add_item)) .route("/userwishlist/bought/:user_id", get(user_wishlist_bought_item)) .route("/userwishlist/received/:user_id", get(user_wishlist_received_item)) + .route("/giftexchanges", get(giftexchanges)) + .route("/giftexchange/:giftexchange_id", get(giftexchange)) .nest_service("/assets", ServeDir::new("templates/assets") .fallback(get_service(ServeDir::new("templates/assets")))) .route("/", get(index)) diff --git a/backend/src/secret_gift_exchange.rs b/backend/src/secret_gift_exchange.rs index a5dc4cd..eb5cd4d 100644 --- a/backend/src/secret_gift_exchange.rs +++ b/backend/src/secret_gift_exchange.rs @@ -1,9 +1,26 @@ +use askama::Template; +use askama_axum::{IntoResponse, Response}; +use axum::{ + extract::{Path, State}, + response::Redirect, + Extension, +}; +use axum_extra::response::Html; +use http::StatusCode; +use serde::{Deserialize, Serialize}; +use sqlx::{FromRow, SqlitePool}; + +use crate::{ + middlewares::is_authorized, + user::{get_user_roles_display, UserData}, +}; + /// Select participants from user list /// create group id for exchange /// allow user to only see their recipient but whole list of participants /// link to recipient wish list /// button to create selections -/// +/// /// Database schema /// Table - gift_exchange /// Columns - id -> number @@ -13,7 +30,7 @@ /// - updated_at -> number /// - name -> text /// - exchange_date -> number -/// +/// /// Table - gift_exchange_participants /// Columns - id -> number /// - created_by -> number @@ -23,7 +40,7 @@ /// - exchange_id -> number (reference gift_exchange table) /// - participant_id -> number (reference user table) /// - gifter_id -> number (reference user table) -/// +/// /// Pages - sge_list /// - list of gift exchanges user is part of /// - sge_exchange @@ -34,9 +51,156 @@ /// - edit existing exchange /// - sge_participant_edit /// - add or remove participant to exchange -/// +/// /// API - select gifters -pub fn select_gifters() { +struct HtmlTemplate(T); +impl IntoResponse for HtmlTemplate +where + T: Template, +{ + fn into_response(self) -> Response { + match self.0.render() { + Ok(html) => Html(html).into_response(), + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to render template. Error: {}", err), + ) + .into_response(), + } + } } + +#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)] +struct GiftExchange { + id: i64, + created_at: i64, + created_by: i64, + updated_at: i64, + updated_by: i64, + name: String, + exchange_date: i64, + status: i64, +} + +#[derive(Template)] +#[template(path = "giftexchanges.html")] +struct GiftExchangesTemplate { + logged_in: bool, + name: String, + user_roles: Vec, + giftexchanges: Vec, +} + +pub async fn giftexchanges( + Extension(user_data): Extension>, + State(db_pool): State, +) -> impl IntoResponse { + let user_name = user_data.as_ref().map(|s| s.name.clone()); + let logged_in = user_name.is_some(); + let name = user_name.unwrap_or_default(); + + let giftexchanges = sqlx::query_as::<_, GiftExchange>("SELECT * FROM gift_exchange") + .fetch_all(&db_pool) + .await + .unwrap(); + + let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default(); + + if is_authorized("/giftexchange", user_data, db_pool.clone()).await { + // Get user roles + let user_roles = get_user_roles_display(userid, &db_pool.clone()).await; + + let template = GiftExchangesTemplate { + logged_in, + name, + user_roles, + giftexchanges, + }; + HtmlTemplate(template).into_response() + } else { + Redirect::to("/").into_response() + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)] +struct GiftExchangeParticipant { + id: i64, + created_at: i64, + created_by: i64, + updated_at: i64, + updated_by: i64, + exchange_id: i64, + participant_id: i64, + gifter_id: i64, +} + +#[derive(Template)] +#[template(path = "giftexchange.html")] +struct GiftExchangeTemplate { + logged_in: bool, + name: String, + user_roles: Vec, + giftexchange: GiftExchange, + participants: Vec, + non_participants: Vec, +} + +pub async fn giftexchange( + Path(exchange_id): Path, + Extension(user_data): Extension>, + State(db_pool): State, +) -> impl IntoResponse { + let user_name = user_data.as_ref().map(|s| s.name.clone()); + let logged_in = user_name.is_some(); + let name = user_name.unwrap_or_default(); + let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default(); + + if is_authorized("/giftexchange", user_data, db_pool.clone()).await { + // Get user roles + let user_roles = get_user_roles_display(userid, &db_pool.clone()).await; + + // Get gift exchange + let giftexchange = match sqlx::query_as!(GiftExchange, "SELECT * FROM gift_exchange WHERE id = ?", exchange_id) + .fetch_one(&db_pool) + .await { + Ok(giftexchange) => giftexchange, + Err(_) => { + GiftExchange::default() + }}; + + // Get participants + let participants = sqlx::query_as::<_, GiftExchangeParticipant>( + "SELECT * FROM gift_exchange_participants WHERE exchange_id = ?", + ) + .bind(exchange_id) + .fetch_all(&db_pool) + .await + .unwrap(); + + // Get non participants + let non_participants = sqlx::query_as::<_, UserData>( + "select * from users where users.id not in (select participant_id from gift_exchange_participants where exchange_id = ?)", + ) + .bind(exchange_id) + .fetch_all(&db_pool) + .await + .unwrap(); + + let template = GiftExchangeTemplate { + logged_in, + name, + user_roles, + giftexchange, + participants, + non_participants, + }; + HtmlTemplate(template).into_response() + } else { + Redirect::to("/").into_response() + } +} + +pub fn select_gifters() {} + diff --git a/backend/templates/giftexchange.html b/backend/templates/giftexchange.html new file mode 100644 index 0000000..bbcb762 --- /dev/null +++ b/backend/templates/giftexchange.html @@ -0,0 +1,52 @@ +{% extends "authorized.html" %} +{% block title %}Gift Exchange{% endblock %} +{% block center %} +
+ +
+
+ Exchange Name: +
+
+
+ + + + + + + + {% for participant in non_participants %} + + + + {% endfor %} + +
Name
{{ participant.name }}
+
+
+
+ + +
+
+
+ + + + + + + + {% for participant in participants %} + + + + {% endfor %} + +
Name
{{ participant.participant_id }}
+
+
+{% endblock center %} \ No newline at end of file diff --git a/backend/templates/giftexchanges.html b/backend/templates/giftexchanges.html new file mode 100644 index 0000000..bffbc3f --- /dev/null +++ b/backend/templates/giftexchanges.html @@ -0,0 +1,29 @@ +{% extends "authorized.html" %} +{% block title %}Gift Exchanges{% endblock %} +{% block center %} +
+ +
+
+ + + + + + + + + + {% for giftexchange in giftexchanges %} + + + + + + {% endfor %} + +
NameDateStatus
{{ giftexchange.name }}{{ giftexchange.exchange_date }}{{ giftexchange.status }}
+
+{% endblock center %} \ No newline at end of file