This commit is contained in:
Ondrej Vlach 2023-12-03 17:30:27 +01:00
parent 2b133a9f3f
commit 32a9ed081e
Signed by: ovlach
GPG Key ID: 4FF1A23B4914DE70
4 changed files with 97 additions and 11 deletions

View File

@ -1,8 +1,11 @@
[debug] [debug]
static_route = "http://localhost:8001" static_route = "http://localhost:8001"
cv_backend_path = "http://localhost:8002" cv_backend_path = "http://localhost:8002"
contact_path = "http://localhost:8004"
[default] [default]
static_route = "http://localhost:8001" static_route = "http://localhost:8001"
cv_backend_path = "http://localhost:8002" cv_backend_path = "http://localhost:8002"
contact_service = "http://localhost:8004"
pdf_service = "http://localhost:8003"
default_person_name = "ovlach" default_person_name = "ovlach"

View File

@ -3,7 +3,7 @@ use nanobyte_tera::{hosts::static_filter, l18n::translate_filter, date::{calcula
use ovlach_tera::entity::lang_entity; use ovlach_tera::entity::lang_entity;
use rocket::{*, fairing::AdHoc}; use rocket::{*, fairing::AdHoc};
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use ::serde::Deserialize; use ::serde::{Deserialize, Serialize};
use tools::tera::advanced_filter; use tools::tera::advanced_filter;
pub mod routes; pub mod routes;
@ -22,6 +22,13 @@ pub struct CVBackendConfig {
cv_backend_path: String, cv_backend_path: String,
} }
#[derive(Serialize, Deserialize, Debug)]
#[serde(crate = "rocket::serde")]
pub struct FrontendSVCConfig {
pub contact_service: String,
pub pdf_service: String,
}
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
pub struct DefaultPerson { pub struct DefaultPerson {
@ -55,6 +62,8 @@ pub fn rocket_builder() -> Rocket<Build> {
AdHoc::config::<CVBackendConfig>() AdHoc::config::<CVBackendConfig>()
).attach( ).attach(
AdHoc::config::<DefaultPerson>() AdHoc::config::<DefaultPerson>()
).attach(
AdHoc::config::<FrontendSVCConfig>()
).attach( ).attach(
nanobyte_opentelemetry::rocket::TracingFairing::ignite() nanobyte_opentelemetry::rocket::TracingFairing::ignite()
).mount("/", routes![ ).mount("/", routes![

View File

@ -8,27 +8,29 @@ use rocket::{get, State, response::Redirect, http::Status, futures::executor::en
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use serde::Serialize; use serde::Serialize;
use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage, DefaultPerson}; use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage, DefaultPerson, FrontendSVCConfig};
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct RootPage { struct RootPage {
static_host: String, static_host: String,
cv: CV, cv: CV,
download_cv_url: String,
lang: LanguageDescription, lang: LanguageDescription,
contact_svc: String,
pdf_download_url: String
} }
#[get("/<language>")] #[get("/<language>")]
pub async fn index(presentation_config: &State<PresentationConfig>, cv_config: &State<CVBackendConfig>, language: RequestLanguage, client: OtelReqwestClient, pub async fn index(presentation_config: &State<PresentationConfig>, cv_config: &State<CVBackendConfig>, language: RequestLanguage, client: OtelReqwestClient,
default_person: &State<DefaultPerson>, span: TracingSpan) -> Result<Template, Status> { default_person: &State<DefaultPerson>, frontend_svc: &State<FrontendSVCConfig>, span: TracingSpan) -> Result<Template, Status> {
let span = span.0.enter(); let span = span.0.enter();
let context = match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &default_person.default_person_name, &client.0).await { let context = match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &default_person.default_person_name, &client.0).await {
Ok(cv) => RootPage { Ok(cv) => RootPage {
static_host: presentation_config.static_route.clone(), static_host: presentation_config.static_route.clone(),
cv, cv,
download_cv_url: "FIXME!".to_string(),
lang: LanguageDescription::new(&language.language.as_str(), "ovlach_frontend"), lang: LanguageDescription::new(&language.language.as_str(), "ovlach_frontend"),
contact_svc: frontend_svc.inner().contact_service.clone(),
pdf_download_url: format!("{}/cv/{}/output.pdf", frontend_svc.inner().pdf_service.clone(), default_person.default_person_name)
}, },
Err(e) => { Err(e) => {
error!("Can't fetch CV data from backend {:?}", e); error!("Can't fetch CV data from backend {:?}", e);

View File

@ -50,7 +50,7 @@
</nav> </nav>
</div> </div>
<div class="d-print-none"> <div class="d-print-none">
<a class="btn btn-outline-light btn-lg shadow-sm mt-1 me-3" href="{{ download_cv_url }}" data-aos="fade-right" data-aos-delay="700">{{ "download-cv" | translate(lang=lang) }}</a> <a class="btn btn-outline-light btn-lg shadow-sm mt-1 me-3" href="{{ pdf_download_url }}" data-aos="fade-right" data-aos-delay="700">{{ "download-cv" | translate(lang=lang) }}</a>
<a class="btn btn-info btn-lg shadow-sm mt-1" href="#contact" data-aos="fade-left" data-aos-delay="700"> {{ "hire-me" | translate(lang=lang) }} </a></div> <a class="btn btn-info btn-lg shadow-sm mt-1" href="#contact" data-aos="fade-left" data-aos-delay="700"> {{ "hire-me" | translate(lang=lang) }} </a></div>
</div> </div>
</div> </div>
@ -132,6 +132,9 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% for skill in cv.skills | filter(attribute="techtype",value="Language") | advanced_filter(attribute="skill", include_null="only") %}
<span class="fw-bolder">{{ skill.name }}</span>{% if not loop.last %},{% endif %}
{% endfor %}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
{% for skill in cv.skills | filter(attribute="techtype",value="Technology") | advanced_filter(attribute="skill", include_null="none") %} {% for skill in cv.skills | filter(attribute="techtype",value="Technology") | advanced_filter(attribute="skill", include_null="none") %}
@ -145,6 +148,14 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% for skill in cv.skills | filter(attribute="techtype",value="Technology") | advanced_filter(attribute="skill", include_null="only") %}
<span class="fw-bolder">{{ skill.name }}</span>{% if not loop.last %},{% endif %}
{% endfor %}
</div>
</div>
<h2 class="h2 fw-light mb-4">{{ "frameworks-and-databases" | translate(lang=lang) }}</h2>
<div class="row">
<div class="col-md-6">
{% for skill in cv.skills | filter(attribute="techtype",value="Framework") | advanced_filter(attribute="skill", include_null="none") %} {% for skill in cv.skills | filter(attribute="techtype",value="Framework") | advanced_filter(attribute="skill", include_null="none") %}
<div class="mb-3"><span class="fw-bolder">{{ skill.name }}</span> <div class="mb-3"><span class="fw-bolder">{{ skill.name }}</span>
<div class="progress my-2 rounded" style="height: 20px"> <div class="progress my-2 rounded" style="height: 20px">
@ -156,11 +167,40 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% for skill in cv.skills | filter(attribute="techtype",value="Technology") | advanced_filter(attribute="skill", include_null="only") %} {% for skill in cv.skills | filter(attribute="techtype",value="Framework") | advanced_filter(attribute="skill", include_null="only") %}
<span class="fw-bolder">{{ skill.name }}</span>,<!-- TODO: fix last, --> <span class="fw-bolder">{{ skill.name }}</span>{% if not loop.last %},{% endif %}
{% endfor %}
</div>
<div class="col-md-6">
{% for skill in cv.skills | filter(attribute="techtype",value="Database") | advanced_filter(attribute="skill", include_null="none") %}
<div class="mb-3"><span class="fw-bolder">{{ skill.name }}</span>
<div class="progress my-2 rounded" style="height: 20px">
<div class="progress-bar bg-info" role="progressbar" data-aos="zoom-in-right" data-aos-delay="100"
data-aos-anchor=".skills-section" style="width: {% if skill.skill == "Master" %}100%{% endif %}{% if skill.skill == "Expert" %}75%{% endif %}{% if skill.skill == "Intermediate" %}50%{% endif %}{% if skill.skill == "Beginer" %}25%{% endif %};"
aria-valuenow="95" aria-valuemin="0" aria-valuemax="100">
{{ skill.skill}}
</div>
</div>
</div>
{% endfor %}
{% for skill in cv.skills | filter(attribute="techtype",value="Database") | advanced_filter(attribute="skill", include_null="only") %}
<span class="fw-bolder">{{ skill.name }}</span>{% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<div>
<span class="fw-bolder">{{"tools" | translate(lang=lang)}}:</span> {% for skill in cv.skills | filter(attribute="techtype",value="Tool") | advanced_filter(attribute="skill", include_null="all") %}
{{ skill.name }}{% if skill.skill %} - {{skill.skill}}{% endif %},<!-- TODO: fix-me (empty "," if operating system empty) %-->
{% endfor %}
<span class="fw-bolder">{{"operating-systems" | translate(lang=lang)}}:</span> {% for skill in cv.skills | filter(attribute="techtype",value="OperatingSystem") | advanced_filter(attribute="skill", include_null="all") %}
{{ skill.name }}</span>{% if skill.skill %} - {{skill.skill}}{% endif %}{% if not loop.last %},{% endif %}
{% endfor %}
</div>
<span class="fw-bolder">{{"languages" | translate(lang=lang)}}:</span>
{% for language in cv.languages %}
{{language.lang}} - {{language.level|translate(lang=lang)}}
{% if not loop.last %},{% endif %}
{% endfor %}
</div> </div>
</div> </div>
@ -168,13 +208,45 @@
<div class="work-experience-section"> <div class="work-experience-section">
<h2 class="h2 fw-light mb-4">{{ "work-experience" | translate(lang=lang) }}</h2> <h2 class="h2 fw-light mb-4">{{ "work-experience" | translate(lang=lang) }}</h2>
<div class="timeline"> <div class="timeline">
{% for job in cv.jobs %} {% for job in cv.jobs | filter(attribute="jobtype", value="Contract") %}
<div class="timeline-card timeline-card-info" data-aos="fade-in" data-aos-delay="{{ loop.index * 10 }}">
<div class="timeline-head px-4 pt-3">
<div class="h5">{{ job.title }} <span class="text-muted h6">at {{ job.company }}</span></div>
</div>
<div class="timeline-body px-4 pb-4">
{% if job.languages and job.technologies %}
{{ job.languages | concat(with=job.technologies) }}
{% elif job.languages %}
{{job.languages}}
{% elif job.technologies %}
{{job.technologies}}
{% endif %}
{% if job.from | format_date(type="job") != job.to | format_date(type="job") %}
<div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }} - {{ job.to | format_date(type="job") }}</div>
{% else %}
<div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }}</div>
{% endif %}
<div>{{ job.description | lang_entity(lang=lang) }} </div>
</div>
</div>
{% endfor %}
</div>
<h2 class="h2 fw-light mb-4">{{ "work-freelance" | translate(lang=lang) }}</h2>
<div class="timeline">
{% for job in cv.jobs | filter(attribute="jobtype", value="Freelance") %}
<div class="timeline-card timeline-card-info" data-aos="fade-in" data-aos-delay="{{ loop.index * 100 }}"> <div class="timeline-card timeline-card-info" data-aos="fade-in" data-aos-delay="{{ loop.index * 100 }}">
<div class="timeline-head px-4 pt-3"> <div class="timeline-head px-4 pt-3">
<div class="h5">{{ job.title }} <span class="text-muted h6">at {{ job.company }}</span></div> <div class="h5">{{ job.title }} <span class="text-muted h6">at {{ job.company }}</span></div>
</div> </div>
<div class="timeline-body px-4 pb-4"> <div class="timeline-body px-4 pb-4">
{% if job.from | format_date(type="job") != job.from | format_date(type="job") %} {% if job.languages and job.technologies %}
{{ job.languages | concat(with=job.technologies) }}
{% elif job.languages %}
{{job.languages}}
{% elif job.technologies %}
{{job.technologies}}
{% endif %}
{% if job.from | format_date(type="job") != job.to | format_date(type="job") %}
<div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }} - {{ job.to | format_date(type="job") }}</div> <div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }} - {{ job.to | format_date(type="job") }}</div>
{% else %} {% else %}
<div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }}</div> <div class="text-muted text-small mb-3">{{ job.from | format_date(type="job") }}</div>
@ -219,7 +291,7 @@
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-5" data-aos="fade-left" data-aos-delay="200"> <div class="col-md-5" data-aos="fade-left" data-aos-delay="200">
<div class="d-print-none"> <div class="d-print-none">
<form action="https://formspree.io/your@email.com" method="POST"> <form action="{{ contact_svc }}" method="POST">
<div class="form-outline mb-4"> <div class="form-outline mb-4">
<input type="text" id="name" class="form-control" required/> <input type="text" id="name" class="form-control" required/>
<label class="form-label" for="name">Name</label> <label class="form-label" for="name">Name</label>