major: statically-generated resume with Vike

main
Avraham Sakal 2 months ago
parent 0d820466d0
commit 3b47596a57

142
.gitignore vendored

@ -0,0 +1,142 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Cloudflare
.wrangler/
# Vercel
.vercel/
# Sentry Vite Plugin
.env.sentry-build-plugin
# aws-cdk
.cdk.staging
cdk.out

@ -0,0 +1,2 @@
nodejs 22.15.0
pnpm 9.7.1

@ -0,0 +1,53 @@
Generated with [vike.dev/new](https://vike.dev/new) ([version 471](https://www.npmjs.com/package/create-vike/v/0.0.471)) using this command:
```sh
pnpm create vike@latest --react --compiled-css --cloudflare --eslint
```
## Contents
* [React](#react)
* [`/pages/+config.ts`](#pagesconfigts)
* [Routing](#routing)
* [`/pages/_error/+Page.jsx`](#pages_errorpagejsx)
* [`/pages/+onPageTransitionStart.ts` and `/pages/+onPageTransitionEnd.ts`](#pagesonpagetransitionstartts-and-pagesonpagetransitionendts)
* [SSR](#ssr)
* [HTML Streaming](#html-streaming)
## React
This app is ready to start. It's powered by [Vike](https://vike.dev) and [React](https://react.dev/learn).
### `/pages/+config.ts`
Such `+` files are [the interface](https://vike.dev/config) between Vike and your code. It defines:
* A default [`<Layout>` component](https://vike.dev/Layout) (that wraps your [`<Page>` components](https://vike.dev/Page)).
* A default [`title`](https://vike.dev/title).
* Global [`<head>` tags](https://vike.dev/head-tags).
### Routing
[Vike's built-in router](https://vike.dev/routing) lets you choose between:
* [Filesystem Routing](https://vike.dev/filesystem-routing) (the URL of a page is determined based on where its `+Page.jsx` file is located on the filesystem)
* [Route Strings](https://vike.dev/route-string)
* [Route Functions](https://vike.dev/route-function)
### `/pages/_error/+Page.jsx`
The [error page](https://vike.dev/error-page) which is rendered when errors occur.
### `/pages/+onPageTransitionStart.ts` and `/pages/+onPageTransitionEnd.ts`
The [`onPageTransitionStart()` hook](https://vike.dev/onPageTransitionStart), together with [`onPageTransitionEnd()`](https://vike.dev/onPageTransitionEnd), enables you to implement page transition animations.
### SSR
SSR is enabled by default. You can [disable it](https://vike.dev/ssr) for all your pages or only for some pages.
### HTML Streaming
You can enable/disable [HTML streaming](https://vike.dev/stream) for all your pages, or only for some pages while still using it for others.

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

@ -1,44 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400..800;1,400..800&display=swap");
/* @font-face {
font-family: Lato;
src: url("Lato2OFL/Lato-Regular.ttf");
}
@font-face {
font-family: Lato;
src: url("Lato2OFL/Lato-Bold.ttf");
font-weight: bold;
}
@font-face {
font-family: Lato;
src: url("Lato2OFL/Lato-Italic.ttf");
font-style: italic;
}
@font-face {
font-family: Lato;
src: url("Lato2OFL/Lato-BoldItalic.ttf");
font-style: italic;
font-weight: bold;
} */
/* @page {
@bottom-center {
content: "https://sakal.us";
font-size: 0.8em;
color: #aaa;
font-family: Lato;
}
@top-center {
content: "CSS Paged Media Tutorial by Andreas Jung";
font-size: 0.8em;
color: #aaa;
font-family: Lato;
}
} */
/* body {
font-family: Lato;
} */

@ -0,0 +1,179 @@
export const contactInfo = {
name: "Brian Sakal",
email: "brian@sakal.us",
phone: "305-930-0248",
website: "https://git.sakal.us/avraham",
location: "Passaic, NJ",
};
export const mainSkills = [
"Javascript/Typescript",
"React",
"Next.js",
"Node.js",
"Express.js",
"Docker",
"MySQL",
"Clickhouse",
"DevOps",
"Kubernetes",
"Linux",
"Cloudflare",
"Vercel",
"S3",
];
export const exposedSkills = [
"PostgreSQL",
"Elasticsearch",
"Redis",
"RethinkDB",
"Helm",
"nginx",
"React Native",
"Swagger",
];
export const employment = [
{
title: "Lead Developer (Full-Stack)",
company: "TorahAnytime.com",
location: "Flushing, NY",
start: "June 2022",
end: "Present",
highlights: [
// From old resume:
// "Fixed/troubleshooted bugs and added features across multiple legacy codebases",
// "Consolidated cloud resources by using Kubernetes.",
// "Transitioned a legacy Cloudflare/nginx TLD site to Vercel, while maintaining custom nginx routes on the TLD, plus the entire legacy site under a subdomain.",
// "Reduced time-to-deployment from 5 minutes to 15 seconds with locally-runnable deploy scripts; besides setting up Drone/Gitlab CI/CD pipelines.",
// "Architected custom analytics backend w/ Clickhouse. Reduced query times from minutes to milliseconds.",
// "Implemented a custom IVR phone system to dynamically browse content.",
// "Deployed and configured various supporting/ancillary services in Kubernetes, including Gitlab, private Docker & NPM registries.",
// "Implemented OpenTelemetry tracing visualized in Grafana, decreasing transcoder troubleshooting by 50%.",
// "Implemented various asynchronous workflows, including a new video transcoder, in Temporal; thus completely severing our dependence on Vimeo, reducing costs by $40k/year.",
// From Otta resume:
"General full-stack work (frontend implementation in Next.js, adding endpoints to api, running database migrations in MySQL, updating Clickhouse with those schema changes, etc.)",
"Architected and implemented an in-house transcoding/content-delivery system based on Temporal, Cloudflare, and Backblaze B2 to replace Vimeo; saving $70k/year.",
"Setup generic events analytics system in Clickhouse to replace MySQL, Redis, and (eventually) Google Analytics, for faster and more granular analytics over arbitrary events and event payloads; significantly improving query times from minutes to milliseconds",
"Legacy code maintenance/incremental refactoring",
"Consolidated various async jobs (e.g. notifications, daily/weekly digest, delayed release, etc.) into type-safe Temporal workflows, enhancing developer experience and setting conventions for easy implementation of future workflows",
"Designed and deployed a multi-instance tRPC server for type-safe endpoints for the frontend",
`Adapted Agile/Scrum to accomodate stakeholder expectations of simultaneous planned and unplanned (i.e. during a Sprint) throughput and turnaround, while keeping longer-term goals predictable.`,
"Crafted multi-stage Dockerfiles to leverage layer caching in various projects",
"Reduced deployment time from 5 minutes to 15 seconds by implementing locally-runnable deploy scripts, besides setting up Github Actions CI/CD pipelines.",
"Developed a custom IVR phone system for dynamically browsing our content.",
"Deployed and configured various supporting services in Kubernetes such as Grafana, private Docker & NPM registries.",
"Implemented OpenTelemetry tracing visualized in Grafana, resulting in 50% decrease in legacy transcoder troubleshooting time.",
"Designed various parameterized Grafana dashboards for at-a-glance analysis of user stats.",
"Implemented two integrations: MySQL-Clickhouse sync (using Clickhouse primitives), and MySQL-Salesforce sync (on Temporal).",
"Transitioned legacy Cloudflare/nginx TLD site to Vercel, while maintaining custom nginx routes on the TLD and hosting the entire legacy site under a subdomain.",
],
},
// {
// title: "Assistant Manager",
// company: "Sureknit Inc. Property Management",
// location: "East Flatbush, NY",
// start: "February 2020",
// end: "June 2022",
// highlights: [
// "Kept accurate records of tenant rent payments.",
// "Balanced and monitored bank accounts for all subsidiary companies.",
// "Furnished necessary documentation for various real estate operations, such as new leases, property sales, etc.",
// ],
// },
// {
// title: "Architect Intern",
// company: "Sandy Hacohen",
// location: "Flushing, NY",
// start: "July 2018",
// end: "November 2018",
// highlights: [
// "Produced architectural drawings of various points-of-view based on given floorplans.",
// "Adjusted existing floorplans to spec.",
// ],
// },
// {
// title: "Operations Manager",
// company: "Tax Opportunities America",
// location: "Flushing, NY",
// start: "April 2014",
// end: "May 2015",
// highlights: [
// "Interfaced with clients, prepared necessary documentation and submitted applications to government programs on behalf of clients.",
// "Monitored and accommodated deadlines, missing client data, and when client promised to have the data available",
// ],
// },
// {
// title: "Representative",
// company: "Computer-Aided Engineering Network (CAEN) Hotline",
// location: "U. of Michigan",
// start: "2007",
// end: "2009, 2010",
// highlights: [
// "Provided first-response assistance to students and collecting pertinent information to arrange for involvement of specialized staff.",
// ],
// },
];
export const projectsHobbies = [
{
title: "Self-host email for the sakal.us domain",
highlights: ["Vultr-Hosted", "Postfix for SMTP", "Dovecot for IMAP"],
},
{
title: "Self-host a Kubernetes cluster",
highlights: [
"k3s",
"Private Docker image registry",
"cert-manager",
"Private coding projects",
],
},
{
title: "Self-host a Gitea code repository",
highlights: [
"Deployed within the above Kubernetes cluster. Available at https://git.sakal.us",
],
},
{
title: "Personal coding blog",
highlights: ["Astro", "Cloudflare Pages", "https://blog.sakal.us"],
},
{
title: "Calendar Optimizer project",
highlights: [
"Stock options research platform",
"Preact",
"trpc",
"Clickhouse",
"https://calendar-optimizer-frontend.sakal.us",
],
},
// {
// title: "Custom From-Scratch Full-Stack eCommerce website",
// highlights: [
// "Previously at piazzaoptical.com",
// "mithril.js",
// "nginx as reverse proxy and TLS termination",
// "lua-http on LuaJIT",
// "LMDB",
// ],
// },
];
export const education = [
{
title: "B.S. in Aerospace Engineering",
location: "University of Michigan, Ann Arbor, MI",
start: "Sept. 2006",
end: "April 2009, Sept. 2010, April 2011",
gpa: "3.185",
},
// {
// title: "Ph.D. in Rabbinics",
// location: "Rabbinical Seminary of America, Flushing, NY",
// start: "April 2009",
// end: "April 2010, April 2011, January 2020",
// },
];

@ -0,0 +1,72 @@
import eslint from "@eslint/js";
import react from "eslint-plugin-react";
import globals from "globals";
import tseslint, { type ConfigArray } from "typescript-eslint";
export default tseslint.config(
{
ignores: [
"dist/*",
// Temporary compiled files
"**/*.ts.build-*.mjs",
// JS files at the root of the project
"*.js",
"*.cjs",
"*.mjs",
],
},
eslint.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
sourceType: "module",
ecmaVersion: "latest",
},
},
},
{
rules: {
"@typescript-eslint/no-unused-vars": [
1,
{
argsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-namespace": 0,
},
},
{
files: ["**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}"],
...react.configs.flat.recommended,
languageOptions: {
...react.configs.flat.recommended.languageOptions,
globals: {
...globals.serviceworker,
...globals.browser,
},
},
settings: {
react: {
version: "detect",
},
},
} as ConfigArray[number],
react.configs.flat["jsx-runtime"] as ConfigArray[number],
{
rules: {
"react/no-unknown-property": [
"error",
{
ignore: ["css"],
},
],
},
},
);

@ -1,343 +0,0 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<section class="header">
<h2 class="lg bold">Brian Sakal</h2>
</section>
<section class="contact-info">
<ul class="inline">
<li>Passaic, NJ</li>
<li>305-930-0248</li>
<li><a href="https://git.sakal.us/">Git</a></li>
</ul>
</section>
<section class="sections">
<section class="relevant-skills">
<label>
<div class="fit-content">
<div class="bold">Skills/Technologies</div>
</div>
</label>
<div>
<ul class="inline">
<li>Docker</li>
<li>DevOps (Logging, Monitoring, Grafana, CI/CD)</li>
<li>Kubernetes</li>
<li>Node.js</li>
<li>Javascript/Typescript</li>
<li>React</li>
<li>MySQL/PostgreSQL</li>
<li>Elasticsearch</li>
<li>Clickhouse</li>
<li>Linux</li>
<li>Cloudflare</li>
<li>Redis</li>
<li>RethinkDB</li>
<li>Helm</li>
<li>nginx</li>
<li>Vercel</li>
<li>S3</li>
<li>Express.js</li>
<li>React</li>
<li>React Native</li>
<li>Next.js</li>
<li>Swagger</li>
</ul>
</div>
</section>
<section class="employment">
<label>
<div class="fit-content">
<span class="bold">Employment</span>
<span class="sm italic right">(Most recent)</span>
</div>
</label>
<div>
<ul class="no-bullet">
<li>
<div class="space-between">
<div>
<span class="bold">Senior Full-Stack Developer,</span>
<span>TorahAnytime.com, Flushing, NY</span>
</div>
<div class="italic right">June 2022Present</div>
</div>
<div>
<ul>
<!-- <li>
Fixed/troubleshooted bugs and added features across multiple
legacy codebases
</li>
<li>Consolidated cloud resources by using Kubernetes.</li> -->
<li>
Transitioned a legacy Cloudflare/nginx TLD site to Vercel,
while maintaining custom nginx routes on the TLD, plus the
entire legacy site under a subdomain.
</li>
<li>
Reduced time-to-deployment from 5 minutes to 15 seconds with
locally-runnable deploy scripts; besides setting up
Drone/Gitlab CI/CD pipelines.
</li>
<li>
Architected custom analytics backend w/ Clickhouse. Reduced
query times from minutes to milliseconds.
</li>
<li>
Implemented a custom IVR phone system to dynamically browse
content.
</li>
<li>
Deployed and configured various supporting/ancillary
services in Kubernetes, including Gitlab, private Docker &
NPM registries.
</li>
<li>
Implemented OpenTelemetry tracing visualized in Grafana,
decreasing transcoder troubleshooting by 50%.
</li>
<li>
Implemented various asynchronous workflows, including a new
video transcoder, in Temporal; thus completely severing our
dependence on Vimeo, reducing costs by $40k/year.
</li>
</ul>
</div>
</li>
<li>
<div class="space-between">
<div>
<span class="bold">Assistant Manager,</span>
<span
>Sureknit Inc. Property Management, East Flatbush, NY</span
>
</div>
<div class="italic right">February 2020June 2022</div>
</div>
<div>
<ul>
<li>Kept accurate records of tenant rent payments.</li>
<li>
Balanced and monitored bank accounts for all subsidiary
companies.
</li>
<li>
Furnished necessary documentation for various real estate
operations, such as new leases, property sales, etc.
</li>
</ul>
</div>
</li>
<!-- <li>
<div>
<span class="bold">Architect Intern,</span>
<span>Sandy Hacohen, Flushing, NY</span>
</div>
<div class="italic right">July 2018November 2018</div>
<div>
<ul>
<li>
Produced architectural drawings of various points-of-view
based on given floorplans.
</li>
<li>Adjusted existing floorplans to spec.</li>
</ul>
</div>
</li> -->
<!-- <li>
<div class="space-between">
<div>
<span class="bold">Operations Manager,</span>
<span>Tax Opportunities America, Flushing, NY</span>
</div>
<div class="italic right">April 2014May 2015</div>
</div>
<div>
<ul>
<li>
Interfaced with clients, prepared necessary documentation
and submitted applications to government programs on behalf
of clients.
</li>
<li>
Monitored and accommodated deadlines, missing client data,
and when client promised to have the data available
</li>
</ul>
</div>
</li> -->
<!-- <li>
<div class="space-between">
<div>
<span class="bold">Representative,</span>
<span
>Computer-Aided Engineering Network (CAEN) Hotline, U. of
Michigan</span
>
</div>
<div class="italic right">20072009, 20102011</div>
</div>
<div>
<ul>
<li>
Provided first-response assistance to students and
collecting pertinent information to arrange for involvement
of specialized staff.
</li>
</ul>
</div>
</li> -->
</ul>
</div>
</section>
<section class="projects-hobbies">
<label>
<div class="fit-content">
<div class="bold">Projects/Hobbies</div>
</div>
</label>
<div>
<ul class="no-bullet">
<li>
<div>
<span class="bold"
>Self-host email for the sakal.us domain</span
>
<span
><ul class="inline">
<li>Vultr-Hosted</li>
<li>Postfix for SMTP</li>
<li>Dovecot for IMAP</li>
</ul></span
>
</div>
</li>
<li>
<div>
<span class="bold">Self-host a Kubernetes cluster</span>
<span
><ul class="inline">
<li><code>k3s</code></li>
<li>Private Docker image registry</li>
<li>
<code>cert-manager</code>
</li>
<li>Private coding projects</li>
</ul></span
>
</div>
</li>
<li>
<div>
<span class="bold">Self-host a Gitea code repository</span>
<span
>Deployed within the above Kubernetes cluster. Available at
<a href="https://git.sakal.us/">https://git.sakal.us</a></span
>
</div>
</li>
<li>
<div>
<span class="bold"
>Personal coding blog (<a
href="https://git.sakal.us/avraham/blog-astro"
>Code</a
>)</span
>
<span
><ul class="inline">
<li>Astro</li>
<li>Cloudflare Pages</li>
<li>
<a href="https://blog.sakal.us/">https://blog.sakal.us</a>
</li>
</ul>
</span>
</div>
</li>
<li>
<div>
<span class="bold"
>Calendar Optimizer project (<a
href="https://git.sakal.us/avraham/calendar-optimizer"
>Code</a
>)</span
>
<span
><ul class="inline">
<li>Stock options research platform</li>
<li>Preact</li>
<li>trpc</li>
<li>Clickhouse</li>
<li>
<a href="https://calendar-optimizer-frontend.sakal.us"
>https://calendar-optimizer-frontend.sakal.us</a
>
</li>
</ul></span
>
</div>
</li>
<li>
<div>
<span class="bold"
>Custom From-Scratch Full-Stack eCommerce website (<a
href="https://git.sakal.us/avraham/piazzaoptical.com"
>Code</a
>)</span
>
<span
><ul class="inline">
<li>Previously at piazzaoptical.com</li>
<li>
<a href="https://mithril.js.org/">mithril.js</a>
</li>
<li>nginx as reverse proxy and TLS termination</li>
<li>
<a href="https://daurnimator.github.io/lua-http/0.4/"
>lua-http</a
>
on <a href="https://luajit.org/">LuaJIT</a>
</li>
<li><a href="http://www.lmdb.tech/doc/">LMDB</a></li>
</ul></span
>
</div>
</li>
</ul>
</div>
</section>
<section class="education">
<label>
<span class="bold">Education</span>
</label>
<div>
<ul class="no-bullet">
<li>
<div class="space-between">
<div>
<span class="bold">B.S. in Aerospace Engineering,</span>
<span>University of Michigan, Ann Arbor, MI</span>
</div>
<div class="italic right">
Sept. 2006April 2009, Sept. 2010April 2011, GPA 3.185
</div>
</div>
</li>
<!-- <li>
<div>
<span class="bold">Ph.D. in Rabbinics,</span>
<span>Rabbinical Seminary of America, Flushing, NY</span>
</div>
<div class="italic right">
April 2009April 2010, April 2011January 2020
</div>
</li> -->
</ul>
</div>
</section>
</section>
</body>
</html>

@ -0,0 +1,19 @@
import "./style.css";
export default function LayoutDefault({
children,
}: {
children: React.ReactNode;
}) {
return (
<div
css={{
display: "flex",
maxWidth: 1024,
margin: "auto",
}}
>
{children}
</div>
);
}

@ -0,0 +1,8 @@
/* Reset */
body {
margin: 0;
font-family: sans-serif;
}
* {
box-sizing: border-box;
}

@ -1,5 +0,0 @@
@import url("./common.css");
@page {
size: letter;
margin: 0.5in;
}

@ -0,0 +1,36 @@
{
"scripts": {
"dev": "vike dev",
"build": "vike build",
"preview": "run-s build preview:wrangler",
"lint": "eslint .",
"preview:wrangler": "wrangler pages dev",
"deploy:wrangler": "wrangler pages deploy",
"deploy": "run-s build deploy:wrangler"
},
"dependencies": {
"vike": "^0.4.237",
"@compiled/react": "^0.18.6",
"@vitejs/plugin-react": "^5.0.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"vike-react": "^0.6.5",
"vike-cloudflare": "^0.1.7"
},
"devDependencies": {
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vite-plugin-compiled-react": "^1.3.1",
"eslint": "^9.33.0",
"@eslint/js": "^9.33.0",
"typescript-eslint": "^8.39.1",
"globals": "^16.3.0",
"eslint-plugin-react": "^7.37.5",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@cloudflare/workers-types": "^4.20250816.0",
"wrangler": "^4.30.0",
"npm-run-all2": "^8.0.4"
},
"type": "module"
}

@ -0,0 +1,17 @@
import type { Config } from "vike/types";
import vikeReact from "vike-react/config";
import Layout from "../layouts/LayoutDefault.js";
// Default config (can be overridden by pages)
// https://vike.dev/config
export default {
// https://vike.dev/Layout
Layout,
// https://vike.dev/head-tags
title: "Brian Sakal | Resume",
description: "Resume Auto-Generated from Metadata",
extends: vikeReact,
} satisfies Config;

@ -0,0 +1,19 @@
import { usePageContext } from "vike-react/usePageContext";
export default function Page() {
const { is404 } = usePageContext();
if (is404) {
return (
<>
<h1>404 Page Not Found</h1>
<p>This page could not be found.</p>
</>
);
}
return (
<>
<h1>500 Internal Server Error</h1>
<p>Something went wrong.</p>
</>
);
}

@ -0,0 +1,222 @@
import { useData } from "vike-react/useData";
import type { Data } from "./+data";
import "../../styles.css";
export default function Page() {
return (
<div
css={{
fontFamily: '"EB Garamond", serif',
fontOpticalSizing: "auto",
fontWeight: 400,
fontStyle: "normal",
fontSize: "0.9em",
display: "flex",
flexDirection: "row",
justifyContent: "start",
}}
>
<div
css={{
display: "flex",
flexDirection: "column",
width: "30%",
backgroundColor: "lavender",
padding: "1em",
gap: "1em",
}}
>
<Header />
<MainSkills />
<ExposedSkills />
</div>
<div
css={{
display: "flex",
flexDirection: "column",
width: "70%",
padding: "1em",
}}
>
<Employment />
<ProjectsHobbies />
<Education />
</div>
</div>
);
}
function Header() {
const { contactInfo } = useData<Data>();
return (
<div
css={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<section>
<div>
<img
src="/assets/resume-image.jpg"
alt="headshot"
css={{ width: "14em", borderRadius: "50%", objectFit: "contain" }}
/>
</div>
</section>
<section>
<h2 css={{ marginBottom: "0.3em" }} className="lg bold">
{contactInfo.name}
</h2>
</section>
<section css={{ textAlign: "center" }}>
<ul className="inline">
<li>{contactInfo.phone}</li>
<li>{contactInfo.email}</li>
</ul>
<ul className="inline">
<li>{contactInfo.location}</li>
<li>
<a href={contactInfo.website}>{contactInfo.website}</a>
</li>
</ul>
</section>
</div>
);
}
function MainSkills() {
const { mainSkills } = useData<Data>();
return (
<section className="relevant-skills">
<h3>Main Skills/Technologies</h3>
<div>
<ul className="inline">
{mainSkills.map((skill) => (
<li>{skill}</li>
))}
</ul>
</div>
</section>
);
}
function ExposedSkills() {
const { exposedSkills } = useData<Data>();
return (
<section className="relevant-skills">
<h3>Have Used</h3>
<div>
<ul className="inline">
{exposedSkills.map((skill) => (
<li>{skill}</li>
))}
</ul>
</div>
</section>
);
}
function Employment() {
const { employment } = useData<Data>();
return (
<section className="employment">
<h1>
<div className="fit-content">
<span className="bold">Employment </span>
<span css={{ fontWeight: 300 }} className="sm italic right">
(Most recent)
</span>
</div>
</h1>
<div>
<ul className="no-bullet">
{employment.map((employment) => (
<li>
<div className="space-between">
<div>
<span className="bold">{employment.title},</span>
<span>
{employment.company}, {employment.location}
</span>
</div>
<div className="italic right">
{employment.start}-{employment.end}
</div>
</div>
<div>
<ul>
{employment.highlights.map((highlight) => (
<li>{highlight}</li>
))}
</ul>
</div>
</li>
))}
</ul>
</div>
</section>
);
}
function ProjectsHobbies() {
const { projectsHobbies } = useData<Data>();
return (
<section className="projects-hobbies">
<h1>
<div className="fit-content">
<div className="bold">Projects/Hobbies</div>
</div>
</h1>
<div>
<ul className="no-bullet">
{projectsHobbies.map((projectHobby) => (
<li>
<div>
<span className="bold">{projectHobby.title}</span>
<span>
<ul className="inline">
{projectHobby.highlights.map((highlight) => (
<li>{highlight}</li>
))}
</ul>
</span>
</div>
</li>
))}
</ul>
</div>
</section>
);
}
function Education() {
const { education } = useData<Data>();
return (
<section className="education">
<h1>
<span className="bold">Education</span>
</h1>
<div>
<ul className="no-bullet">
{education.map((education) => (
<li>
<div className="space-between">
<div>
<span className="bold">{education.title},</span>
<span>{education.location}</span>
</div>
<div className="italic right">
{education.start}{education.end}
</div>
</div>
</li>
))}
</ul>
</div>
</section>
);
}

@ -0,0 +1,7 @@
import * as resumeData from "../../database/resume.js";
export default function data() {
return resumeData;
}
export type Data = ReturnType<typeof data>;

File diff suppressed because it is too large Load Diff

@ -1,2 +0,0 @@
#!/bin/sh
miniserve --index index.html .

@ -0,0 +1,29 @@
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"module": "ESNext",
"noEmit": true,
"moduleResolution": "Bundler",
"target": "ES2022",
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
],
"types": [
"vite/client",
"vike-react",
"vike-cloudflare/types"
],
"jsx": "react-jsx",
"jsxImportSource": "react"
},
"exclude": [
"dist"
]
}

@ -0,0 +1,19 @@
import { pages } from "vike-cloudflare";
import react from "@vitejs/plugin-react";
import { compiled } from "vite-plugin-compiled-react";
import vike from "vike/plugin";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
vike(),
compiled({
extract: true,
}),
react(),
pages(),
],
build: {
target: "es2022",
},
});

@ -0,0 +1,4 @@
name = "my-app"
compatibility_date = "2024-09-29"
pages_build_output_dir = "./dist/cloudflare"
compatibility_flags = [ "nodejs_compat" ]
Loading…
Cancel
Save