feat(corp/rih): submit form data to backend
Change-Id: I0c74deea8debf9acbcf6eabf225969dbfe9cc34c Reviewed-on: https://cl.tvl.fyi/c/depot/+/8703 Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
20a6710365
commit
6fa6f3a7f4
4 changed files with 74 additions and 3 deletions
1
corp/rih/Cargo.lock
generated
1
corp/rih/Cargo.lock
generated
|
@ -1093,6 +1093,7 @@ dependencies = [
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"static_markdown",
|
"static_markdown",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yew",
|
"yew",
|
||||||
"yew-router",
|
"yew-router",
|
||||||
|
|
|
@ -16,6 +16,7 @@ serde_json = "1.0"
|
||||||
serde_urlencoded = "*" # pinned by yew
|
serde_urlencoded = "*" # pinned by yew
|
||||||
yew = { version = "0.20", features = ["csr"] }
|
yew = { version = "0.20", features = ["csr"] }
|
||||||
yew-router = "0.17"
|
yew-router = "0.17"
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
|
||||||
# needs to be in sync with nixpkgs
|
# needs to be in sync with nixpkgs
|
||||||
wasm-bindgen = "= 0.2.84"
|
wasm-bindgen = "= 0.2.84"
|
||||||
|
|
|
@ -99,6 +99,7 @@ html! {
|
||||||
<p>{"Let's get started with you telling us a bit about what kind of job you would like!"}</p>
|
<p>{"Let's get started with you telling us a bit about what kind of job you would like!"}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
if !self.submitted {
|
||||||
<div class="mx-auto col-6 border rounded-3 shadow">
|
<div class="mx-auto col-6 border rounded-3 shadow">
|
||||||
<form class="m-3">
|
<form class="m-3">
|
||||||
|
|
||||||
|
@ -172,12 +173,18 @@ html! {
|
||||||
<div id="captcha-container" class="smart-captcha mb-3" style="height: 100px" />
|
<div id="captcha-container" class="smart-captcha mb-3" style="height: 100px" />
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary"
|
<button type="submit" class="btn btn-primary"
|
||||||
disabled={!(self.record.is_complete() && self.captcha_token.is_some())}>
|
disabled={!(self.record.is_complete() && self.captcha_token.is_some())}
|
||||||
|
onclick={link.callback(|_| Msg::Submit)}>
|
||||||
{"Submit"}
|
{"Submit"}
|
||||||
</button>
|
</button>
|
||||||
<p class="pt-2"><i>{"This page is still under construction! Please reach out at contact@ if you have any questions."}</i></p>
|
<p class="pt-2"><i>{"This page is still under construction! Please reach out at contact@ if you have any questions."}</i></p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
} else {
|
||||||
|
<div class="mx-auto col-6 border rounded-3 shadow">
|
||||||
|
<p>{"Thank you for submitting your data! We will reach out to confirm your email address, and further if any matches are found. You can contact us at contact@russiaishiring.com with any questions you might have."}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,9 @@ use fuzzy_matcher::skim::SkimMatcherV2;
|
||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
use gloo::console;
|
use gloo::console;
|
||||||
use gloo::history::{BrowserHistory, History};
|
use gloo::history::{BrowserHistory, History};
|
||||||
|
use gloo::net::http;
|
||||||
use gloo::storage::{LocalStorage, Storage};
|
use gloo::storage::{LocalStorage, Storage};
|
||||||
|
use gloo::utils::format::JsValueSerdeExt;
|
||||||
use rand::seq::IteratorRandom;
|
use rand::seq::IteratorRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -24,6 +26,13 @@ const CAPTCHA_KEY: &'static str = "ysc1_K7iOi3FSmsyO8pZGu8Im2iQClCtPsVx7jSRyhyCV
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
const CAPTCHA_KEY: &'static str = "ysc1_a3LVlaDRDMwU8CLSZ0WKENTI2exyOxz5J2c6x28P5339d410";
|
const CAPTCHA_KEY: &'static str = "ysc1_a3LVlaDRDMwU8CLSZ0WKENTI2exyOxz5J2c6x28P5339d410";
|
||||||
|
|
||||||
|
// Form data is submitted to different endpoints in dev/prod.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
const SUBMIT_URL: &'static str = "http://localhost:9090/submit";
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
const SUBMIT_URL: &'static str = "https://api.russiaishiring.com/submit";
|
||||||
|
|
||||||
/// This code ends up being compiled for the native and for the
|
/// This code ends up being compiled for the native and for the
|
||||||
/// webassembly architectures during the build & test process.
|
/// webassembly architectures during the build & test process.
|
||||||
/// However, the `rust_iso3166` crate exposes a different API (!)
|
/// However, the `rust_iso3166` crate exposes a different API (!)
|
||||||
|
@ -83,7 +92,7 @@ enum Route {
|
||||||
|
|
||||||
/// Represents a single record as filled in by a user. This is the
|
/// Represents a single record as filled in by a user. This is the
|
||||||
/// primary data structure we want to populate and persist somewhere.
|
/// primary data structure we want to populate and persist somewhere.
|
||||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
||||||
struct Record {
|
struct Record {
|
||||||
// Personal information
|
// Personal information
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -127,6 +136,9 @@ struct App {
|
||||||
// Captcha callback closure which needs to be kept alive for the
|
// Captcha callback closure which needs to be kept alive for the
|
||||||
// lifecycle of the app.
|
// lifecycle of the app.
|
||||||
captcha_callback: Closure<dyn FnMut(String)>,
|
captcha_callback: Closure<dyn FnMut(String)>,
|
||||||
|
|
||||||
|
// Has data been submitted already by this user?
|
||||||
|
submitted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -146,7 +158,9 @@ enum Msg {
|
||||||
SetPosition(String),
|
SetPosition(String),
|
||||||
SetJobDetails(String),
|
SetJobDetails(String),
|
||||||
SetWorkBackground(String),
|
SetWorkBackground(String),
|
||||||
|
|
||||||
CaptchaSolved(String),
|
CaptchaSolved(String),
|
||||||
|
Submit,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callback handler for adding a technology.
|
/// Callback handler for adding a technology.
|
||||||
|
@ -306,6 +320,42 @@ fn render_technologies(link: &Scope<App>, technologies: &BTreeSet<String>) -> Ht
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Submit the collected data to the backend service.
|
||||||
|
async fn submit_data(captcha_token: &str, record: &Record) -> bool {
|
||||||
|
let response = http::Request::get(SUBMIT_URL)
|
||||||
|
.method(http::Method::POST)
|
||||||
|
.json(&serde_json::json!({
|
||||||
|
"captcha_token": captcha_token,
|
||||||
|
"record": record,
|
||||||
|
}))
|
||||||
|
.expect("serialising a serde_json::Value can not fail")
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// currently there is nothing we can actually do with the response
|
||||||
|
// here, but we should add some way to communicate back some
|
||||||
|
// server errors etc., even if the whole thing should be as
|
||||||
|
// forgiving as possible.
|
||||||
|
response.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the submit event, if all data was successfully collected.
|
||||||
|
fn handle_submit(app: &App, link: Scope<App>) -> Msg {
|
||||||
|
let token = app.captcha_token.as_ref().unwrap().clone();
|
||||||
|
let record = app.record.clone();
|
||||||
|
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
if !submit_data(&token, &record).await {
|
||||||
|
console::warn!("failed to submit data for some reason");
|
||||||
|
} else {
|
||||||
|
console::log!("submitted data successfully");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Msg::NoOp
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for App {
|
impl Component for App {
|
||||||
type Message = Msg;
|
type Message = Msg;
|
||||||
type Properties = ();
|
type Properties = ();
|
||||||
|
@ -323,10 +373,11 @@ impl Component for App {
|
||||||
link.send_message(Msg::CaptchaSolved(val));
|
link.send_message(Msg::CaptchaSolved(val));
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
submitted: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
console::log!("handling ", format!("{:?}", msg));
|
console::log!("handling ", format!("{:?}", msg));
|
||||||
let (state_change, view_change) = match msg {
|
let (state_change, view_change) = match msg {
|
||||||
Msg::NoOp => (false, false),
|
Msg::NoOp => (false, false),
|
||||||
|
@ -397,6 +448,17 @@ impl Component for App {
|
||||||
self.captcha_token = Some(token);
|
self.captcha_token = Some(token);
|
||||||
(false, true)
|
(false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Msg::Submit => {
|
||||||
|
if self.record.is_complete() && self.captcha_token.is_some() {
|
||||||
|
self.submitted = true;
|
||||||
|
handle_submit(self, ctx.link().clone());
|
||||||
|
(false, true)
|
||||||
|
} else {
|
||||||
|
console::warn!("submitted data, but form or captcha was not ready");
|
||||||
|
(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if state_change {
|
if state_change {
|
||||||
|
|
Loading…
Reference in a new issue