diff --git a/flake.nix b/flake.nix index 3ba17f4..f54f113 100644 --- a/flake.nix +++ b/flake.nix @@ -34,57 +34,74 @@ ''; }; - src = typixLib.cleanTypstSource ./.; - commonArgs = { - typstSource = "src/main.typ"; - typstOutput = "out/main.pdf"; + mkTypst = entry: rec { + src = typixLib.cleanTypstSource ./.; + commonArgs = { + typstSource = "src/${entry}.typ"; + typstOutput = "out/${entry}.pdf"; - fontPaths = [ ]; + fontPaths = [ + "${pkgs.inter}/share/fonts/truetype" + ]; - virtualPaths = [ ]; + virtualPaths = [ ]; + }; + + commonBuildArgs = commonArgs // { + XDG_CACHE_HOME = typstPackagesCache; + }; + + build-drv = typixLib.buildTypstProject ( + commonBuildArgs + // { + inherit src; + } + ); + + build-script = typixLib.buildTypstProjectLocal ( + commonBuildArgs + // { + inherit src; + } + ); + + watch-script = typixLib.watchTypstProject commonArgs; }; - commonBuildArgs = commonArgs // { - XDG_CACHE_HOME = typstPackagesCache; - }; - - build-drv = typixLib.buildTypstProject ( - commonBuildArgs - // { - inherit src; - } - ); - - build-script = typixLib.buildTypstProjectLocal ( - commonBuildArgs - // { - inherit src; - } - ); - - watch-script = typixLib.watchTypstProject commonArgs; + typstMain = mkTypst "main"; + typstWork = mkTypst "work"; in { checks = { - inherit build-drv build-script watch-script; + inherit (typstMain) build-drv build-script watch-script; + # inherit (typstWork) build-drv build-script watch-script; }; - packages.default = build-drv; + packages = { + default = typstMain.build-drv; + work = typstWork.build-drv; + }; apps = rec { default = watch; build = inputs.flake-utils.lib.mkApp { - drv = build-script; + drv = typstMain.build-script; }; watch = inputs.flake-utils.lib.mkApp { - drv = watch-script; + drv = typstMain.watch-script; + }; + build-work = inputs.flake-utils.lib.mkApp { + drv = typstWork.build-script; + }; + watch-work = inputs.flake-utils.lib.mkApp { + drv = typstWork.watch-script; }; }; devShells.default = typixLib.devShell { - inherit (commonArgs) fontPaths virtualPaths; + inherit (typstMain.commonArgs) fontPaths virtualPaths; packages = [ - watch-script + typstMain.watch-script pkgs.typstyle pkgs.tinymist diff --git a/src/lib/work.typ b/src/lib/work.typ new file mode 100644 index 0000000..4763919 --- /dev/null +++ b/src/lib/work.typ @@ -0,0 +1,247 @@ +#let dates-helper( + start-date: "", + end-date: "", +) = { + start-date + " - " + end-date +} + +#let job_entry(date, company, position, description, experiences) = { + grid( + columns: (22%, 33%, auto), + gutter: 12pt, + // Column 1: Date Range + box( + fill: rgb(242, 242, 242, 255), + width: 100%, + inset: 10pt, + text(date), + ), + + // Column 2: Company Info + box()[ + #text(company) \ + #text(weight: "bold", position) \ + #text(description) + ], + + // Column 3: Work Experiences + for exp in experiences { + box( + fill: rgb(242, 242, 242, 255), + width: 100%, + inset: 10pt, + [ + #text(exp.role) \ + #text(weight: "bold", exp.heading) \ + #text(size: 10pt, exp.description) + ], + ) + } + ) +} + +#let resume( + name: "", + position: "", + address: none, + birth_date: "", + email: "", + phone: "", + profile-picture: image, + company-logo: image, + font: "Inter", + paper: "a4", + body, +) = { + set document(author: name, title: name) + + set text(font: font, lang: "en") + show heading: set text(rgb(0, 194, 255, 255)) + + grid( + columns: 100%, + align: (right), + [ + #company-logo + \ \ \ \ + ] + ) + + grid( + columns: (60%, 180pt), + align: (top, right), + [ + #text(size: 30pt, weight: "bold", name) \ + \ + #text(size: 13pt, weight: "bold", position) \ + \ \ + ], + ) + + set text() + grid( + columns: (50%, auto), + gutter: 100pt, + box( + width: 100%, + [ + #heading(level: 2, "Personal Data") + + #grid( + columns: (50%, 50%), + gutter: 20pt, + box( + width: 100%, + [ + #text(weight: "bold", "Name:") \ + #text(name) + ], + ), + box( + width: 100%, + [ + #text(weight: "bold", "Birth date:") \ + #text(birth_date) + ], + ), + + box( + width: 100%, + [ + #text(weight: "bold", "Address:") \ + #text(address.street) + #text(address.city) + ], + ), + box( + width: 100%, + [ + #text(weight: "bold", "Contact:") \ + #text(email) + #{ + if phone != "" { + text(phone) + } + } + ], + ), + ) + ], + ), + profile-picture, + ) + + linebreak() + linebreak() + + body +} + +#let work_experience(body) = { + set text(fill: black) + + set text(fill: rgb(0, 194, 255, 255)) + grid( + columns: (60%, auto), + heading(level: 2, "Work Experience"), heading(level: 2, "Case Studies"), + ) + + set text(fill: black) + body +} + +#let education_entry(date, university, degree, subject) = { + box( + width: 100%, + [ + #text(weight: "bold", date) \ + #text(weight: "bold", university) \ + #text(degree + " in " + subject) + \ \ + ], + ) +} + +#let certification_entry(date, issuer, certificate) = { + box( + width: 100%, + [ + #text(weight: "bold", date) \ + #text(weight: "bold", certificate) \ + #text(weight: "bold", issuer) + \ \ + ], + ) +} + +#let language_levels = ( + (0, 25, "Beginner"), + (26, 50, "Intermediate"), + (51, 75, "Advanced"), + (76, 99, "Fluent"), + (100, 100, "Native"), +) + +#let lang_level_text = score => { + let result = "Unknown" + + for (min, max, label) in language_levels { + if score >= min and score <= max { + result = label + } + } + + result +} + +#let language_entry(lang, score) = { + grid( + columns: (40%, auto), + box( + width: 100%, + [ + #text(weight: "bold")[#lang] + ], + ), + box( + width: 100%, + [ + #lang_level_text(score) + #box( + width: 105pt, + height: 6pt, + fill: rgb(220, 220, 220), + inset: 0pt, + [ + #rect( + width: (105pt * score / 100), + height: 6pt, + fill: rgb(0, 194, 255, 255), + ) + ], + ) + ], + ), + ) +} + +#let bottom_grid(sections) = { + linebreak() + linebreak() + linebreak() + + set text(fill: rgb(0, 194, 255, 255)) + grid( + columns: (60%, auto), + gutter: 3pt, + ..sections.map(s => { + if s != none { + heading(level: 2, s.title) + + set text(fill: black) + s.content + } + }), + ) + set text(fill: black) +} diff --git a/src/photo.jpg b/src/photo.jpg new file mode 100644 index 0000000..9685c19 Binary files /dev/null and b/src/photo.jpg differ diff --git a/src/upsquared.png b/src/upsquared.png new file mode 100644 index 0000000..e48dfef Binary files /dev/null and b/src/upsquared.png differ diff --git a/src/work.typ b/src/work.typ new file mode 100644 index 0000000..8d05bfb --- /dev/null +++ b/src/work.typ @@ -0,0 +1,191 @@ +#import "lib/work.typ": * + +#let name = "Felix Schröter" +#let position = "Senior Software Engineer & Tech Lead" +#let address = (street: "Salzwedeler Str. 1", city: "21339 Lüneburg") +#let birth_date = "26.09.1995" +#let email = "fs@upsquared.com" + +#let profile-picture = image("photo.jpg", width: 115pt, height: 135pt) +#let company-logo = image("upsquared.png", width: 220pt, height: auto) + +#show: resume.with( + name: name, + position: position, + address: address, + birth_date: birth_date, + email: email, + profile-picture: profile-picture, + company-logo: company-logo, +) + +#show: work_experience.with() + +#job_entry( + dates-helper(start-date: "Jun 2024", end-date: "Present"), + "upsquared GmbH", + "Senior Software Engineer & Tech Lead", + "Leading Cloud-native Development, Infrastructure & DevOps solutions", + ( + ( + role: "Tech Lead", + heading: "Grow engineering team", + description: "Onboard, mentor & lead new team of engineers.", + ), + ( + role: "Software & Cloud Architect", + heading: "Cloud-native solutions", + description: "Architect solutions built on Rust, Kubernetes, OpenTofu & GCP", + ), + ), +) + +#job_entry( + dates-helper(start-date: "Oct 2019", end-date: "May 2024"), + "upsquared GmbH", + "Senior Software Engineer", + "Full-Stack Architecture, Development, Infrastructure & DevOps for Cloud-native solutions", + ( + ( + role: "Software & Cloud Architect", + heading: "Cloud-native solutions", + description: "Architect solutions built on Rust, C#, Kubernetes, Terraform & GCP", + ), + ( + role: "DevOps Engineer", + heading: "Full-stack Observability", + description: "Enhanced Observability with Sentry, Rust, C#, React & GKE", + ), + ( + role: "Frontend Engineer", + heading: "Plant Analyzer Web", + description: "Built web-based frontend for digital phenotyping platform", + ), + ( + role: "DevOps Engineer", + heading: "Reproducible CI/CD", + description: "Established reproducible builds & dev environments using Nix", + ), + ), +) + +#job_entry( + dates-helper(start-date: "Apr 2019", end-date: "Sep 2019"), + "Digital Spring", + "Senior Software Engineer", + "Full-Stack Architecture, Development, Infrastructure & DevOps for Cloud-native solutions", + ( + ( + role: "DevOps Engineer", + heading: "GitLab CI/CD", + description: "Set up GitLab CI/CD pipeline for .NET microservices", + ), + ), +) + +#job_entry( + dates-helper(start-date: "Oct 2016", end-date: "Mar 2019"), + "Digital Spring", + "Software Engineer", + "Full-Stack Development, Infrastructure & DevOps for Cloud-native solutions", + ( + ( + role: "Backend & Cloud Architect", + heading: "Cloud-native solutions", + description: "Built microservices & Kubernetes cluster from scratch", + ), + ( + role: "Backend & Cloud Engineer", + heading: "Serverless solutions", + description: "Built serverless backends with AWS Lambda", + ), + ( + role: "Frontend Engineer", + heading: "Modern web apps", + description: "Implemented frontends in React & Angular for different projects", + ), + ), +) + +#job_entry( + dates-helper(start-date: "Jun 2015", end-date: "Sep 2016"), + "Werum IT Solutions GmbH (now Körber Pharma Software GmbH)", + "Software Engineer", + "", + ( + ( + role: "Software Engineer", + heading: "Interface development", + description: "DCS/PCS and ERP interface development with .NET & PL/SQL", + ), + ), +) + +#job_entry( + dates-helper(start-date: "Aug 2012", end-date: "Jun 2015"), + "Werum IT Solutions GmbH (now Körber Pharma Software GmbH)", + "Trainee", + "", + ( + ( + role: "Software Engineer", + heading: "Process automation", + description: "Process automation (DCS/PCS) interface development with .NET", + ), + ), +) + +#let education = ( + ( + dates-helper(start-date: "May 2024", end-date: "Present"), + "University of Colorado Boulder", + "Master of Science", + "Computer Science", + ), + ( + dates-helper(start-date: "Aug 2012", end-date: "Jun 2015"), + "Berufsbildende Schulen I Lüneburg", + "Computer Science Expert", + "Software Development", + ), +); + +#let certifications = ( + ( + "Feb 2024", + "CompTIA", + "CompTIA Cloud+", + ), + ( + "Feb 2024", + "Cloud Security Alliance", + "Certificate of Cloud Security Knowledge v.4", + ), +); + +#let languages = ( + ("English", 90), + ("German", 100), +) + +#let sections = ( + ( + title: "Education", + content: education.map(e => education_entry(..e)).join("\n"), + ), + ( + title: "Languages", + content: languages.map(l => language_entry(..l)).join("\n"), + ), + none, + // ( + // title: "Certifications", + // content: certifications.map(c => certification_entry(..c)).join("\n"), + // ), + ( + title: "Interests", + content: "reading, tea, Nix, NixOS, Linux, homelab, Doctor Who", + ), +) + +#bottom_grid(sections)