From ad6fba4c79e8b5ab08e2f0db8bc4087f03151f7f Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Tue, 14 Jun 2022 15:04:17 -0400 Subject: Add salary transparency page (#492) * Move dhall data and types into `/dhall` folder * Reformat salary transparency data into Dhall * Wire up the old salary transparency page with a custom element * Wire up a new salary transparency page * Expose raw data as JSON * Make dhall types more portable * Remove gallery from the navbar * Make signal boost page point to the new data location * Add salary transparency page to the footer of the site * Add site update post for this Signed-off-by: Xe --- src/app/markdown.rs | 9 ++++++- src/app/mod.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 13 deletions(-) (limited to 'src/app') diff --git a/src/app/markdown.rs b/src/app/markdown.rs index f6ae342..d73a5c5 100644 --- a/src/app/markdown.rs +++ b/src/app/markdown.rs @@ -1,3 +1,4 @@ +use crate::app::Config; use crate::templates::Html; use color_eyre::eyre::{Result, WrapErr}; use comrak::nodes::{Ast, AstNode, NodeValue}; @@ -9,13 +10,14 @@ use comrak::{ use lazy_static::lazy_static; use lol_html::{element, html_content::ContentType, rewrite_str, RewriteStrSettings}; use std::cell::RefCell; +use std::sync::Arc; use url::Url; lazy_static! { static ref SYNTECT_ADAPTER: SyntectAdapter<'static> = SyntectAdapter::new("base16-mocha.dark"); } -pub fn render(inp: &str) -> Result { +pub fn render(cfg: Arc, inp: &str) -> Result { let mut options = ComrakOptions::default(); options.extension.autolink = true; @@ -99,6 +101,11 @@ pub fn render(inp: &str) -> Result { element!("xeblog-hero", |el| { let file = el.get_attribute("file").expect("wanted xeblog-hero to contain file"); el.replace(&crate::tmpl::xeblog_hero(file, el.get_attribute("prompt")).0, ContentType::Html); + Ok(()) + }), + element!("xeblog-salary-history", |el| { + el.replace(&crate::tmpl::xeblog_salary_history(cfg.clone()).0, ContentType::Html); + Ok(()) }) ], diff --git a/src/app/mod.rs b/src/app/mod.rs index 7125cf8..a12d1c6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,23 +1,73 @@ use crate::{post::Post, signalboost::Person}; -use color_eyre::eyre::Result; use chrono::prelude::*; -use serde::Deserialize; +use color_eyre::eyre::Result; +use maud::{html, Markup}; +use serde::{Deserialize, Serialize}; use std::{ + fmt::{self, Display}, fs, path::PathBuf, + sync::Arc, }; use tracing::{error, instrument}; pub mod markdown; pub mod poke; -#[derive(Clone, Deserialize)] +#[derive(Clone, Deserialize, Default)] pub struct Config { pub(crate) signalboost: Vec, #[serde(rename = "resumeFname")] pub(crate) resume_fname: PathBuf, #[serde(rename = "miToken")] pub(crate) mi_token: String, + #[serde(rename = "jobHistory")] + pub(crate) job_history: Vec, +} + +#[derive(Clone, Deserialize, Serialize, Default)] +pub struct Salary { + pub amount: i32, + pub per: String, + pub currency: String, +} + +impl Display for Salary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}${}/{}", self.currency, self.amount, self.per) + } +} + +#[derive(Clone, Deserialize, Serialize, Default)] +pub struct Job { + pub company: String, + pub title: String, + #[serde(rename = "startDate")] + pub start_date: String, + #[serde(rename = "endDate")] + pub end_date: Option, + #[serde(rename = "daysWorked")] + pub days_worked: Option, + #[serde(rename = "daysBetween")] + pub days_between: Option, + pub salary: Salary, + #[serde(rename = "leaveReason")] + pub leave_reason: Option, +} + +impl Job { + pub fn pay_history_row(&self) -> Markup { + html! { + tr { + td { (self.title) } + td { (self.start_date) } + td { (self.end_date.as_ref().unwrap_or(&"current".to_string())) } + td { (if self.days_worked.is_some() { self.days_worked.as_ref().unwrap().to_string() } else { "n/a".to_string() }) } + td { (self.salary) } + td { (self.leave_reason.as_ref().unwrap_or(&"n/a".to_string())) } + } + } + } } #[instrument] @@ -57,7 +107,7 @@ async fn patrons() -> Result> { pub const ICON: &'static str = "https://xeiaso.net/static/img/avatar.png"; pub struct State { - pub cfg: Config, + pub cfg: Arc, pub signalboost: Vec, pub resume: String, pub blog: Vec, @@ -71,14 +121,17 @@ pub struct State { } pub async fn init(cfg: PathBuf) -> Result { - let cfg: Config = serde_dhall::from_file(cfg).parse()?; + let cfg: Arc = Arc::new(serde_dhall::from_file(cfg).parse()?); let sb = cfg.signalboost.clone(); - let resume = fs::read_to_string(cfg.resume_fname.clone())?; - let resume: String = markdown::render(&resume)?; - let mi = mi::Client::new(cfg.mi_token.clone(), crate::APPLICATION_NAME.to_string())?; - let blog = crate::post::load("blog").await?; - let gallery = crate::post::load("gallery").await?; - let talks = crate::post::load("talks").await?; + let resume = fs::read_to_string(cfg.clone().resume_fname.clone())?; + let resume: String = markdown::render(cfg.clone(), &resume)?; + let mi = mi::Client::new( + cfg.clone().mi_token.clone(), + crate::APPLICATION_NAME.to_string(), + )?; + let blog = crate::post::load(cfg.clone(), "blog").await?; + let gallery = crate::post::load(cfg.clone(), "gallery").await?; + let talks = crate::post::load(cfg.clone(), "talks").await?; let mut everything: Vec = vec![]; { @@ -99,7 +152,7 @@ pub async fn init(cfg: PathBuf) -> Result { .filter(|p| today.num_days_from_ce() >= p.date.num_days_from_ce()) .take(5) .collect(); - + let mut jfb = jsonfeed::Feed::builder() .title("Xe's Blog") .description("My blog posts and rants about various technology things.") -- cgit v1.2.3