mirror of
https://github.com/transatoshi-mw/grinminer.net.git
synced 2025-10-07 06:12:46 +00:00
new site
This commit is contained in:
11
website/grinminer-form/Cargo.toml
Normal file
11
website/grinminer-form/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "grinminer-form"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rocket = { git = "https://github.com/rwf2/Rocket" }
|
||||
|
||||
[dependencies.rocket_dyn_templates]
|
||||
version = "0.1.0"
|
||||
features = ["handlebars", "tera", "minijinja"]
|
151
website/grinminer-form/index.html
Normal file
151
website/grinminer-form/index.html
Normal file
@@ -0,0 +1,151 @@
|
||||
** start of undefined **
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1 id="title" class="text-center">Grinminer.net application</h1>
|
||||
<p id="description" class="description text-center">
|
||||
Apply for a free or paid node/stratum depending on support level
|
||||
</p>
|
||||
</header>
|
||||
<form id="survey-form">
|
||||
<div class="form-group">
|
||||
<label id="name-label" for="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
id="name"
|
||||
class="form-control"
|
||||
placeholder="Enter your name"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label id="email-label" for="email">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
class="form-control"
|
||||
placeholder="Enter your Email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label id="number-label" for="number">Number of nodes<span class="clue">(max 4)</span></label>
|
||||
<input
|
||||
type="number"
|
||||
name="nodes"
|
||||
id="number"
|
||||
min="1"
|
||||
max="4"
|
||||
class="form-control"
|
||||
placeholder="node"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<p>Select an option</p>
|
||||
<select id="dropdown" name="stratum" class="form-control" required>
|
||||
<option disabled selected value>Select features</option>
|
||||
<option value="testnet">Testnet node</option>
|
||||
<option value="testnet">Testnet node+mwixnet</option>
|
||||
<option value="pruned">Pruned node</option>
|
||||
<option value="node+stratum">Pruned node+stratum</option>
|
||||
<option value="fullnode">Full node (10ツ)</option>
|
||||
<option value="fullnode+stratum">Full node+stratum(10ツ)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<p>Do you need setup help and ongoing support your node(s)?</p>
|
||||
<label>
|
||||
<input
|
||||
name="setup"
|
||||
value="no"
|
||||
type="radio"
|
||||
class="input-radio"
|
||||
checked
|
||||
/>No</label>
|
||||
<label>
|
||||
<input
|
||||
name="setup"
|
||||
value="yes"
|
||||
type="radio"
|
||||
class="input-radio"
|
||||
checked
|
||||
/>Yes (25ツ)</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<p>Do you agree to only use this VPS for Grin?</p>
|
||||
<select id="agreement" name="agreement" class="form-control" required>
|
||||
<option disabled selected value>Select an option</option>
|
||||
<option value="challenges">Yes</option>
|
||||
<option value="projects">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<p>What future features are you interested in?<span class="clue">(Check all that apply)</span></p>
|
||||
<label>
|
||||
<input
|
||||
name="prefer"
|
||||
value="firmware-update"
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
/>Updated firmware</label>
|
||||
<label>
|
||||
<input
|
||||
name="prefer"
|
||||
value="firmware-overclock"
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
/>Overclocked firmware</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
name="prefer"
|
||||
value="stratumv2"
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
/>Stratum V2</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
name="prefer"
|
||||
value="controlpanel"
|
||||
type="checkbox"
|
||||
class="input-checkbox"
|
||||
/>Mining control panel</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<p>Comments or suggestions?</p>
|
||||
<textarea
|
||||
id="comments"
|
||||
class="input-textarea"
|
||||
name="comment"
|
||||
placeholder="Enter your comments here..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" id="submit" class="submit-button">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
** end of undefined **
|
||||
|
||||
** start of undefined **
|
||||
|
||||
|
||||
|
||||
** end of undefined **
|
||||
|
97
website/grinminer-form/src/main.rs
Normal file
97
website/grinminer-form/src/main.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::time::Date;
|
||||
use rocket::http::{Status, ContentType};
|
||||
use rocket::form::{Form, Contextual, FromForm, FromFormField, Context};
|
||||
use rocket::fs::{FileServer, TempFile, relative};
|
||||
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
struct Password<'v> {
|
||||
#[field(validate = len(6..))]
|
||||
#[field(validate = eq(self.second))]
|
||||
#[allow(unused)]
|
||||
first: &'v str,
|
||||
#[allow(unused)]
|
||||
#[field(validate = eq(self.first))]
|
||||
second: &'v str,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromFormField)]
|
||||
enum Rights {
|
||||
Public,
|
||||
Reserved,
|
||||
Exclusive,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromFormField)]
|
||||
enum Category {
|
||||
Biology,
|
||||
Chemistry,
|
||||
Physics,
|
||||
#[field(value = "CS")]
|
||||
ComputerScience,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
#[allow(dead_code)]
|
||||
struct Submission<'v> {
|
||||
#[field(validate = len(1..))]
|
||||
title: &'v str,
|
||||
date: Date,
|
||||
#[field(validate = len(1..=250))]
|
||||
r#abstract: &'v str,
|
||||
#[field(validate = ext(ContentType::PDF))]
|
||||
file: TempFile<'v>,
|
||||
#[field(validate = len(1..))]
|
||||
category: Vec<Category>,
|
||||
rights: Rights,
|
||||
ready: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
#[allow(dead_code)]
|
||||
struct Account<'v> {
|
||||
#[field(validate = len(1..))]
|
||||
name: &'v str,
|
||||
password: Password<'v>,
|
||||
#[field(validate = contains('@').or_else(msg!("invalid email address")))]
|
||||
email: &'v str,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
#[allow(dead_code)]
|
||||
struct Submit<'v> {
|
||||
account: Account<'v>,
|
||||
submission: Submission<'v>,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> Template {
|
||||
Template::render("index", &Context::default())
|
||||
}
|
||||
|
||||
// NOTE: We use `Contextual` here because we want to collect all submitted form
|
||||
// fields to re-render forms with submitted values on error. If you have no such
|
||||
// need, do not use `Contextual`. Use the equivalent of `Form<Submit<'_>>`.
|
||||
#[post("/", data = "<form>")]
|
||||
fn submit<'r>(form: Form<Contextual<'r, Submit<'r>>>) -> (Status, Template) {
|
||||
let template = match form.value {
|
||||
Some(ref submission) => {
|
||||
println!("submission: {:#?}", submission);
|
||||
Template::render("success", &form.context)
|
||||
}
|
||||
None => Template::render("index", &form.context),
|
||||
};
|
||||
|
||||
(form.context.status(), template)
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
rocket::build()
|
||||
.mount("/", routes![index, submit])
|
||||
.attach(Template::fairing())
|
||||
.mount("/", FileServer::from(relative!("/static")))
|
||||
}
|
193
website/grinminer-form/src/tests.rs
Normal file
193
website/grinminer-form/src/tests.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use std::fmt;
|
||||
use super::{rocket, FormInput, FormOption};
|
||||
|
||||
use rocket::local::blocking::Client;
|
||||
use rocket::http::ContentType;
|
||||
|
||||
impl fmt::Display for FormOption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
FormOption::A => write!(f, "a"),
|
||||
FormOption::B => write!(f, "b"),
|
||||
FormOption::C => write!(f, "c"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_form_eq {
|
||||
($client:expr, $form_str:expr, $expected:expr) => {{
|
||||
let res = $client.post("/")
|
||||
.header(ContentType::Form)
|
||||
.body($form_str)
|
||||
.dispatch();
|
||||
|
||||
assert_eq!(res.into_string(), Some($expected));
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! assert_valid_form {
|
||||
($client:expr, $input:expr) => {{
|
||||
let f = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}",
|
||||
$input.checkbox, $input.number, $input.radio, $input.password,
|
||||
$input.text_area, $input.select);
|
||||
assert_form_eq!($client, &f, format!("{:?}", $input));
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! assert_valid_raw_form {
|
||||
($client:expr, $form_str:expr, $input:expr) => {{
|
||||
assert_form_eq!($client, $form_str, format!("{:?}", $input));
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_good_forms() {
|
||||
let client = Client::tracked(rocket()).unwrap();
|
||||
let mut input = FormInput {
|
||||
checkbox: true,
|
||||
number: 310,
|
||||
radio: FormOption::A,
|
||||
password: "beep".into(),
|
||||
text_area: "bop".to_string(),
|
||||
select: FormOption::B
|
||||
};
|
||||
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.checkbox = false;
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.number = 0;
|
||||
assert_valid_form!(&client, &input);
|
||||
input.number = 120;
|
||||
assert_valid_form!(&client, &input);
|
||||
input.number = 133;
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.radio = FormOption::B;
|
||||
assert_valid_form!(&client, &input);
|
||||
input.radio = FormOption::C;
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.password = "".into();
|
||||
assert_valid_form!(&client, &input);
|
||||
input.password = "----90138490285u2o3hndslkv".into();
|
||||
assert_valid_form!(&client, &input);
|
||||
input.password = "hi".into();
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.text_area = "".to_string();
|
||||
assert_valid_form!(&client, &input);
|
||||
input.text_area = "----90138490285u2o3hndslkv".to_string();
|
||||
assert_valid_form!(&client, &input);
|
||||
input.text_area = "hey".to_string();
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
input.select = FormOption::A;
|
||||
assert_valid_form!(&client, &input);
|
||||
input.select = FormOption::C;
|
||||
assert_valid_form!(&client, &input);
|
||||
|
||||
// checkbox need not be present; defaults to false; accepts 'on' and 'off'
|
||||
assert_valid_raw_form!(&client,
|
||||
"number=133&type=c&password=hi&textarea=hey&select=c",
|
||||
&input);
|
||||
|
||||
assert_valid_raw_form!(&client,
|
||||
"checkbox=off&number=133&type=c&password=hi&textarea=hey&select=c",
|
||||
&input);
|
||||
|
||||
input.checkbox = true;
|
||||
assert_valid_raw_form!(&client,
|
||||
"checkbox=on&number=133&type=c&password=hi&textarea=hey&select=c",
|
||||
&input);
|
||||
}
|
||||
|
||||
macro_rules! assert_invalid_form {
|
||||
($client:expr, $vals:expr) => {{
|
||||
let vals = $vals;
|
||||
let s = format!("checkbox={}&number={}&type={}&password={}&textarea={}&select={}",
|
||||
vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
|
||||
assert_form_eq!($client, &s, format!("Invalid form input: {}", s));
|
||||
*vals = ["true", "1", "a", "hi", "hey", "b"];
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! assert_invalid_raw_form {
|
||||
($client:expr, $form_str:expr) => {{
|
||||
assert_form_eq!($client, $form_str, format!("Invalid form input: {}", $form_str));
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_semantically_invalid_forms() {
|
||||
let client = Client::tracked(rocket()).unwrap();
|
||||
let mut form_vals = ["true", "1", "a", "hi", "hey", "b"];
|
||||
|
||||
form_vals[0] = "not true";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[0] = "bing";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[0] = "true0";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[0] = " false";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
|
||||
form_vals[1] = "-1";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[1] = "1e10";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[1] = "-1-1";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[1] = "NaN";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
|
||||
form_vals[2] = "A?";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[2] = " B";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[2] = "d";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[2] = "100";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[2] = "";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
|
||||
// password and textarea are always valid, so we skip them
|
||||
form_vals[5] = "A.";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[5] = "b ";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[5] = "d";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[5] = "-a";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
form_vals[5] = "";
|
||||
assert_invalid_form!(&client, &mut form_vals);
|
||||
|
||||
// now forms with missing fields
|
||||
assert_invalid_raw_form!(&client, "number=10&type=a&password=hi&textarea=hey");
|
||||
assert_invalid_raw_form!(&client, "number=10&radio=a&password=hi&textarea=hey&select=b");
|
||||
assert_invalid_raw_form!(&client, "number=10&password=hi&select=b");
|
||||
assert_invalid_raw_form!(&client, "number=10&select=b");
|
||||
assert_invalid_raw_form!(&client, "password=hi&select=b");
|
||||
assert_invalid_raw_form!(&client, "password=hi");
|
||||
assert_invalid_raw_form!(&client, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_structurally_invalid_forms() {
|
||||
let client = Client::tracked(rocket()).unwrap();
|
||||
assert_invalid_raw_form!(&client, "==&&&&&&==");
|
||||
assert_invalid_raw_form!(&client, "a&=b");
|
||||
assert_invalid_raw_form!(&client, "=");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_bad_utf8() {
|
||||
let client = Client::tracked(rocket()).unwrap();
|
||||
unsafe {
|
||||
let bad_str = std::str::from_utf8_unchecked(b"a=\xff");
|
||||
assert_form_eq!(&client, bad_str, "Form input was invalid UTF-8.".into());
|
||||
}
|
||||
}
|
1
website/grinminer-form/static/chota.min.css
vendored
Normal file
1
website/grinminer-form/static/chota.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
148
website/grinminer-form/templates/index.html.tera
Normal file
148
website/grinminer-form/templates/index.html.tera
Normal file
@@ -0,0 +1,148 @@
|
||||
{% import "macros" as m %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Rocket Form Example</title>
|
||||
<link rel="stylesheet" href="/chota.min.css">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Form Example</h1>
|
||||
|
||||
{% if errors | length > 0 %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<small class="text-error">
|
||||
error: {{ errors | length }} field{{ errors | length | pluralize }}
|
||||
failed to validate
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="/" method="post" enctype="multipart/form-data">
|
||||
<fieldset>
|
||||
<legend>About You</legend>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ m::input(label="Name", type="text", name="account.name") }}
|
||||
<!-- required -->
|
||||
</div>
|
||||
<div class="col">
|
||||
{{ m::input(label="Email Address", type="text", name="account.email") }}
|
||||
<!-- required pattern=".*@.*"/> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ m::input(label="Password", type="password", name="account.password.first") }}
|
||||
<!-- required minlength="6" value="" /> -->
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
{{
|
||||
m::input(label="Confirm Password",
|
||||
type="password",
|
||||
name="account.password.second")
|
||||
}}
|
||||
|
||||
<!-- required minlength="6" value="" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Metadata</legend>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ m::input(label="Title", type="text", name="submission.title") }}
|
||||
<!-- required -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ m::input(label="Publish Date", type="date", name="submission.date") }}
|
||||
<!-- <input type="date" name="submission.date" id="date" value="2020-12-26"> -->
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
{{
|
||||
m::select(
|
||||
label="Rights Assignment",
|
||||
name="submission.rights",
|
||||
options=["Public", "Reserved", "Exclusive"]
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label>Applicable Categories</label>
|
||||
<br />
|
||||
{{ m::checkbox(name="submission.category", label="Biology", value="Biology") }}
|
||||
<br />
|
||||
{{ m::checkbox(name="submission.category", label="Chemistry", value="Chemistry") }}
|
||||
<br />
|
||||
{{ m::checkbox(name="submission.category", label="Physics", value="Physics") }}
|
||||
<br />
|
||||
{{ m::checkbox(name="submission.category", label="CS", value="CS") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Contents</legend>
|
||||
|
||||
{{
|
||||
m::textarea(
|
||||
label="Abstract",
|
||||
name="submission.abstract",
|
||||
placeholder="Your abstract, max 250 characters...",
|
||||
max=250
|
||||
)
|
||||
}}
|
||||
|
||||
{{
|
||||
m::input(
|
||||
label="File to Upload (PDF, max 1MiB)",
|
||||
type="file",
|
||||
name="submission.file"
|
||||
)
|
||||
}}
|
||||
|
||||
<!-- <input type="file" name="submission.file" id="file" required accept=".pdf"> -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ m::checkbox(name="submission.ready", label="Submission is ready for review.") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<br />
|
||||
<input type="submit" value="Submit" class="is-full-width" />
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
63
website/grinminer-form/templates/macros.html.tera
Normal file
63
website/grinminer-form/templates/macros.html.tera
Normal file
@@ -0,0 +1,63 @@
|
||||
{% macro value_for(name) %}
|
||||
{%- if name in values -%}
|
||||
{{- values | get(key=name) | first -}}
|
||||
{%- endif -%}
|
||||
{% endmacro value_for %}
|
||||
|
||||
{% macro errors_for(name) %}
|
||||
{%- if name in errors -%}
|
||||
{% set field_errors = errors | get(key=name) %}
|
||||
{% for error in field_errors %}
|
||||
<p class="text-error is-marginless">{{ error.msg }}</p>
|
||||
{% endfor %}
|
||||
{%- endif -%}
|
||||
{% endmacro errors_for %}
|
||||
|
||||
{% macro input(type, label, name, value="") %}
|
||||
<label for="{{ name }}">{{ label }}</label>
|
||||
<input type="{{ type }}"
|
||||
name="{{ name }}"
|
||||
id="{{ name }}"
|
||||
value='{{ self::value_for(name=name) }}'
|
||||
{% if name in errors %} class="error" {% endif %}
|
||||
/>
|
||||
|
||||
{{ self::errors_for(name=name) }}
|
||||
{% endmacro input %}
|
||||
|
||||
{% macro checkbox(name, label, value="yes") %}
|
||||
<label {% if name in errors %} class="bd-error" {% endif %}>
|
||||
<input type="checkbox" name="{{ name }}" value={{ value }}
|
||||
{% if name in values %}
|
||||
{% set field_values = values | get(key=name) %}
|
||||
{% if field_values is containing(value) %}
|
||||
checked
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
>
|
||||
{{ label }}
|
||||
</label>
|
||||
{% endmacro checkbox %}
|
||||
|
||||
{% macro textarea(label, name, placeholder="", max=250) %}
|
||||
<label for="{{ name }}">{{ label }}</label>
|
||||
<textarea placeholder="{{ placeholder }}"
|
||||
name="{{ name }}" id="{{ name }}" rows="8" cols="40"
|
||||
{% if name in errors %} class="error" {% endif %}
|
||||
>
|
||||
{{- self::value_for(name=name) -}}
|
||||
</textarea>
|
||||
|
||||
{{ self::errors_for(name=name) }}
|
||||
{% endmacro textarea %}
|
||||
|
||||
{% macro select(label, name, options) %}
|
||||
<label for="{{ name }}">{{ label }}</label>
|
||||
<select name="{{ name }}" id="{{ name }}">
|
||||
{% for value in options %}
|
||||
<option value="{{ value }}"
|
||||
{% if self::value_for(name=name) == value %} selected {% endif %}
|
||||
>{{ value }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endmacro select %}
|
30
website/grinminer-form/templates/success.html.tera
Normal file
30
website/grinminer-form/templates/success.html.tera
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Rocket Form Example</title>
|
||||
<link rel="stylesheet" href="/chota.min.css">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Success!</h1>
|
||||
|
||||
<h3>Submission Data</h3>
|
||||
|
||||
<ul>
|
||||
{% for key, value in values %}
|
||||
<li><strong>{{ key }}</strong> - {{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<a href="/">< Submit Another</a>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user