initial working
This commit is contained in:
		
						commit
						0cd75a9ff5
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
/*/target
 | 
			
		||||
/target
 | 
			
		||||
terminwahl_front/dist
 | 
			
		||||
db.sqlite
 | 
			
		||||
							
								
								
									
										2653
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2653
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
[workspace]
 | 
			
		||||
members = ["terminwahl_back", "terminwahl_front"]
 | 
			
		||||
							
								
								
									
										24
									
								
								terminwahl_back/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								terminwahl_back/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "terminwahl_back"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
default-run = "terminwahl_back"
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
actix-web = "4.3"
 | 
			
		||||
actix-rt = "2.8"
 | 
			
		||||
actix-files = "0.6.2"
 | 
			
		||||
actix-session = { version = "0.7", features = ["cookie-session"] }
 | 
			
		||||
# sqlx is currently on version 0.3.5 in this project due to breaking changes introduced in versions
 | 
			
		||||
# beyond 0.4.0, which changed the return type of 'exectute' to a 'Done'. Also the row parsing related
 | 
			
		||||
# traits have been altered. The overall architecture of this CRUD can still be reproduced with a
 | 
			
		||||
# newer version of sqlx, and the version will be updated in the future.
 | 
			
		||||
sqlx = { version = "0.6.2", features = ["sqlite", "runtime-actix-rustls", "chrono"] }
 | 
			
		||||
serde = {version="1.0", features = ["derive"]}
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
uuid = { version = "1.2", features = ["serde", "v4"] }
 | 
			
		||||
dotenv = "*"
 | 
			
		||||
env_logger = "0.10"
 | 
			
		||||
log = "*"
 | 
			
		||||
chrono = {version="*", features = ["serde"]}
 | 
			
		||||
							
								
								
									
										24
									
								
								terminwahl_back/migrations/20230127154843_initial.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								terminwahl_back/migrations/20230127154843_initial.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
-- Add migration script here
 | 
			
		||||
CREATE TABLE teachers (
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    ansprache TEXT NOT NULL,
 | 
			
		||||
    last_name TEXT NOT NULL,
 | 
			
		||||
    subject_id INTEGER NOT NULL,
 | 
			
		||||
    FOREIGN KEY(subject_id) REFERENCES subjects(id)
 | 
			
		||||
);
 | 
			
		||||
CREATE TABLE subjects (
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    name TEXT NOT NULL
 | 
			
		||||
);
 | 
			
		||||
CREATE TABLE appointments (
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    teacher_id INTEGER NOT NULL,
 | 
			
		||||
    slot_id INTEGER NOT NULL,
 | 
			
		||||
    FOREIGN KEY(teacher_id) REFERENCES teachers(id),
 | 
			
		||||
    FOREIGN KEY(slot_id) REFERENCES appointment_slots(id)
 | 
			
		||||
);
 | 
			
		||||
CREATE TABLE appointment_slots (
 | 
			
		||||
    id INTEGER PRIMARY KEY,
 | 
			
		||||
    start_time DATETIME NOT NULL,
 | 
			
		||||
    end_time DATETIME NOT NULL
 | 
			
		||||
);
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
-- Add migration script here
 | 
			
		||||
INSERT INTO subjects (name)
 | 
			
		||||
VALUES ('Mathematik'),
 | 
			
		||||
    ('Deutsch'),
 | 
			
		||||
    ('Biologie/Chemie'),
 | 
			
		||||
    ('Kunst');
 | 
			
		||||
INSERT INTO teachers (ansprache, last_name, subject_id)
 | 
			
		||||
VALUES ('Frau', 'Dessauer-Reiners', 2),
 | 
			
		||||
    ('Frau', 'Grimrath', 3),
 | 
			
		||||
    ('Frau', 'Junghans', 2),
 | 
			
		||||
    ('Frau', 'Schilling', 1),
 | 
			
		||||
    ('Herr', 'Kühl', 1),
 | 
			
		||||
    ('Frau', 'Küng', 4),
 | 
			
		||||
    ('Frau', 'Nöhring', 2),
 | 
			
		||||
    ('Herr', 'Rudolf', 2),
 | 
			
		||||
    ('Herr', 'Schad', 3),
 | 
			
		||||
    ('Herr', 'Scholz', 2),
 | 
			
		||||
    ('Frau', 'Stanutiu', 1);
 | 
			
		||||
							
								
								
									
										2
									
								
								terminwahl_back/src/api.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								terminwahl_back/src/api.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
pub mod errors;
 | 
			
		||||
pub mod read;
 | 
			
		||||
							
								
								
									
										26
									
								
								terminwahl_back/src/api/errors.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								terminwahl_back/src/api/errors.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
use actix_files::NamedFile;
 | 
			
		||||
use actix_web::{dev, middleware::ErrorHandlerResponse, Result};
 | 
			
		||||
 | 
			
		||||
pub fn bad_request<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
 | 
			
		||||
    let new_resp = NamedFile::open("static/errors/400.html")?
 | 
			
		||||
        .set_status_code(res.status())
 | 
			
		||||
        .into_response(res.request())
 | 
			
		||||
        .map_into_right_body();
 | 
			
		||||
    Ok(ErrorHandlerResponse::Response(res.into_response(new_resp)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn not_found<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
 | 
			
		||||
    let new_resp = NamedFile::open("static/errors/404.html")?
 | 
			
		||||
        .set_status_code(res.status())
 | 
			
		||||
        .into_response(res.request())
 | 
			
		||||
        .map_into_right_body();
 | 
			
		||||
    Ok(ErrorHandlerResponse::Response(res.into_response(new_resp)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn internal_server_error<B>(res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
 | 
			
		||||
    let new_resp = NamedFile::open("static/errors/500.html")?
 | 
			
		||||
        .set_status_code(res.status())
 | 
			
		||||
        .into_response(res.request())
 | 
			
		||||
        .map_into_right_body();
 | 
			
		||||
    Ok(ErrorHandlerResponse::Response(res.into_response(new_resp)))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								terminwahl_back/src/api/read.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								terminwahl_back/src/api/read.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use actix_web::{error, web, Error, HttpResponse};
 | 
			
		||||
 | 
			
		||||
use crate::db::{self, Pool};
 | 
			
		||||
 | 
			
		||||
pub async fn get_teachers_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
 | 
			
		||||
    let tasks = db::read::get_teachers(&pool)
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(error::ErrorInternalServerError)?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(tasks))
 | 
			
		||||
}
 | 
			
		||||
pub async fn get_subjects_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
 | 
			
		||||
    let tasks = db::read::get_subjects(&pool)
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(error::ErrorInternalServerError)?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(tasks))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_free_slots_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
 | 
			
		||||
    let tasks = db::read::get_free_slots(&pool)
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(error::ErrorInternalServerError)?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(tasks))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								terminwahl_back/src/bin/generate_slots.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								terminwahl_back/src/bin/generate_slots.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
 | 
			
		||||
use dotenv::dotenv;
 | 
			
		||||
use std::env;
 | 
			
		||||
use terminwahl_back::db;
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
    dotenv().ok();
 | 
			
		||||
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
 | 
			
		||||
 | 
			
		||||
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
 | 
			
		||||
    let pool = db::init_pool(&database_url)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Failed to create pool");
 | 
			
		||||
    let duration = chrono::Duration::minutes(15);
 | 
			
		||||
    let date = NaiveDate::from_ymd_opt(2023, 2, 28).unwrap();
 | 
			
		||||
    let time = NaiveTime::from_hms_opt(14, 30, 0).unwrap();
 | 
			
		||||
    let mut start = NaiveDateTime::new(date, time);
 | 
			
		||||
    for _ in 0..4 {
 | 
			
		||||
        let end = start + duration;
 | 
			
		||||
        sqlx::query("INSERT INTO appointment_slots (start_time, end_time) VALUES (?, ?)")
 | 
			
		||||
            .bind(start)
 | 
			
		||||
            .bind(end)
 | 
			
		||||
            .execute(&pool)
 | 
			
		||||
            .await
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        start = end;
 | 
			
		||||
    }
 | 
			
		||||
    start += duration;
 | 
			
		||||
    for _ in 0..4 {
 | 
			
		||||
        let end = start + duration;
 | 
			
		||||
        sqlx::query("INSERT INTO appointment_slots (start_time, end_time) VALUES (?, ?)")
 | 
			
		||||
            .bind(start)
 | 
			
		||||
            .bind(end)
 | 
			
		||||
            .execute(&pool)
 | 
			
		||||
            .await
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        start = end;
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								terminwahl_back/src/db.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								terminwahl_back/src/db.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
pub mod read;
 | 
			
		||||
pub mod write;
 | 
			
		||||
 | 
			
		||||
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
 | 
			
		||||
 | 
			
		||||
pub async fn init_pool(database_url: &str) -> Result<SqlitePool, sqlx::Error> {
 | 
			
		||||
    SqlitePoolOptions::new().connect(database_url).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type Pool = SqlitePool;
 | 
			
		||||
							
								
								
									
										42
									
								
								terminwahl_back/src/db/read.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								terminwahl_back/src/db/read.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
use sqlx::query_as;
 | 
			
		||||
 | 
			
		||||
use crate::types::{AppointmentSlot, AppointmentSlots, Subject, Subjects, Teacher, Teachers};
 | 
			
		||||
 | 
			
		||||
use super::Pool;
 | 
			
		||||
 | 
			
		||||
pub async fn get_teachers(db: &Pool) -> Result<Teachers, sqlx::Error> {
 | 
			
		||||
    query_as!(
 | 
			
		||||
        Teacher,
 | 
			
		||||
        r#"
 | 
			
		||||
        SELECT *
 | 
			
		||||
        FROM `teachers`"#,
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(&*db)
 | 
			
		||||
    .await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_subjects(db: &Pool) -> Result<Subjects, sqlx::Error> {
 | 
			
		||||
    query_as!(
 | 
			
		||||
        Subject,
 | 
			
		||||
        r#"
 | 
			
		||||
        SELECT *
 | 
			
		||||
        FROM `subjects`"#,
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(&*db)
 | 
			
		||||
    .await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_free_slots(db: &Pool) -> Result<AppointmentSlots, sqlx::Error> {
 | 
			
		||||
    match query_as!(
 | 
			
		||||
        AppointmentSlot,
 | 
			
		||||
        r#"
 | 
			
		||||
        SELECT *
 | 
			
		||||
        FROM `appointment_slots`"#,
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(&*db)
 | 
			
		||||
    .await
 | 
			
		||||
    {
 | 
			
		||||
        Ok(elems) => Ok(elems.into_iter().map(|e| (e.id, e)).collect()),
 | 
			
		||||
        Err(e) => Err(e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								terminwahl_back/src/db/write.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								terminwahl_back/src/db/write.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								terminwahl_back/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								terminwahl_back/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
pub mod api;
 | 
			
		||||
pub mod db;
 | 
			
		||||
pub mod types;
 | 
			
		||||
							
								
								
									
										62
									
								
								terminwahl_back/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								terminwahl_back/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
use actix_files::Files;
 | 
			
		||||
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
 | 
			
		||||
use actix_web::{
 | 
			
		||||
    cookie::Key,
 | 
			
		||||
    http,
 | 
			
		||||
    middleware::{ErrorHandlers, Logger},
 | 
			
		||||
    web, App, HttpServer,
 | 
			
		||||
};
 | 
			
		||||
use dotenv::dotenv;
 | 
			
		||||
use std::{
 | 
			
		||||
    env,
 | 
			
		||||
    sync::{Arc, Mutex},
 | 
			
		||||
};
 | 
			
		||||
use terminwahl_back::{api, db};
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
    dotenv().ok();
 | 
			
		||||
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
 | 
			
		||||
 | 
			
		||||
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
 | 
			
		||||
    let pool = db::init_pool(&database_url)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Failed to create pool");
 | 
			
		||||
 | 
			
		||||
    log::info!("starting HTTP server at http://localhost:8080");
 | 
			
		||||
 | 
			
		||||
    HttpServer::new(move || {
 | 
			
		||||
        log::debug!("Constructing the App");
 | 
			
		||||
 | 
			
		||||
        let session_store = SessionMiddleware::new(CookieSessionStore::default(), Key::generate());
 | 
			
		||||
 | 
			
		||||
        let error_handlers = ErrorHandlers::new()
 | 
			
		||||
            .handler(
 | 
			
		||||
                http::StatusCode::INTERNAL_SERVER_ERROR,
 | 
			
		||||
                api::errors::internal_server_error,
 | 
			
		||||
            )
 | 
			
		||||
            .handler(http::StatusCode::BAD_REQUEST, api::errors::bad_request)
 | 
			
		||||
            .handler(http::StatusCode::NOT_FOUND, api::errors::not_found);
 | 
			
		||||
 | 
			
		||||
        App::new()
 | 
			
		||||
            .app_data(web::Data::new(pool.clone()))
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .wrap(session_store)
 | 
			
		||||
            .wrap(error_handlers)
 | 
			
		||||
            .service(
 | 
			
		||||
                web::resource("/get/teachers").route(web::get().to(api::read::get_teachers_json)),
 | 
			
		||||
            )
 | 
			
		||||
            .service(
 | 
			
		||||
                web::resource("/get/subjects").route(web::get().to(api::read::get_subjects_json)),
 | 
			
		||||
            )
 | 
			
		||||
            .service(
 | 
			
		||||
                web::resource("/get/free_slots")
 | 
			
		||||
                    .route(web::get().to(api::read::get_free_slots_json)),
 | 
			
		||||
            )
 | 
			
		||||
            .service(Files::new("/", "./terminwahl_front/dist/").index_file("index.html"))
 | 
			
		||||
    })
 | 
			
		||||
    .bind(("127.0.0.1", 8080))?
 | 
			
		||||
    .workers(2)
 | 
			
		||||
    .run()
 | 
			
		||||
    .await
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								terminwahl_back/src/types.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								terminwahl_back/src/types.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use chrono::{DateTime, Local, NaiveDateTime};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
pub type IdType = i64;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct Teacher {
 | 
			
		||||
    pub id: IdType,
 | 
			
		||||
    pub ansprache: String,
 | 
			
		||||
    pub last_name: String,
 | 
			
		||||
    pub subject_id: IdType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type Teachers = Vec<Teacher>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct Subject {
 | 
			
		||||
    pub id: IdType,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
}
 | 
			
		||||
pub type Subjects = Vec<Subject>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct Appointment {
 | 
			
		||||
    pub id: IdType,
 | 
			
		||||
    pub teacher_id: IdType,
 | 
			
		||||
    pub slot_id: IdType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type AppointmentSlots = HashMap<IdType, AppointmentSlot>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize, Serialize)]
 | 
			
		||||
pub struct AppointmentSlot {
 | 
			
		||||
    pub id: IdType,
 | 
			
		||||
    pub start_time: NaiveDateTime,
 | 
			
		||||
    pub end_time: NaiveDateTime,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										427
									
								
								terminwahl_back/static/css/normalize.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								terminwahl_back/static/css/normalize.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,427 @@
 | 
			
		||||
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Set default font family to sans-serif.
 | 
			
		||||
 * 2. Prevent iOS text size adjust after orientation change, without disabling
 | 
			
		||||
 *    user zoom.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
html {
 | 
			
		||||
  font-family: sans-serif; /* 1 */
 | 
			
		||||
  -ms-text-size-adjust: 100%; /* 2 */
 | 
			
		||||
  -webkit-text-size-adjust: 100%; /* 2 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove default margin.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* HTML5 display definitions
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Correct `block` display not defined for any HTML5 element in IE 8/9.
 | 
			
		||||
 * Correct `block` display not defined for `details` or `summary` in IE 10/11
 | 
			
		||||
 * and Firefox.
 | 
			
		||||
 * Correct `block` display not defined for `main` in IE 11.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
article,
 | 
			
		||||
aside,
 | 
			
		||||
details,
 | 
			
		||||
figcaption,
 | 
			
		||||
figure,
 | 
			
		||||
footer,
 | 
			
		||||
header,
 | 
			
		||||
hgroup,
 | 
			
		||||
main,
 | 
			
		||||
menu,
 | 
			
		||||
nav,
 | 
			
		||||
section,
 | 
			
		||||
summary {
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Correct `inline-block` display not defined in IE 8/9.
 | 
			
		||||
 * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
audio,
 | 
			
		||||
canvas,
 | 
			
		||||
progress,
 | 
			
		||||
video {
 | 
			
		||||
  display: inline-block; /* 1 */
 | 
			
		||||
  vertical-align: baseline; /* 2 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Prevent modern browsers from displaying `audio` without controls.
 | 
			
		||||
 * Remove excess height in iOS 5 devices.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
audio:not([controls]) {
 | 
			
		||||
  display: none;
 | 
			
		||||
  height: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address `[hidden]` styling not present in IE 8/9/10.
 | 
			
		||||
 * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
[hidden],
 | 
			
		||||
template {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Links
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove the gray background color from active links in IE 10.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
a {
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Improve readability when focused and also mouse hovered in all browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
a:active,
 | 
			
		||||
a:hover {
 | 
			
		||||
  outline: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Text-level semantics
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
abbr[title] {
 | 
			
		||||
  border-bottom: 1px dotted;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
b,
 | 
			
		||||
strong {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address styling not present in Safari and Chrome.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
dfn {
 | 
			
		||||
  font-style: italic;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address variable `h1` font-size and margin within `section` and `article`
 | 
			
		||||
 * contexts in Firefox 4+, Safari, and Chrome.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
  font-size: 2em;
 | 
			
		||||
  margin: 0.67em 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address styling not present in IE 8/9.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
mark {
 | 
			
		||||
  background: #ff0;
 | 
			
		||||
  color: #000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address inconsistent and variable font size in all browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
small {
 | 
			
		||||
  font-size: 80%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
sub,
 | 
			
		||||
sup {
 | 
			
		||||
  font-size: 75%;
 | 
			
		||||
  line-height: 0;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  vertical-align: baseline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sup {
 | 
			
		||||
  top: -0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub {
 | 
			
		||||
  bottom: -0.25em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Embedded content
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove border when inside `a` element in IE 8/9/10.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
img {
 | 
			
		||||
  border: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Correct overflow not hidden in IE 9/10/11.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
svg:not(:root) {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grouping content
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address margin not present in IE 8/9 and Safari.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
figure {
 | 
			
		||||
  margin: 1em 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address differences between Firefox and other browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
hr {
 | 
			
		||||
  -moz-box-sizing: content-box;
 | 
			
		||||
  box-sizing: content-box;
 | 
			
		||||
  height: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Contain overflow in all browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
pre {
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address odd `em`-unit font size rendering in all browsers.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
code,
 | 
			
		||||
kbd,
 | 
			
		||||
pre,
 | 
			
		||||
samp {
 | 
			
		||||
  font-family: monospace, monospace;
 | 
			
		||||
  font-size: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Forms
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Known limitation: by default, Chrome and Safari on OS X allow very limited
 | 
			
		||||
 * styling of `select`, unless a `border` property is set.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Correct color not being inherited.
 | 
			
		||||
 *    Known issue: affects color of disabled elements.
 | 
			
		||||
 * 2. Correct font properties not being inherited.
 | 
			
		||||
 * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button,
 | 
			
		||||
input,
 | 
			
		||||
optgroup,
 | 
			
		||||
select,
 | 
			
		||||
textarea {
 | 
			
		||||
  color: inherit; /* 1 */
 | 
			
		||||
  font: inherit; /* 2 */
 | 
			
		||||
  margin: 0; /* 3 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address `overflow` set to `hidden` in IE 8/9/10/11.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
  overflow: visible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address inconsistent `text-transform` inheritance for `button` and `select`.
 | 
			
		||||
 * All other form control elements do not inherit `text-transform` values.
 | 
			
		||||
 * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
 | 
			
		||||
 * Correct `select` style inheritance in Firefox.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button,
 | 
			
		||||
select {
 | 
			
		||||
  text-transform: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
 | 
			
		||||
 *    and `video` controls.
 | 
			
		||||
 * 2. Correct inability to style clickable `input` types in iOS.
 | 
			
		||||
 * 3. Improve usability and consistency of cursor style between image-type
 | 
			
		||||
 *    `input` and others.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button,
 | 
			
		||||
html input[type="button"], /* 1 */
 | 
			
		||||
input[type="reset"],
 | 
			
		||||
input[type="submit"] {
 | 
			
		||||
  -webkit-appearance: button; /* 2 */
 | 
			
		||||
  cursor: pointer; /* 3 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Re-set default cursor for disabled elements.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button[disabled],
 | 
			
		||||
html input[disabled] {
 | 
			
		||||
  cursor: default;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove inner padding and border in Firefox 4+.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
button::-moz-focus-inner,
 | 
			
		||||
input::-moz-focus-inner {
 | 
			
		||||
  border: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Address Firefox 4+ setting `line-height` on `input` using `!important` in
 | 
			
		||||
 * the UA stylesheet.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
input {
 | 
			
		||||
  line-height: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * It's recommended that you don't attempt to style these elements.
 | 
			
		||||
 * Firefox's implementation doesn't respect box-sizing, padding, or width.
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Address box sizing set to `content-box` in IE 8/9/10.
 | 
			
		||||
 * 2. Remove excess padding in IE 8/9/10.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
input[type="checkbox"],
 | 
			
		||||
input[type="radio"] {
 | 
			
		||||
  box-sizing: border-box; /* 1 */
 | 
			
		||||
  padding: 0; /* 2 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fix the cursor style for Chrome's increment/decrement buttons. For certain
 | 
			
		||||
 * `font-size` values of the `input`, it causes the cursor style of the
 | 
			
		||||
 * decrement button to change from `default` to `text`.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
input[type="number"]::-webkit-inner-spin-button,
 | 
			
		||||
input[type="number"]::-webkit-outer-spin-button {
 | 
			
		||||
  height: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
 | 
			
		||||
 * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
 | 
			
		||||
 *    (include `-moz` to future-proof).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
input[type="search"] {
 | 
			
		||||
  -webkit-appearance: textfield; /* 1 */
 | 
			
		||||
  -moz-box-sizing: content-box;
 | 
			
		||||
  -webkit-box-sizing: content-box; /* 2 */
 | 
			
		||||
  box-sizing: content-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove inner padding and search cancel button in Safari and Chrome on OS X.
 | 
			
		||||
 * Safari (but not Chrome) clips the cancel button when the search input has
 | 
			
		||||
 * padding (and `textfield` appearance).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
input[type="search"]::-webkit-search-cancel-button,
 | 
			
		||||
input[type="search"]::-webkit-search-decoration {
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Define consistent border, margin, and padding.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
fieldset {
 | 
			
		||||
  border: 1px solid #c0c0c0;
 | 
			
		||||
  margin: 0 2px;
 | 
			
		||||
  padding: 0.35em 0.625em 0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 1. Correct `color` not being inherited in IE 8/9/10/11.
 | 
			
		||||
 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
legend {
 | 
			
		||||
  border: 0; /* 1 */
 | 
			
		||||
  padding: 0; /* 2 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove default vertical scrollbar in IE 8/9/10/11.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
textarea {
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Don't inherit the `font-weight` (applied by a rule above).
 | 
			
		||||
 * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
optgroup {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Tables
 | 
			
		||||
   ========================================================================== */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remove most spacing between table cells.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
table {
 | 
			
		||||
  border-collapse: collapse;
 | 
			
		||||
  border-spacing: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td,
 | 
			
		||||
th {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										421
									
								
								terminwahl_back/static/css/skeleton.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										421
									
								
								terminwahl_back/static/css/skeleton.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,421 @@
 | 
			
		||||
/*
 | 
			
		||||
* Skeleton V2.0.4
 | 
			
		||||
* Copyright 2014, Dave Gamache
 | 
			
		||||
* www.getskeleton.com
 | 
			
		||||
* Free to use under the MIT license.
 | 
			
		||||
* http://www.opensource.org/licenses/mit-license.php
 | 
			
		||||
* 12/29/2014
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Table of contents
 | 
			
		||||
––––––––––––––––––––––––––––––––––––––––––––––––––
 | 
			
		||||
- Grid
 | 
			
		||||
- Base Styles
 | 
			
		||||
- Typography
 | 
			
		||||
- Links
 | 
			
		||||
- Buttons
 | 
			
		||||
- Forms
 | 
			
		||||
- Lists
 | 
			
		||||
- Code
 | 
			
		||||
- Tables
 | 
			
		||||
- Spacing
 | 
			
		||||
- Utilities
 | 
			
		||||
- Clearing
 | 
			
		||||
- Media Queries
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Grid
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
.container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-width: 960px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  padding: 0 20px;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
.column,
 | 
			
		||||
.columns {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  float: left;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
 | 
			
		||||
/* For devices larger than 400px */
 | 
			
		||||
@media (min-width: 400px) {
 | 
			
		||||
  .container {
 | 
			
		||||
    width: 85%;
 | 
			
		||||
    padding: 0; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For devices larger than 550px */
 | 
			
		||||
@media (min-width: 550px) {
 | 
			
		||||
  .container {
 | 
			
		||||
    width: 80%; }
 | 
			
		||||
  .column,
 | 
			
		||||
  .columns {
 | 
			
		||||
    margin-left: 4%; }
 | 
			
		||||
  .column:first-child,
 | 
			
		||||
  .columns:first-child {
 | 
			
		||||
    margin-left: 0; }
 | 
			
		||||
 | 
			
		||||
  .one.column,
 | 
			
		||||
  .one.columns                    { width: 4.66666666667%; }
 | 
			
		||||
  .two.columns                    { width: 13.3333333333%; }
 | 
			
		||||
  .three.columns                  { width: 22%;            }
 | 
			
		||||
  .four.columns                   { width: 30.6666666667%; }
 | 
			
		||||
  .five.columns                   { width: 39.3333333333%; }
 | 
			
		||||
  .six.columns                    { width: 48%;            }
 | 
			
		||||
  .seven.columns                  { width: 56.6666666667%; }
 | 
			
		||||
  .eight.columns                  { width: 65.3333333333%; }
 | 
			
		||||
  .nine.columns                   { width: 74.0%;          }
 | 
			
		||||
  .ten.columns                    { width: 82.6666666667%; }
 | 
			
		||||
  .eleven.columns                 { width: 91.3333333333%; }
 | 
			
		||||
  .twelve.columns                 { width: 100%; margin-left: 0; }
 | 
			
		||||
 | 
			
		||||
  .one-third.column               { width: 30.6666666667%; }
 | 
			
		||||
  .two-thirds.column              { width: 65.3333333333%; }
 | 
			
		||||
 | 
			
		||||
  .one-half.column                { width: 48%; }
 | 
			
		||||
 | 
			
		||||
  /* Offsets */
 | 
			
		||||
  .offset-by-one.column,
 | 
			
		||||
  .offset-by-one.columns          { margin-left: 8.66666666667%; }
 | 
			
		||||
  .offset-by-two.column,
 | 
			
		||||
  .offset-by-two.columns          { margin-left: 17.3333333333%; }
 | 
			
		||||
  .offset-by-three.column,
 | 
			
		||||
  .offset-by-three.columns        { margin-left: 26%;            }
 | 
			
		||||
  .offset-by-four.column,
 | 
			
		||||
  .offset-by-four.columns         { margin-left: 34.6666666667%; }
 | 
			
		||||
  .offset-by-five.column,
 | 
			
		||||
  .offset-by-five.columns         { margin-left: 43.3333333333%; }
 | 
			
		||||
  .offset-by-six.column,
 | 
			
		||||
  .offset-by-six.columns          { margin-left: 52%;            }
 | 
			
		||||
  .offset-by-seven.column,
 | 
			
		||||
  .offset-by-seven.columns        { margin-left: 60.6666666667%; }
 | 
			
		||||
  .offset-by-eight.column,
 | 
			
		||||
  .offset-by-eight.columns        { margin-left: 69.3333333333%; }
 | 
			
		||||
  .offset-by-nine.column,
 | 
			
		||||
  .offset-by-nine.columns         { margin-left: 78.0%;          }
 | 
			
		||||
  .offset-by-ten.column,
 | 
			
		||||
  .offset-by-ten.columns          { margin-left: 86.6666666667%; }
 | 
			
		||||
  .offset-by-eleven.column,
 | 
			
		||||
  .offset-by-eleven.columns       { margin-left: 95.3333333333%; }
 | 
			
		||||
 | 
			
		||||
  .offset-by-one-third.column,
 | 
			
		||||
  .offset-by-one-third.columns    { margin-left: 34.6666666667%; }
 | 
			
		||||
  .offset-by-two-thirds.column,
 | 
			
		||||
  .offset-by-two-thirds.columns   { margin-left: 69.3333333333%; }
 | 
			
		||||
 | 
			
		||||
  .offset-by-one-half.column,
 | 
			
		||||
  .offset-by-one-half.columns     { margin-left: 52%; }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Base Styles
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
/* NOTE
 | 
			
		||||
html is set to 62.5% so that all the REM measurements throughout Skeleton
 | 
			
		||||
are based on 10px sizing. So basically 1.5rem = 15px :) */
 | 
			
		||||
html {
 | 
			
		||||
  font-size: 62.5%; }
 | 
			
		||||
body {
 | 
			
		||||
  font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
  font-weight: 400;
 | 
			
		||||
  font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
 | 
			
		||||
  color: #222; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Typography
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
h1, h2, h3, h4, h5, h6 {
 | 
			
		||||
  margin-top: 0;
 | 
			
		||||
  margin-bottom: 2rem;
 | 
			
		||||
  font-weight: 300; }
 | 
			
		||||
h1 { font-size: 4.0rem; line-height: 1.2;  letter-spacing: -.1rem;}
 | 
			
		||||
h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
 | 
			
		||||
h3 { font-size: 3.0rem; line-height: 1.3;  letter-spacing: -.1rem; }
 | 
			
		||||
h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
 | 
			
		||||
h5 { font-size: 1.8rem; line-height: 1.5;  letter-spacing: -.05rem; }
 | 
			
		||||
h6 { font-size: 1.5rem; line-height: 1.6;  letter-spacing: 0; }
 | 
			
		||||
 | 
			
		||||
/* Larger than phablet */
 | 
			
		||||
@media (min-width: 550px) {
 | 
			
		||||
  h1 { font-size: 5.0rem; }
 | 
			
		||||
  h2 { font-size: 4.2rem; }
 | 
			
		||||
  h3 { font-size: 3.6rem; }
 | 
			
		||||
  h4 { font-size: 3.0rem; }
 | 
			
		||||
  h5 { font-size: 2.4rem; }
 | 
			
		||||
  h6 { font-size: 1.5rem; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
  margin-top: 0; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Links
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
a {
 | 
			
		||||
  color: #1EAEDB; }
 | 
			
		||||
a:hover {
 | 
			
		||||
  color: #0FA0CE; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Buttons
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
.button,
 | 
			
		||||
button,
 | 
			
		||||
input[type="submit"],
 | 
			
		||||
input[type="reset"],
 | 
			
		||||
input[type="button"] {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  height: 38px;
 | 
			
		||||
  padding: 0 30px;
 | 
			
		||||
  color: #555;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  font-size: 11px;
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
  line-height: 38px;
 | 
			
		||||
  letter-spacing: .1rem;
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  border: 1px solid #bbb;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
.button:hover,
 | 
			
		||||
button:hover,
 | 
			
		||||
input[type="submit"]:hover,
 | 
			
		||||
input[type="reset"]:hover,
 | 
			
		||||
input[type="button"]:hover,
 | 
			
		||||
.button:focus,
 | 
			
		||||
button:focus,
 | 
			
		||||
input[type="submit"]:focus,
 | 
			
		||||
input[type="reset"]:focus,
 | 
			
		||||
input[type="button"]:focus {
 | 
			
		||||
  color: #333;
 | 
			
		||||
  border-color: #888;
 | 
			
		||||
  outline: 0; }
 | 
			
		||||
.button.button-primary,
 | 
			
		||||
button.button-primary,
 | 
			
		||||
button.primary,
 | 
			
		||||
input[type="submit"].button-primary,
 | 
			
		||||
input[type="reset"].button-primary,
 | 
			
		||||
input[type="button"].button-primary {
 | 
			
		||||
  color: #FFF;
 | 
			
		||||
  background-color: #33C3F0;
 | 
			
		||||
  border-color: #33C3F0; }
 | 
			
		||||
.button.button-primary:hover,
 | 
			
		||||
button.button-primary:hover,
 | 
			
		||||
button.primary:hover,
 | 
			
		||||
input[type="submit"].button-primary:hover,
 | 
			
		||||
input[type="reset"].button-primary:hover,
 | 
			
		||||
input[type="button"].button-primary:hover,
 | 
			
		||||
.button.button-primary:focus,
 | 
			
		||||
button.button-primary:focus,
 | 
			
		||||
button.primary:focus,
 | 
			
		||||
input[type="submit"].button-primary:focus,
 | 
			
		||||
input[type="reset"].button-primary:focus,
 | 
			
		||||
input[type="button"].button-primary:focus {
 | 
			
		||||
  color: #FFF;
 | 
			
		||||
  background-color: #1EAEDB;
 | 
			
		||||
  border-color: #1EAEDB; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Forms
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
input[type="email"],
 | 
			
		||||
input[type="number"],
 | 
			
		||||
input[type="search"],
 | 
			
		||||
input[type="text"],
 | 
			
		||||
input[type="tel"],
 | 
			
		||||
input[type="url"],
 | 
			
		||||
input[type="password"],
 | 
			
		||||
textarea,
 | 
			
		||||
select {
 | 
			
		||||
  height: 38px;
 | 
			
		||||
  padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #D1D1D1;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
/* Removes awkward default styles on some inputs for iOS */
 | 
			
		||||
input[type="email"],
 | 
			
		||||
input[type="number"],
 | 
			
		||||
input[type="search"],
 | 
			
		||||
input[type="text"],
 | 
			
		||||
input[type="tel"],
 | 
			
		||||
input[type="url"],
 | 
			
		||||
input[type="password"],
 | 
			
		||||
textarea {
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
     -moz-appearance: none;
 | 
			
		||||
          appearance: none; }
 | 
			
		||||
textarea {
 | 
			
		||||
  min-height: 65px;
 | 
			
		||||
  padding-top: 6px;
 | 
			
		||||
  padding-bottom: 6px; }
 | 
			
		||||
input[type="email"]:focus,
 | 
			
		||||
input[type="number"]:focus,
 | 
			
		||||
input[type="search"]:focus,
 | 
			
		||||
input[type="text"]:focus,
 | 
			
		||||
input[type="tel"]:focus,
 | 
			
		||||
input[type="url"]:focus,
 | 
			
		||||
input[type="password"]:focus,
 | 
			
		||||
textarea:focus,
 | 
			
		||||
select:focus {
 | 
			
		||||
  border: 1px solid #33C3F0;
 | 
			
		||||
  outline: 0; }
 | 
			
		||||
label,
 | 
			
		||||
legend {
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin-bottom: .5rem;
 | 
			
		||||
  font-weight: 600; }
 | 
			
		||||
fieldset {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  border-width: 0; }
 | 
			
		||||
input[type="checkbox"],
 | 
			
		||||
input[type="radio"] {
 | 
			
		||||
  display: inline; }
 | 
			
		||||
label > .label-body {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-left: .5rem;
 | 
			
		||||
  font-weight: normal; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Lists
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
ul {
 | 
			
		||||
  list-style: circle inside; }
 | 
			
		||||
ol {
 | 
			
		||||
  list-style: decimal inside; }
 | 
			
		||||
ol, ul {
 | 
			
		||||
  padding-left: 0;
 | 
			
		||||
  margin-top: 0; }
 | 
			
		||||
ul ul,
 | 
			
		||||
ul ol,
 | 
			
		||||
ol ol,
 | 
			
		||||
ol ul {
 | 
			
		||||
  margin: 1.5rem 0 1.5rem 3rem;
 | 
			
		||||
  font-size: 90%; }
 | 
			
		||||
li {
 | 
			
		||||
  margin-bottom: 1rem; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Code
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
code {
 | 
			
		||||
  padding: .2rem .5rem;
 | 
			
		||||
  margin: 0 .2rem;
 | 
			
		||||
  font-size: 90%;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  background: #F1F1F1;
 | 
			
		||||
  border: 1px solid #E1E1E1;
 | 
			
		||||
  border-radius: 4px; }
 | 
			
		||||
pre > code {
 | 
			
		||||
  display: block;
 | 
			
		||||
  padding: 1rem 1.5rem;
 | 
			
		||||
  white-space: pre; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Tables
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
th,
 | 
			
		||||
td {
 | 
			
		||||
  padding: 12px 15px;
 | 
			
		||||
  text-align: left;
 | 
			
		||||
  border-bottom: 1px solid #E1E1E1; }
 | 
			
		||||
th:first-child,
 | 
			
		||||
td:first-child {
 | 
			
		||||
  padding-left: 0; }
 | 
			
		||||
th:last-child,
 | 
			
		||||
td:last-child {
 | 
			
		||||
  padding-right: 0; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Spacing
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
button,
 | 
			
		||||
.button {
 | 
			
		||||
  margin-bottom: 1rem; }
 | 
			
		||||
input,
 | 
			
		||||
textarea,
 | 
			
		||||
select,
 | 
			
		||||
fieldset {
 | 
			
		||||
  margin-bottom: 1.5rem; }
 | 
			
		||||
pre,
 | 
			
		||||
blockquote,
 | 
			
		||||
dl,
 | 
			
		||||
figure,
 | 
			
		||||
table,
 | 
			
		||||
p,
 | 
			
		||||
ul,
 | 
			
		||||
ol,
 | 
			
		||||
form {
 | 
			
		||||
  margin-bottom: 2.5rem; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Utilities
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
.u-full-width {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
.u-max-full-width {
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
  box-sizing: border-box; }
 | 
			
		||||
.u-pull-right {
 | 
			
		||||
  float: right; }
 | 
			
		||||
.u-pull-left {
 | 
			
		||||
  float: left; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Misc
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
hr {
 | 
			
		||||
  margin-top: 3rem;
 | 
			
		||||
  margin-bottom: 3.5rem;
 | 
			
		||||
  border-width: 0;
 | 
			
		||||
  border-top: 1px solid #E1E1E1; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Clearing
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
 | 
			
		||||
/* Self Clearing Goodness */
 | 
			
		||||
.container:after,
 | 
			
		||||
.row:after,
 | 
			
		||||
.u-cf {
 | 
			
		||||
  content: "";
 | 
			
		||||
  display: table;
 | 
			
		||||
  clear: both; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Media Queries
 | 
			
		||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
 | 
			
		||||
/*
 | 
			
		||||
Note: The best way to structure the use of media queries is to create the queries
 | 
			
		||||
near the relevant code. For example, if you wanted to change the styles for buttons
 | 
			
		||||
on small devices, paste the mobile query code up in the buttons section and style it
 | 
			
		||||
there.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Larger than mobile */
 | 
			
		||||
@media (min-width: 400px) {}
 | 
			
		||||
 | 
			
		||||
/* Larger than phablet (also point when grid becomes active) */
 | 
			
		||||
@media (min-width: 550px) {}
 | 
			
		||||
 | 
			
		||||
/* Larger than tablet */
 | 
			
		||||
@media (min-width: 750px) {}
 | 
			
		||||
 | 
			
		||||
/* Larger than desktop */
 | 
			
		||||
@media (min-width: 1000px) {}
 | 
			
		||||
 | 
			
		||||
/* Larger than Desktop HD */
 | 
			
		||||
@media (min-width: 1200px) {}
 | 
			
		||||
							
								
								
									
										58
									
								
								terminwahl_back/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								terminwahl_back/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
.field-error {
 | 
			
		||||
  border: 1px solid #ff0000 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.field-error-msg {
 | 
			
		||||
  color: #ff0000;
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin: -10px 0 10px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.field-success {
 | 
			
		||||
  border: 1px solid #5AB953 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.field-success-msg {
 | 
			
		||||
  color: #5AB953;
 | 
			
		||||
  display: block;
 | 
			
		||||
  margin: -10px 0 10px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
span.completed {
 | 
			
		||||
    text-decoration: line-through;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form.inline {
 | 
			
		||||
  display: inline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form.link,
 | 
			
		||||
button.link {
 | 
			
		||||
  display: inline;
 | 
			
		||||
  color: #1EAEDB;
 | 
			
		||||
  border: none;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  background: none;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: 0 0 0 0;
 | 
			
		||||
  height: inherit;
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
  font-size: inherit;
 | 
			
		||||
  text-transform: none;
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
  line-height: inherit;
 | 
			
		||||
  letter-spacing: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form.link:hover, button.link:hover {
 | 
			
		||||
  color: #0FA0CE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button.small {
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  padding: 0 10px;
 | 
			
		||||
  font-size: 10px;
 | 
			
		||||
  line-height: 20px;
 | 
			
		||||
  margin: 0 2.5px;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								terminwahl_back/static/errors/400.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								terminwahl_back/static/errors/400.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
 | 
			
		||||
  <title>The server could not understand the request (400)</title>
 | 
			
		||||
 | 
			
		||||
  <link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/normalize.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/skeleton.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <h1>The server could not understand the request</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										22
									
								
								terminwahl_back/static/errors/404.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								terminwahl_back/static/errors/404.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
 | 
			
		||||
  <title>The page you were looking for doesn't exist (404)</title>
 | 
			
		||||
 | 
			
		||||
  <link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/normalize.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/skeleton.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <h1>The page you were looking for doesn't exist.</h1>
 | 
			
		||||
      <p>You may have mistyped the address or the page may have moved.</p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										24
									
								
								terminwahl_back/static/errors/500.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								terminwahl_back/static/errors/500.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
 | 
			
		||||
  <title>Ooops (500)</title>
 | 
			
		||||
 | 
			
		||||
  <link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/normalize.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/skeleton.css">
 | 
			
		||||
  <link rel="stylesheet" href="/static/css/style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <h1>Ooops ...</h1>
 | 
			
		||||
      <p>How embarrassing!</p>
 | 
			
		||||
      <p>Looks like something weird happened while processing your request.</p>
 | 
			
		||||
      <p>Please try again in a few moments.</p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										10
									
								
								terminwahl_front/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								terminwahl_front/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "terminwahl_front"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
 | 
			
		||||
yew = { version = "0.20.0", features = ["csr"] }
 | 
			
		||||
							
								
								
									
										9
									
								
								terminwahl_front/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								terminwahl_front/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <title>Yew App</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										24
									
								
								terminwahl_front/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								terminwahl_front/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
use yew::prelude::*;
 | 
			
		||||
 | 
			
		||||
#[function_component]
 | 
			
		||||
fn App() -> Html {
 | 
			
		||||
    let counter = use_state(|| 5);
 | 
			
		||||
    let onclick = {
 | 
			
		||||
        let counter = counter.clone();
 | 
			
		||||
        move |_| {
 | 
			
		||||
            let value = *counter + 1;
 | 
			
		||||
            counter.set(value);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    html! {
 | 
			
		||||
        <div>
 | 
			
		||||
            <button {onclick}>{ "+1" }</button>
 | 
			
		||||
            <p>{ *counter }</p>
 | 
			
		||||
        </div>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    yew::Renderer::<App>::new().render();
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user