This commit is contained in:
parent
d44246a483
commit
197e4cfde9
8 changed files with 302 additions and 50 deletions
63
Cargo.lock
generated
63
Cargo.lock
generated
|
@ -49,9 +49,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.95"
|
version = "1.0.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
|
@ -152,7 +152,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "based"
|
name = "based"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.hydrar.de/jmarya/based?branch=ui#f2cfcf27bbe7668caf95a99e7b8f7698e023fdec"
|
source = "git+https://git.hydrar.de/jmarya/based#b8ed8da199e372a704c2b457c49549e396e44e66"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -257,9 +257,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.14"
|
version = "1.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9"
|
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
@ -1166,9 +1166,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
@ -1271,9 +1271,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.25"
|
version = "0.4.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loom"
|
name = "loom"
|
||||||
|
@ -1345,9 +1345,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
@ -1384,9 +1384,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.13"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
|
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1764,9 +1764,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.8"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
]
|
]
|
||||||
|
@ -2104,18 +2104,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.217"
|
version = "1.0.218"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.217"
|
version = "1.0.218"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2124,9 +2124,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.138"
|
version = "1.0.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -2567,9 +2567,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.16.0"
|
version = "3.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
@ -2847,9 +2847,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ubyte"
|
name = "ubyte"
|
||||||
|
@ -2869,6 +2869,7 @@ dependencies = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yml",
|
"serde_yml",
|
||||||
|
"sqlx",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2906,9 +2907,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.16"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
|
@ -2962,9 +2963,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.13.1"
|
version = "1.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
|
checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -3318,9 +3319,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,4 +9,5 @@ rocket = "0.5.1"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_yml = "0.0.12"
|
serde_yml = "0.0.12"
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
based = { git = "https://git.hydrar.de/jmarya/based", branch = "ui" }
|
based = { git = "https://git.hydrar.de/jmarya/based" }
|
||||||
|
sqlx = "0.8.3"
|
||||||
|
|
|
@ -10,6 +10,11 @@ description = "Root Project"
|
||||||
# Project Icon
|
# Project Icon
|
||||||
icon = "icon.png"
|
icon = "icon.png"
|
||||||
|
|
||||||
|
# Associated repositories
|
||||||
|
repositories = [
|
||||||
|
"https://github.com/example/example"
|
||||||
|
]
|
||||||
|
|
||||||
# Project Website
|
# Project Website
|
||||||
website = "https://example.com"
|
website = "https://example.com"
|
||||||
|
|
||||||
|
|
9
migrations/0001_init.sql
Normal file
9
migrations/0001_init.sql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE TABLE investment (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
project TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
amount DOUBLE PRECISION NOT NULL,
|
||||||
|
"when" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
payed BOOLEAN NOT NULL DEFAULT false
|
||||||
|
);
|
|
@ -12,6 +12,7 @@ pub struct Project {
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub website: Option<String>,
|
pub website: Option<String>,
|
||||||
|
pub repositories: Option<Vec<String>>,
|
||||||
pub documentation: Option<String>,
|
pub documentation: Option<String>,
|
||||||
pub since: Option<String>,
|
pub since: Option<String>,
|
||||||
pub contact: Option<ContactInfo>,
|
pub contact: Option<ContactInfo>,
|
||||||
|
|
|
@ -1,10 +1,97 @@
|
||||||
// TODO : Investment feature
|
|
||||||
// TODO : Reoccuring investments
|
// TODO : Reoccuring investments
|
||||||
|
|
||||||
|
use based::get_pg;
|
||||||
|
use rocket::{form::Form, post, response::Redirect, FromForm};
|
||||||
|
use sqlx::prelude::FromRow;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, FromRow)]
|
||||||
pub struct Investment {
|
pub struct Investment {
|
||||||
|
pub id: i64,
|
||||||
pub project: String,
|
pub project: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
pub amount: f64,
|
pub amount: f64,
|
||||||
pub what: String,
|
|
||||||
pub why: String,
|
|
||||||
pub when: String,
|
pub when: String,
|
||||||
|
pub payed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Investment {
|
||||||
|
pub async fn new_investment(
|
||||||
|
project: &str,
|
||||||
|
amount: f64,
|
||||||
|
title: &str,
|
||||||
|
description: &str,
|
||||||
|
) -> Investment {
|
||||||
|
let project = project.trim_end_matches("/");
|
||||||
|
sqlx::query_as(
|
||||||
|
"INSERT INTO investment (project, title, description, amount)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
RETURNING *",
|
||||||
|
)
|
||||||
|
.bind(project)
|
||||||
|
.bind(title)
|
||||||
|
.bind(description)
|
||||||
|
.bind(amount)
|
||||||
|
.fetch_one(get_pg!())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pay(id: i64) -> String {
|
||||||
|
let res: (String,) =
|
||||||
|
sqlx::query_as("UPDATE investment SET payed = true WHERE id = $1 RETURNING project")
|
||||||
|
.bind(id)
|
||||||
|
.fetch_one(get_pg!())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
res.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_investments_of(prj: &str) -> Vec<Investment> {
|
||||||
|
let prj = prj.trim_end_matches("/");
|
||||||
|
sqlx::query_as("SELECT * FROM investment WHERE project = $1 ORDER BY \"when\" DESC")
|
||||||
|
.bind(prj)
|
||||||
|
.fetch_all(get_pg!())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_total_of(prj: &str) -> (f64, f64) {
|
||||||
|
let prj = prj.trim_end_matches("/");
|
||||||
|
let payed: Result<(f64,), sqlx::Error> = sqlx::query_as(
|
||||||
|
"SELECT SUM(amount) FROM investment WHERE project = $1 AND payed = true",
|
||||||
|
)
|
||||||
|
.bind(prj)
|
||||||
|
.fetch_one(get_pg!())
|
||||||
|
.await;
|
||||||
|
let total: Result<(f64,), sqlx::Error> =
|
||||||
|
sqlx::query_as("SELECT SUM(amount) FROM investment WHERE project = $1")
|
||||||
|
.bind(prj)
|
||||||
|
.fetch_one(get_pg!())
|
||||||
|
.await;
|
||||||
|
if let (Ok(payed), Ok(total)) = (payed, total) {
|
||||||
|
(payed.0, total.0)
|
||||||
|
} else {
|
||||||
|
(0.00, 0.00)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct NewInvestForm {
|
||||||
|
title: String,
|
||||||
|
amount: String,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/prj/<root>/invest", data = "<form>")]
|
||||||
|
pub async fn post_new_invest(root: String, form: Form<NewInvestForm>) -> Redirect {
|
||||||
|
Investment::new_investment(
|
||||||
|
&root,
|
||||||
|
form.amount.parse().unwrap(),
|
||||||
|
&form.title,
|
||||||
|
&form.description,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Redirect::to(format!("/prj/{}?tab=invest", root))
|
||||||
}
|
}
|
||||||
|
|
176
src/main.rs
176
src/main.rs
|
@ -1,17 +1,25 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use based::asset::AssetRoutes;
|
use based::asset::AssetRoutes;
|
||||||
use based::page;
|
|
||||||
use based::request::{RequestContext, StringResponse};
|
use based::request::{RequestContext, StringResponse};
|
||||||
use based::ui::components::{Breadcrumb, Shell, Tabs};
|
use based::ui::components::prelude::{
|
||||||
use based::ui::prelude::*;
|
ColoredMaterialIcon, MaterialIcon, Modal, ModalOpenButton, Shell, Timeline, TimelineElement,
|
||||||
use based::ui::primitives::flex::Row;
|
Tooltip,
|
||||||
|
};
|
||||||
|
use based::ui::components::{Breadcrumb, Card, ProgressBar, Tabs};
|
||||||
|
use based::ui::primitives::div::Center;
|
||||||
|
use based::ui::primitives::flex::{Column, Row};
|
||||||
|
use based::ui::primitives::list::{CheckIcon, CheckIconRounded, CheckIconRoundedGray};
|
||||||
use based::ui::primitives::Optional;
|
use based::ui::primitives::Optional;
|
||||||
|
use based::ui::{prelude::*, AttrExtendable};
|
||||||
|
use based::{get_pg, page};
|
||||||
use config::{get_prj, Project, ProjectConfig};
|
use config::{get_prj, Project, ProjectConfig};
|
||||||
|
use invest::Investment;
|
||||||
use maud::{PreEscaped, Render};
|
use maud::{PreEscaped, Render};
|
||||||
use rocket::fs::NamedFile;
|
use rocket::fs::NamedFile;
|
||||||
use rocket::request::FromSegments;
|
use rocket::request::FromSegments;
|
||||||
use rocket::{get, launch, routes, State};
|
use rocket::response::Redirect;
|
||||||
|
use rocket::{get, launch, post, routes, State};
|
||||||
use site::gen_shell;
|
use site::gen_shell;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -52,19 +60,20 @@ impl<'r> FromSegments<'r> for PathSegment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/prj/<path..>")]
|
#[get("/prj/<path..>?<tab>")]
|
||||||
pub async fn project_page(
|
pub async fn project_page(
|
||||||
path: PathSegment,
|
path: PathSegment,
|
||||||
ctx: RequestContext,
|
ctx: RequestContext,
|
||||||
shell: &State<Shell>,
|
shell: &State<Shell>,
|
||||||
c: &State<ProjectConfig>,
|
c: &State<ProjectConfig>,
|
||||||
|
tab: Option<&str>,
|
||||||
) -> Option<StringResponse> {
|
) -> Option<StringResponse> {
|
||||||
let mut prj = c.get(path.segments.first()?);
|
let mut prj = c.get(path.segments.first()?);
|
||||||
for p in &path.segments[1..] {
|
for p in &path.segments[1..] {
|
||||||
prj = prj.as_ref()?.sub.as_ref()?.get(p);
|
prj = prj.as_ref()?.sub.as_ref()?.get(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(render_project(shell, ctx, prj?, c, path.to_str()).await)
|
Some(render_project(shell, ctx, prj?, c, path.to_str(), tab.unwrap_or_default()).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/static/<file>")]
|
#[get("/static/<file>")]
|
||||||
|
@ -84,7 +93,7 @@ pub async fn main_page(
|
||||||
let first = keys.first().unwrap();
|
let first = keys.first().unwrap();
|
||||||
let prj = c.get(*first).unwrap();
|
let prj = c.get(*first).unwrap();
|
||||||
|
|
||||||
render_project(shell, ctx, prj, c, format!("{first}/")).await
|
render_project(shell, ctx, prj, c, format!("{first}/"), "").await
|
||||||
} else {
|
} else {
|
||||||
// TODO : root project overview
|
// TODO : root project overview
|
||||||
site::gen_site(c, ctx).await
|
site::gen_site(c, ctx).await
|
||||||
|
@ -105,6 +114,120 @@ pub fn DetailLink(text: &str, reference: &str) -> PreEscaped<String> {
|
||||||
.render()
|
.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_invest_timeline(investments: &[Investment]) -> PreEscaped<String> {
|
||||||
|
let mut t = Timeline();
|
||||||
|
|
||||||
|
for i in investments {
|
||||||
|
t = t.add_element(TimelineElement::new(
|
||||||
|
Text(&i.when),
|
||||||
|
Row(vec![
|
||||||
|
Text(&i.title).render(),
|
||||||
|
Text(&format!("{:.2} $", i.amount)).render(),
|
||||||
|
if i.payed {
|
||||||
|
Tooltip(CheckIconRounded(), Text("Payed")).render()
|
||||||
|
} else {
|
||||||
|
CheckIconRoundedGray()
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.gap(ScreenValue::_2)
|
||||||
|
.items_center(),
|
||||||
|
Column(vec![
|
||||||
|
Text(&i.description).render(),
|
||||||
|
if !i.payed {
|
||||||
|
Form::new(&format!("/invest/pay?id={}", i.id))
|
||||||
|
.add_input(FormSubmitButton(Text("Pay")))
|
||||||
|
.render()
|
||||||
|
} else {
|
||||||
|
Nothing()
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.gap(ScreenValue::_2),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn invest_tab(prj: &Project, root: &str) -> PreEscaped<String> {
|
||||||
|
let (add_invest_id, add_invest_modal) = Modal(
|
||||||
|
"Add Investment",
|
||||||
|
Form::new(&format!("/prj/{root}invest"))
|
||||||
|
.id("new_invest")
|
||||||
|
.add_input(TextInput("title").required().placeholder("Title"))
|
||||||
|
.add_input(
|
||||||
|
TextInput("amount")
|
||||||
|
.icon(MaterialIcon("attach_money"))
|
||||||
|
.required()
|
||||||
|
.placeholder("Amount")
|
||||||
|
.pattern(r#"^\d+(\.\d+)?$"#),
|
||||||
|
)
|
||||||
|
.add_input(
|
||||||
|
TextArea()
|
||||||
|
.name("description")
|
||||||
|
.required()
|
||||||
|
.placeholder("Description"),
|
||||||
|
),
|
||||||
|
|_| {
|
||||||
|
Center(Context(
|
||||||
|
FormSubmitButton("Create").add_attr("form", "new_invest"),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let investments = Investment::get_all_investments_of(root).await;
|
||||||
|
|
||||||
|
let t = build_invest_timeline(&investments);
|
||||||
|
|
||||||
|
let inv = Investment::get_total_of(root).await;
|
||||||
|
|
||||||
|
let percentage = inv.0 / inv.1;
|
||||||
|
let percentage = (percentage * 100.0) as u8;
|
||||||
|
|
||||||
|
Column(vec![
|
||||||
|
add_invest_modal,
|
||||||
|
Margin(
|
||||||
|
Row(vec![
|
||||||
|
Margin(Width(
|
||||||
|
ScreenValue::full,
|
||||||
|
Row(vec![
|
||||||
|
ColoredMaterialIcon("paid", Green::_600),
|
||||||
|
Text(&format!(
|
||||||
|
"Total Investment: {:.2} $ / {:.2} $",
|
||||||
|
inv.0, inv.1
|
||||||
|
))
|
||||||
|
.color(&Green::_600)
|
||||||
|
.render(),
|
||||||
|
Width(
|
||||||
|
ScreenValue::full,
|
||||||
|
Margin(ProgressBar(percentage, true, Green::_600)).x(ScreenValue::_12),
|
||||||
|
)
|
||||||
|
.render(),
|
||||||
|
])
|
||||||
|
.gap(ScreenValue::_2)
|
||||||
|
.full_center(),
|
||||||
|
))
|
||||||
|
.x(ScreenValue::auto)
|
||||||
|
.render(),
|
||||||
|
ModalOpenButton(
|
||||||
|
&add_invest_id,
|
||||||
|
Padding(
|
||||||
|
Rounded(Background(MaterialIcon("add")).color(Blue::_600))
|
||||||
|
.size(Size::Medium),
|
||||||
|
)
|
||||||
|
.x(ScreenValue::_2)
|
||||||
|
.top(ScreenValue::_2),
|
||||||
|
)
|
||||||
|
.render(),
|
||||||
|
])
|
||||||
|
.justify(Justify::Between),
|
||||||
|
)
|
||||||
|
.left(ScreenValue::_4)
|
||||||
|
.render(),
|
||||||
|
t,
|
||||||
|
])
|
||||||
|
.render()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn info_tab(prj: &Project, root: &str) -> PreEscaped<String> {
|
pub fn info_tab(prj: &Project, root: &str) -> PreEscaped<String> {
|
||||||
Flex(
|
Flex(
|
||||||
Div()
|
Div()
|
||||||
|
@ -119,6 +242,14 @@ pub fn info_tab(prj: &Project, root: &str) -> PreEscaped<String> {
|
||||||
.push(Optional(prj.documentation.as_deref(), |docs| {
|
.push(Optional(prj.documentation.as_deref(), |docs| {
|
||||||
DetailLink("Documentation:", docs)
|
DetailLink("Documentation:", docs)
|
||||||
}))
|
}))
|
||||||
|
.push(Optional(prj.repositories.as_deref(), |repos| {
|
||||||
|
Column(vec![
|
||||||
|
Text("Repositories:").xl().semibold().render(),
|
||||||
|
UnorderedList()
|
||||||
|
.push_for_each(repos, |repo: &_| Link(repo, Text(repo)))
|
||||||
|
.render(),
|
||||||
|
])
|
||||||
|
}))
|
||||||
.push(Optional(prj.since.as_deref(), |since| {
|
.push(Optional(prj.since.as_deref(), |since| {
|
||||||
Text(&format!("Since: {since}"))
|
Text(&format!("Since: {since}"))
|
||||||
}))
|
}))
|
||||||
|
@ -193,6 +324,7 @@ pub async fn render_project(
|
||||||
prj: &Project,
|
prj: &Project,
|
||||||
c: &State<ProjectConfig>,
|
c: &State<ProjectConfig>,
|
||||||
mut root: String,
|
mut root: String,
|
||||||
|
tab: &str,
|
||||||
) -> StringResponse {
|
) -> StringResponse {
|
||||||
let title = format!("{} - Umbrella ☂️", prj.name);
|
let title = format!("{} - Umbrella ☂️", prj.name);
|
||||||
|
|
||||||
|
@ -214,6 +346,8 @@ pub async fn render_project(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let invest_tab = invest_tab(prj, &root).await;
|
||||||
|
|
||||||
page!(
|
page!(
|
||||||
shell,
|
shell,
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -230,12 +364,9 @@ pub async fn render_project(
|
||||||
.push(
|
.push(
|
||||||
Padding(
|
Padding(
|
||||||
Tabs()
|
Tabs()
|
||||||
|
.active(tab)
|
||||||
.add_tab("info", Text("📜 Info").medium(), info_tab(prj, &root))
|
.add_tab("info", Text("📜 Info").medium(), info_tab(prj, &root))
|
||||||
.add_tab(
|
.add_tab("invest", Text("💵 Invest").medium(), invest_tab)
|
||||||
"invest",
|
|
||||||
Text("💵 Invest").medium(),
|
|
||||||
Div().vanish().push(Text("TODO"))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.x(ScreenValue::_6)
|
.x(ScreenValue::_6)
|
||||||
)
|
)
|
||||||
|
@ -254,9 +385,26 @@ async fn rocket() -> _ {
|
||||||
|
|
||||||
let shell = gen_shell();
|
let shell = gen_shell();
|
||||||
|
|
||||||
|
sqlx::migrate!("./migrations").run(get_pg!()).await.unwrap();
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount_assets()
|
.mount_assets()
|
||||||
.mount("/", routes![main_page, icon_res, project_page])
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![
|
||||||
|
main_page,
|
||||||
|
icon_res,
|
||||||
|
project_page,
|
||||||
|
invest::post_new_invest,
|
||||||
|
pay_investment
|
||||||
|
],
|
||||||
|
)
|
||||||
.manage(conf)
|
.manage(conf)
|
||||||
.manage(shell)
|
.manage(shell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/invest/pay?<id>")]
|
||||||
|
pub async fn pay_investment(id: i64) -> Redirect {
|
||||||
|
let prj = Investment::pay(id).await;
|
||||||
|
Redirect::to(format!("/prj/{}?tab=invest", prj))
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use based::{
|
use based::{
|
||||||
request::{RequestContext, StringResponse},
|
request::{RequestContext, StringResponse},
|
||||||
ui::components::Shell,
|
ui::components::prelude::Shell,
|
||||||
};
|
};
|
||||||
use maud::{html, Render};
|
use maud::{html, Render};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue