diff --git a/Makefile b/Makefile index e427aaa..43c490f 100644 --- a/Makefile +++ b/Makefile @@ -30,9 +30,9 @@ frontend: ## Build frontend assets @yarn install @-rm -rf ./static/cache/bundle/ @-mkdir ./static/cache/bundle/css/ + @yarn sass @yarn build @./scripts/bundle.sh - #@yarn run dart-sass -s compressed templates/main.scss ./static/cache/bundle/css/main.css lint: ## Lint codebase cargo fmt -v --all -- --emit files diff --git a/package.json b/package.json index 3228b3a..1ccf8cd 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "scripts": { "build": "webpack --mode production", - "sass": "yarn run dart-sass", + "sass": "dart-sass -s compressed templates/main.scss ./static/cache/bundle/css/main.css", "start": "webpack-dev-server --mode development --progress --color", "test": "jest" }, diff --git a/src/main.rs b/src/main.rs index 50d8cd0..252722f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ mod api; mod data; mod errors; mod middleware; -//mod pages; +mod pages; mod settings; mod static_assets; #[cfg(test)] @@ -39,7 +39,7 @@ mod tests; pub use crate::data::Data; pub use api::v1::ROUTES as V1_API_ROUTES; pub use middleware::auth::CheckLogin; -//pub use pages::routes::ROUTES as PAGES; +pub use pages::routes::ROUTES as PAGES; pub use settings::Settings; pub use static_assets::static_files::assets; @@ -48,9 +48,9 @@ use static_assets::FileMap; lazy_static! { pub static ref SETTINGS: Settings = Settings::new().unwrap(); pub static ref FILES: FileMap = FileMap::new(); -// -// pub static ref CSS: &'static str = -// FILES.get("./static/cache/bundle/css/main.css").unwrap(); + + pub static ref CSS: &'static str = + FILES.get("./static/cache/bundle/css/main.css").unwrap(); pub static ref JS: &'static str = FILES.get("./static/cache/bundle/bundle.js").unwrap(); @@ -157,7 +157,7 @@ pub fn get_identity_service() -> IdentityService { } pub fn services(cfg: &mut actix_web::web::ServiceConfig) { - //pages::services(cfg); + pages::services(cfg); api::v1::services(cfg); static_assets::services(cfg); } diff --git a/src/pages/auth/join.rs b/src/pages/auth/join.rs index 19ee487..77b5972 100644 --- a/src/pages/auth/join.rs +++ b/src/pages/auth/join.rs @@ -19,7 +19,7 @@ use actix_web::{error::ResponseError, http::header, web, HttpResponse, Responder use lazy_static::lazy_static; use sailfish::TemplateOnce; -use crate::api::v1::auth::runners; +use crate::api::v1::admin::auth::runners; use crate::errors::*; use crate::pages::errors::ErrorPage; use crate::AppData; @@ -92,10 +92,10 @@ mod tests { use super::*; - use crate::api::v1::account::{ + use crate::api::v1::admin::account::{ username::runners::username_exists, AccountCheckPayload, }; - use crate::api::v1::auth::runners::Register; + use crate::api::v1::admin::auth::runners::Register; use crate::data::Data; use crate::tests::*; use crate::*; diff --git a/src/pages/auth/login.rs b/src/pages/auth/login.rs index 08be460..a6d0f42 100644 --- a/src/pages/auth/login.rs +++ b/src/pages/auth/login.rs @@ -21,7 +21,7 @@ use lazy_static::lazy_static; use my_codegen::{get, post}; use sailfish::TemplateOnce; -use crate::api::v1::auth::runners; +use crate::api::v1::admin::auth::runners; use crate::errors::*; use crate::pages::errors::ErrorPage; use crate::AppData; @@ -95,7 +95,7 @@ mod tests { use super::*; - use crate::api::v1::auth::runners::{Login, Register}; + use crate::api::v1::admin::auth::runners::{Login, Register}; use crate::data::Data; use crate::tests::*; use crate::*; @@ -120,7 +120,7 @@ mod tests { }; let resp = test::call_service( &app, - post_request!(&msg, V1_API_ROUTES.auth.register).to_request(), + post_request!(&msg, V1_API_ROUTES.admin.auth.register).to_request(), ) .await; assert_eq!(resp.status(), StatusCode::OK); diff --git a/src/pages/auth/mod.rs b/src/pages/auth/mod.rs index ae5025b..35f31a6 100644 --- a/src/pages/auth/mod.rs +++ b/src/pages/auth/mod.rs @@ -18,6 +18,8 @@ pub mod join; pub mod login; pub mod sudo; +pub use crate::api::v1::admin::get_admin_check_login; + pub fn services(cfg: &mut actix_web::web::ServiceConfig) { cfg.service(login::login); cfg.service(login::login_submit); @@ -33,8 +35,8 @@ pub mod routes { impl Auth { pub const fn new() -> Auth { Auth { - login: "/login", - join: "/join", + login: "/api/v1/admin/page/login", + join: "/api/v1/admin/page/join", } } diff --git a/src/pages/mod.rs b/src/pages/mod.rs index 276c146..c14368c 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -60,11 +60,14 @@ mod tests { let app = get_app!(data).await; let urls = vec![ - PAGES.home.into(), + //PAGES.home.into(), PAGES.panel.campaigns.home.into(), PAGES.panel.campaigns.new.into(), - PAGES.panel.campaigns.get_feedback_route(&campaign.uuid), - PAGES.panel.campaigns.get_delete_route(&campaign.uuid), + // PAGES.panel.campaigns.get_feedback_route(&campaign.uuid), + PAGES + .panel + .campaigns + .get_delete_route(&campaign.campaign_id), ]; for url in urls.iter() { diff --git a/src/pages/panel/campaigns/delete.rs b/src/pages/panel/campaigns/delete.rs index d7199c9..9b2b5cb 100644 --- a/src/pages/panel/campaigns/delete.rs +++ b/src/pages/panel/campaigns/delete.rs @@ -25,8 +25,9 @@ use my_codegen::{get, post}; use sailfish::TemplateOnce; use uuid::Uuid; -use crate::api::v1::auth::runners::{login_runner, Login, Password}; -use crate::api::v1::campaign::runners; +use super::get_admin_check_login; +use crate::api::v1::admin::auth::runners::{login_runner, Login, Password}; +use crate::api::v1::admin::campaigns::runners; use crate::errors::*; use crate::pages::auth::sudo::SudoPage; use crate::AppData; @@ -43,11 +44,11 @@ async fn get_title( let campaign = sqlx::query_as!( Name, "SELECT name - FROM kaizen_campaign + FROM survey_campaigns WHERE - uuid = $1 + id = $1 AND - user_id = (SELECT ID from kaizen_users WHERE name = $2)", + user_id = (SELECT ID from survey_admins WHERE name = $2)", &uuid, &username ) @@ -57,7 +58,10 @@ async fn get_title( Ok(format!("Delete camapign \"{}\"?", campaign.name)) } -#[get(path = "PAGES.panel.campaigns.delete", wrap = "crate::CheckLogin")] +#[get( + path = "PAGES.panel.campaigns.delete", + wrap = "get_admin_check_login()" +)] pub async fn delete_campaign( id: Identity, path: web::Path, @@ -80,7 +84,10 @@ pub async fn delete_campaign( .body(page)) } -#[post(path = "PAGES.panel.campaigns.delete", wrap = "crate::CheckLogin")] +#[post( + path = "PAGES.panel.campaigns.delete", + wrap = "get_admin_check_login()" +)] pub async fn delete_campaign_submit( id: Identity, path: web::Path, @@ -102,7 +109,7 @@ pub async fn delete_campaign_submit( let status = e.status_code(); let heading = status.canonical_reason().unwrap_or("Error"); - let form_route = crate::V1_API_ROUTES.campaign.get_delete_route(&path); + let form_route = crate::V1_API_ROUTES.admin.campaign.get_delete_route(&path); let title = get_title(&username, &uuid, &data).await?; let mut ctx = SudoPage::new(&form_route, &title); let err = format!("{}", e); @@ -179,7 +186,7 @@ mod tests { &app, post_request!( &creds, - &PAGES.panel.campaigns.get_delete_route(&uuid.uuid), + &PAGES.panel.campaigns.get_delete_route(&uuid.campaign_id), FORM ) .cookie(cookies.clone()) diff --git a/src/pages/panel/campaigns/get.rs b/src/pages/panel/campaigns/get.rs index 0d8085f..73df5b4 100644 --- a/src/pages/panel/campaigns/get.rs +++ b/src/pages/panel/campaigns/get.rs @@ -14,48 +14,49 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -use actix_identity::Identity; -use actix_web::{web, HttpResponse, Responder}; -use my_codegen::get; -use sailfish::TemplateOnce; - -use crate::api::v1::campaign::{runners, GetFeedbackResp}; -use crate::AppData; -use crate::PAGES; - -#[derive(TemplateOnce)] -#[template(path = "panel/campaigns/get/index.html")] -struct ViewFeedback<'a> { - campaign: GetFeedbackResp, - uuid: &'a str, -} - -const PAGE: &str = "New Campaign"; - -impl<'a> ViewFeedback<'a> { - pub fn new(campaign: GetFeedbackResp, uuid: &'a str) -> Self { - Self { campaign, uuid } - } -} - -#[get( - path = "PAGES.panel.campaigns.get_feedback", - wrap = "crate::CheckLogin" -)] -pub async fn get_feedback( - id: Identity, - data: AppData, - path: web::Path, -) -> impl Responder { - let username = id.identity().unwrap(); - let path = path.into_inner(); - let feedback_resp = runners::get_feedback(&username, &path, &data) - .await - .unwrap(); - let page = ViewFeedback::new(feedback_resp, &path) - .render_once() - .unwrap(); - HttpResponse::Ok() - .content_type("text/html; charset=utf-8") - .body(page) -} +//use actix_identity::Identity; +//use actix_web::{web, HttpResponse, Responder}; +//use my_codegen::get; +//use sailfish::TemplateOnce; +// +//use crate::api::v1::admin::campaigns::{runners, GetFeedbackResp}; +//use crate::AppData; +//use crate::PAGES; +//use super::get_admin_check_login; +// +//#[derive(TemplateOnce)] +//#[template(path = "panel/campaigns/get/index.html")] +//struct ViewFeedback<'a> { +// campaign: GetFeedbackResp, +// uuid: &'a str, +//} +// +//const PAGE: &str = "New Campaign"; +// +//impl<'a> ViewFeedback<'a> { +// pub fn new(campaign: GetFeedbackResp, uuid: &'a str) -> Self { +// Self { campaign, uuid } +// } +//} +// +//#[get( +// path = "PAGES.panel.campaigns.get_feedback", +// wrap = "get_admin_check_login()" +//)] +//pub async fn get_feedback( +// id: Identity, +// data: AppData, +// path: web::Path, +//) -> impl Responder { +// let username = id.identity().unwrap(); +// let path = path.into_inner(); +// let feedback_resp = runners::get_feedback(&username, &path, &data) +// .await +// .unwrap(); +// let page = ViewFeedback::new(feedback_resp, &path) +// .render_once() +// .unwrap(); +// HttpResponse::Ok() +// .content_type("text/html; charset=utf-8") +// .body(page) +//} diff --git a/src/pages/panel/campaigns/mod.rs b/src/pages/panel/campaigns/mod.rs index 1fb5541..20d47d5 100644 --- a/src/pages/panel/campaigns/mod.rs +++ b/src/pages/panel/campaigns/mod.rs @@ -18,10 +18,14 @@ use actix_web::{HttpResponse, Responder}; use my_codegen::get; use sailfish::TemplateOnce; -use crate::api::v1::campaign::{runners::list_campaign_runner, ListCampaignResp}; +use crate::api::v1::admin::campaigns::{ + runners::list_campaign_runner, ListCampaignResp, +}; use crate::AppData; use crate::PAGES; +use super::get_admin_check_login; + pub mod delete; pub mod get; pub mod new; @@ -36,10 +40,10 @@ pub mod routes { impl Campaigns { pub const fn new() -> Campaigns { Campaigns { - home: "/campaigns", - new: "/campaigns/new", - get_feedback: "/campaigns/{uuid}/feedback", - delete: "/campaigns/{uuid}/delete", + home: "/api/v1/admin/page/campaigns", + new: "/api/v1/admin/page/campaigns/new", + get_feedback: "/api/v1/admin/page/campaigns/{uuid}/feedback", + delete: "/api/v1/admin/page/campaigns/{uuid}/delete", } } @@ -62,7 +66,7 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) { cfg.service(home); cfg.service(new::new_campaign); cfg.service(new::new_campaign_submit); - cfg.service(get::get_feedback); + // cfg.service(get::get_feedback); cfg.service(delete::delete_campaign); cfg.service(delete::delete_campaign_submit); } @@ -81,7 +85,7 @@ impl HomePage { const PAGE: &str = "Campaigns"; -#[get(path = "PAGES.panel.campaigns.home", wrap = "crate::CheckLogin")] +#[get(path = "PAGES.panel.campaigns.home", wrap = "get_admin_check_login()")] pub async fn home(data: AppData, id: Identity) -> impl Responder { let username = id.identity().unwrap(); let campaigns = list_campaign_runner(&username, &data).await.unwrap(); diff --git a/src/pages/panel/campaigns/new.rs b/src/pages/panel/campaigns/new.rs index d7ab8ed..6e3af84 100644 --- a/src/pages/panel/campaigns/new.rs +++ b/src/pages/panel/campaigns/new.rs @@ -21,12 +21,14 @@ use lazy_static::lazy_static; use my_codegen::{get, post}; use sailfish::TemplateOnce; -use crate::api::v1::campaign::{runners, CreateReq}; +use crate::api::v1::admin::campaigns::{runners, AddCapmaign}; use crate::errors::*; use crate::pages::errors::ErrorPage; use crate::AppData; use crate::PAGES; +use super::get_admin_check_login; + #[derive(Clone, TemplateOnce)] #[template(path = "panel/campaigns/new/index.html")] struct NewCampaign<'a> { @@ -53,20 +55,23 @@ lazy_static! { static ref INDEX: String = NewCampaign::default().render_once().unwrap(); } -#[get(path = "PAGES.panel.campaigns.new", wrap = "crate::CheckLogin")] +#[get(path = "PAGES.panel.campaigns.new", wrap = "get_admin_check_login()")] pub async fn new_campaign() -> impl Responder { HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(&*INDEX) } -#[post(path = "PAGES.panel.campaigns.new", wrap = "crate::CheckLogin")] +#[post(path = "PAGES.panel.campaigns.new", wrap = "get_admin_check_login()")] pub async fn new_campaign_submit( id: Identity, - payload: web::Form, + payload: web::Json, data: AppData, ) -> PageResult { - match runners::new(&payload.into_inner(), &data, &id).await { + let username = id.identity().unwrap(); + let mut payload = payload.into_inner(); + + match runners::add_runner(&username, &mut payload, &data).await { Ok(_) => { Ok(HttpResponse::Found() //TODO show stats of new campaign @@ -113,17 +118,19 @@ mod tests { let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await; let cookies = get_cookie!(signin_resp); - let new = CreateReq { + let new = AddCapmaign { name: CAMPAIGN_NAME.into(), + difficulties: DIFFICULTIES.into(), }; let new_resp = test::call_service( &app, - post_request!(&new, PAGES.panel.campaigns.new, FORM) - .cookie(cookies.clone()) + post_request!(&new, crate::PAGES.panel.campaigns.new) + .cookie(cookies) .to_request(), ) .await; + assert_eq!(new_resp.status(), StatusCode::FOUND); let headers = new_resp.headers(); assert_eq!( diff --git a/src/pages/panel/mod.rs b/src/pages/panel/mod.rs index cc7e626..2db471d 100644 --- a/src/pages/panel/mod.rs +++ b/src/pages/panel/mod.rs @@ -16,6 +16,8 @@ use actix_web::{http, HttpResponse, Responder}; use my_codegen::get; +pub use crate::api::v1::admin::get_admin_check_login; + use crate::PAGES; mod campaigns; @@ -30,7 +32,7 @@ pub mod routes { impl Panel { pub const fn new() -> Panel { Panel { - home: "/", + home: "/api/v1/admin/home/", campaigns: Campaigns::new(), } } @@ -47,7 +49,7 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) { campaigns::services(cfg); } -#[get(path = "PAGES.panel.home", wrap = "crate::CheckLogin")] +#[get(path = "PAGES.panel.home", wrap = "get_admin_check_login()")] pub async fn home() -> impl Responder { HttpResponse::Found() .insert_header((http::header::LOCATION, PAGES.panel.campaigns.home)) diff --git a/src/static_assets/static_files.rs b/src/static_assets/static_files.rs index bb29064..9c2d8fa 100644 --- a/src/static_assets/static_files.rs +++ b/src/static_assets/static_files.rs @@ -127,6 +127,7 @@ mod tests { assets::LOGO.path, assets::HEADSETS.path, *crate::JS, + *crate::CSS, *crate::GLUE, ] .iter() diff --git a/templates/auth/demo-user-banner.html b/templates/auth/demo-user-banner.html new file mode 100644 index 0000000..2f2ad5b --- /dev/null +++ b/templates/auth/demo-user-banner.html @@ -0,0 +1,11 @@ +<. if crate::SETTINGS.allow_demo && crate::SETTINGS.allow_registration { .> +

+ Try Kaizen without joining
+ + user: <.= crate::demo::DEMO_USER .> + + + password: <.= crate::demo::DEMO_PASSWORD .> + +

+<. } .> diff --git a/templates/auth/join/index.html b/templates/auth/join/index.html new file mode 100644 index 0000000..0011816 --- /dev/null +++ b/templates/auth/join/index.html @@ -0,0 +1,69 @@ +<. include!("../../components/base/top.html"); .> + +
+ +

Join Kaizen

+ <. include!("../../components/error/index.html"); .> +
+ + + + + + + + +
+ Forgot password? + +
+
+ +

+ Already have an account? + Login +

+
+<. include!("../../components/footer/index.html"); .> + +<. include!("../../components/base/bottom.html"); .> diff --git a/templates/auth/login/index.html b/templates/auth/login/index.html new file mode 100644 index 0000000..29f724f --- /dev/null +++ b/templates/auth/login/index.html @@ -0,0 +1,48 @@ +<. include!("../../components/base/top.html"); .> + +
+ +

Sign in

+ <. include!("../../components/error/index.html"); .> +
+ + + + +
+ Forgot password? + +
+
+ +

+ New to Kaizen? + Create an account +

+
+<. include!("../../components/footer/index.html"); .> + +<. include!("../../components/base/bottom.html"); .> diff --git a/templates/auth/main.scss b/templates/auth/main.scss new file mode 100644 index 0000000..030ea8c --- /dev/null +++ b/templates/auth/main.scss @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +.auth__body { + height: 100vh; + max-height: 100vh; + overflow-y: hidden; +} + +.auth__container { + width: 380px; + align-items: center; + margin: auto; + display: flex; + flex-direction: column; +} + +.auth__logo { + width: 150px; + padding-left: 20px; +} + +.auth__demo-user__banner { + margin: auto; + margin-top: 5px; + font-size: 0.8rem; + text-align: center; +} + +.auth__demo-user__cred { + font-family: monospace, monospace; +} + +.sudo__message { + margin-top: 20px; + margin-bottom: 20px; +} diff --git a/templates/auth/sudo/index.html b/templates/auth/sudo/index.html new file mode 100644 index 0000000..bcbb66c --- /dev/null +++ b/templates/auth/sudo/index.html @@ -0,0 +1,34 @@ +<. include!("../../components/base/top.html"); .> + +
+ +

Confirm Access

+ +

<.= title .>

+ <. include!("../../components/error/index.html"); .> +
+ + +
+ Forgot password? + +
+
+
+ <. include!("../../components/footer/index.html"); .> + +<. include!("../../components/base/bottom.html"); .> diff --git a/templates/components/_button.scss b/templates/components/_button.scss new file mode 100644 index 0000000..c6665b5 --- /dev/null +++ b/templates/components/_button.scss @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +@mixin button { + border: none; + + // margin: 10px 0; + // background-color: none; + background-color: #b4345b; + color: #fff; + // text-align: center; + + border-radius: 0px; + + padding: 0.1rem 0.75rem; + font-weight: 400; + text-align: center; + line-height: 1.2rem; + white-space: nowrap; + vertical-align: middle; + user-select: none; + border: 1px solid transparent; +} diff --git a/templates/components/base/bottom.html b/templates/components/base/bottom.html new file mode 100644 index 0000000..5f85cab --- /dev/null +++ b/templates/components/base/bottom.html @@ -0,0 +1 @@ + diff --git a/templates/components/base/top.html b/templates/components/base/top.html new file mode 100644 index 0000000..744e276 --- /dev/null +++ b/templates/components/base/top.html @@ -0,0 +1,8 @@ + + + + + + <.= PAGE .> | <.= crate::pages::NAME .> + + diff --git a/templates/components/error/index.html b/templates/components/error/index.html new file mode 100644 index 0000000..1fbb8f9 --- /dev/null +++ b/templates/components/error/index.html @@ -0,0 +1,6 @@ +<. if let Some(error) = error { .> +
+

<.= error.title .>

+

<.= error.message .>

+
+<. } .> diff --git a/templates/components/error/main.scss b/templates/components/error/main.scss new file mode 100644 index 0000000..bcf4fc6 --- /dev/null +++ b/templates/components/error/main.scss @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +.error__container { + width: 350px; + background-color: #d63f3f; + width: 100%; + padding: 10px 0; + opacity: 0.9; + font-size: 0.8rem; + font-family: monospace, monospace; +} + +.error__title { + color: #fff; + margin-left: 10px; +} + +.error__msg { + color: #fff; + margin-left: 10px; +} diff --git a/templates/components/footer/index.html b/templates/components/footer/index.html new file mode 100644 index 0000000..b86e7a0 --- /dev/null +++ b/templates/components/footer/index.html @@ -0,0 +1,24 @@ + diff --git a/templates/components/footer/main.scss b/templates/components/footer/main.scss new file mode 100644 index 0000000..4ec25b3 --- /dev/null +++ b/templates/components/footer/main.scss @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +.footer { + font-size: 0.8rem; + padding: 0px 20px; + + position: relative; + bottom: 10px; + width: 100%; +} + +.footer__section { + display: inline; + float: right; +} + +.footer__item { + display: inline; + list-style: none; + padding: 10px; +} diff --git a/templates/components/form/_partials.scss b/templates/components/form/_partials.scss new file mode 100644 index 0000000..433c276 --- /dev/null +++ b/templates/components/form/_partials.scss @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@import "../button"; + +@mixin form { + margin: 15px auto; + width: 100%; +} + +@mixin label { + display: block; + margin: 10px 0; +} + +@mixin input { + display: block; + margin: 10px 0; + width: 100%; + height: 35px; + border-radius: 0px; + border: 1px solid grey; +} + +@mixin action_container { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin alt_action { + font-size: 0.9rem; +} + +@mixin submit { + @include button; +} + +@mixin submit_hover { + cursor: pointer; +} diff --git a/templates/components/form/main.scss b/templates/components/form/main.scss new file mode 100644 index 0000000..580f768 --- /dev/null +++ b/templates/components/form/main.scss @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@import "partials"; + +.form { + @include form; +} + +.form__label { + @include label; +} + +.form__input { + @include input; +} + +.form__action-container { + @include action_container; +} + +.form__alt-action { + @include alt_action; +} + +.form__submit { + @include submit; +} + +.form__submit:hover { + @include submit_hover; +} diff --git a/templates/components/header.html b/templates/components/header.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/errors/index.html b/templates/errors/index.html new file mode 100644 index 0000000..e526fbe --- /dev/null +++ b/templates/errors/index.html @@ -0,0 +1,9 @@ +<. include!("../components/base/top.html"); .> + +
+

<.= title .>

+

<.= message .>

+
+ <. include!("../components/footer/index.html"); .> + +<. include!("../components/base/bottom.html"); .> diff --git a/templates/errors/main.scss b/templates/errors/main.scss new file mode 100644 index 0000000..593322e --- /dev/null +++ b/templates/errors/main.scss @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +.error-title { + text-align: center; +} + +.error-message { + text-align: center; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..b5ef88c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,63 @@ +<. include!("./components/base/top.html"); .> + +
+

mCaptcha device benchmark survey

+

Why should I participate

+

+ mCaptcha + is a cutting edge, privacy respecting CAPTCHA system. We use a + proof of work + based mechanism to defend against + denial-of-service attacks + which allows for a non-interactive CAPTCHA experience. We require + performance metrics measured on a wide range of devices to fine-tune the + system for optimal user experience, see for yourself! +

+
+ +
+ +

What do I get?

+

+ We are conducting a lucky draw. Two lucky participants, selected at + random, will win a pair of + JBL headsets worth over ₹2,500! Also, you will be helping us make the internet healthier :) +

+ +

Privacy policy

+

This survey collects the following information:

+
    +
  • Device name
  • +
  • Operating system
  • +
  • Processor information
  • +
  • Benchmark results
  • +
+ No Personally identifying information is collected +
+ <. include!("../components/footer/index.html"); .> + Get started + + +<. include!("./components/base/bottom.html"); .> diff --git a/templates/main.scss b/templates/main.scss new file mode 100644 index 0000000..e2a380f --- /dev/null +++ b/templates/main.scss @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@import "./components/footer/main"; +@import "./components/form/main"; +@import "./components/error/main"; + +@import "./auth/main"; +@import "./panel/main"; +@import "./panel/campaigns/new/main"; +@import "./panel/campaigns/get/main"; +@import "./errors/main"; + +* { + margin: 0; + padding: 0; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + + font-family: "Inter UI", -apple-system, BlinkMacSystemFont, "Roboto", + "Segoe UI", Helvetica, Arial, sans-serif; +} + +a { + text-decoration: none; + color: rgb(0, 86, 179); +} + +a:hover { + text-decoration: underline; +} + +body { + max-width: 100vw; + overflow-x: hidden; + min-height: 100vh; + display: flex; + flex-direction: column; +} diff --git a/templates/panel/campaigns/get/index.html b/templates/panel/campaigns/get/index.html new file mode 100644 index 0000000..7c789a5 --- /dev/null +++ b/templates/panel/campaigns/get/index.html @@ -0,0 +1,58 @@ +<. include!("../../../components/base/top.html"); .> + + <. include!("../../nav/index.html"); .> +
+

<.= campaign.name .>

+ + + + +
+ Campaign ID + <.= uuid .> +
+ + <. if campaign.feedbacks.is_empty() { .> +

+ Looks like you don't have any feedback on this campaign. +

+ <. } else { .> + + + + + + + + + + <. for feedback in campaign.feedbacks.iter() { .> + + + + + + + + <. } .> +
+ <. include!("../../../components/footer/index.html"); .> + +<. include!("../../../components/base/bottom.html"); .> diff --git a/templates/panel/campaigns/get/main.scss b/templates/panel/campaigns/get/main.scss new file mode 100644 index 0000000..d004c4b --- /dev/null +++ b/templates/panel/campaigns/get/main.scss @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +.feedback__table { + width: 80%; + margin: 40px; +} + +.feedback__title-text--small { + width: 50px; +} + +.feedback__title-text--large { + width: 350px; + text-align: start; +} + +.feedback__title-text--normal { + width: 150px; +} + +.feedback__description{ + text-align: start; +} diff --git a/templates/panel/campaigns/index.html b/templates/panel/campaigns/index.html new file mode 100644 index 0000000..19e292a --- /dev/null +++ b/templates/panel/campaigns/index.html @@ -0,0 +1,42 @@ +<. include!("../../components/base/top.html"); .> + + <. include!("../nav/index.html"); .> +
+ <. if data.is_empty() { .> +

+ Looks like you don't have any campaings registered, + click here + to create a new campaign! +

+ + <. } else { .> + + + + + + + + + <. for campaign in data.iter() { .> + <. let route = crate::PAGES.panel.campaigns.get_feedback_route(&campaign.uuid); .> + + + + + <. } .> + +
NameUUID
+ +

<.= campaign.name .>

+
+
+ +

<.= campaign.uuid .>

+
+ <. } .> +
+ <. include!("../../components/footer/index.html"); .> + +<. include!("../../components/base/bottom.html"); .> diff --git a/templates/panel/campaigns/new/form.html b/templates/panel/campaigns/new/form.html new file mode 100644 index 0000000..e0b5efb --- /dev/null +++ b/templates/panel/campaigns/new/form.html @@ -0,0 +1,21 @@ +

Create new campaigns

+<. include!("../../../components/error/index.html"); .> +
+ + +
diff --git a/templates/panel/campaigns/new/index.html b/templates/panel/campaigns/new/index.html new file mode 100644 index 0000000..8613b8d --- /dev/null +++ b/templates/panel/campaigns/new/index.html @@ -0,0 +1,7 @@ +<. include!("../../../components/base/top.html"); .> + + <. include!("../../nav/index.html"); .> +
<. include!("./form.html"); .>
+ <. include!("../../../components/footer/index.html"); .> + +<. include!("../../../components/base/bottom.html"); .> diff --git a/templates/panel/campaigns/new/main.scss b/templates/panel/campaigns/new/main.scss new file mode 100644 index 0000000..407cd4c --- /dev/null +++ b/templates/panel/campaigns/new/main.scss @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@import "../../../components/form/partials"; + +.new-campaign__form { + @include form; + width: 480px; +} diff --git a/templates/panel/main.scss b/templates/panel/main.scss new file mode 100644 index 0000000..3c68844 --- /dev/null +++ b/templates/panel/main.scss @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +@import "./nav/main.scss"; +@import "../components/button"; + +.panel__body { + width: 100vw; + min-height: 100vh; +} + +.panel__container { + width: 80%; + align-items: center; + margin: auto; + margin-top: 50px; + display: flex; + flex-direction: column; +} + +.link__btn { + @include button; + margin: 0px 5px; + padding: 0.3rem 0.3rem; + padding-bottom: 0.2rem; +} + +table { + border-collapse: collapse; + caption-side: bottom; + border-color: #e9ecef; + text-align: center; + min-width: 50%; + margin: 20px auto; +} + +table > thead { + vertical-align: bottom; + border-bottom: 1px solid #cdc8ca; + text-align: center; +} + +table { + th { + text-align: center; + } + td { + margin: auto; + padding: 10px; + border-bottom: 1px solid #edddd1; + } +} + +.asset__container { + margin: 10px auto; +} + +.asset__name { + font-weight: bold; +} + +.asset__value { + margin: 10px 0; + display: inline; + width: auto; + word-wrap: break-word; + overflow-wrap: break-word; + font-family: monospace, monospace; + + background-color: #f2f2f2; + padding: 0.125rem 0.25rem; + margin: 0 0.25rem; + border-radius: 3px; + font-size: 1em; + color: #1a1a1a; + overflow-x: auto; + white-space: pre; +} diff --git a/templates/panel/nav/index.html b/templates/panel/nav/index.html new file mode 100644 index 0000000..65b3a34 --- /dev/null +++ b/templates/panel/nav/index.html @@ -0,0 +1,45 @@ + diff --git a/templates/panel/nav/main.scss b/templates/panel/nav/main.scss new file mode 100644 index 0000000..1246498 --- /dev/null +++ b/templates/panel/nav/main.scss @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +.nav__container { + display: flex; + flex-direction: row; + + box-sizing: border-box; + width: 100%; +} + +.nav__hamburger-menu { + display: none; +} + +.nav__spacer { + flex: 3; + margin: auto; +} + +.nav__logo-container { + display: inline-flex; +} + +.nav__toggle { + display: none; +} + +.nav__logo { + display: inline-flex; + margin: auto; + padding: 5px; + width: 40px; +} + +.nav__logo-text { + margin: auto; + letter-spacing: 5px; + padding: 5px; +} + +.nav__link-group { + list-style: none; + display: flex; + flex-direction: row; + align-items: center; + align-self: center; + margin: auto; + text-align: center; +} + +.nav__link-container { + display: flex; + padding: 10px; + height: 100%; + margin: auto; +} + +.nav__link { + text-decoration: none; +} diff --git a/templates/panel/nav/mobile.scss b/templates/panel/nav/mobile.scss new file mode 100644 index 0000000..bb7270d --- /dev/null +++ b/templates/panel/nav/mobile.scss @@ -0,0 +1,102 @@ +$hamburger-menu-animation: 0.4s ease-out; +$nav__hamburger-inner-height: 1.3px; + +.nav__container { + flex-direction: column; +} + +.nav__header { + display: flex; + flex-direction: row; + min-width: 100%; + height: 55px; + justify-content: space-between; +} + +.nav__link-group { + position: sticky; + flex-direction: column; +} + +.nav__hamburger-menu { + display: inline-block; + width: 50px; + height: 50px; +} + +.nav__spacer { + display: none; +} + +.nav__toggle:not(:checked) ~ .nav__link-group { + max-height: 0; + transition: max-height $hamburger-menu-animation; + overflow: hidden; +} + +.nav__toggle:checked ~ .nav__link-group { + max-height: 500px; + transition: max-height $hamburger-menu-animation; +} + +.nav__toggle:checked ~ .nav__header { + .nav__hamburger-inner::after { + width: 24px; + bottom: $nav__hamburger-inner-height; + transform: rotate(-90deg); + transition: bottom 0.1s ease-out, + transform 0.22s cubic-bezier(0.215, 0.61, 0.355, 1) 0.12s, + width 0.1s ease-out; + } + + .nav__hamburger-inner::before { + top: 0; + opacity: 0; + transition: top 0.1s ease-out, opacity 0.1s ease-out 0.12s; + } + + .nav__hamburger-inner { + transform: rotate(225deg); + transition-delay: 0.12s; + transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } +} + +.nav__hamburger-inner::after { + bottom: -7px; + transition: bottom 0.1s ease-in 0.25s, + transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19), + width 0.1s ease-in 0.25s; +} + +.nav__hamburger-inner::after, +.nav__hamburger-inner::before { + content: ''; + display: block; +} + +.nav__hamburger-inner::before { + top: -7px; + transition: top 0.1s ease-in 0.25s, opacity 0.1s ease-in; +} + +.nav__hamburger-inner { + top: 50%; + margin: auto; + transition-duration: 0.22s; + transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); +} + +.nav__hamburger-inner, +.nav__hamburger-inner::after, +.nav__hamburger-inner::before { + width: 24px; + height: $nav__hamburger-inner-height; + position: relative; + background: #000; +} + +.nav__hamburger-menu, +.nav__hamburger-inner { + display: block; +}