5 Commits

Author SHA1 Message Date
Arnie 302d3aa4ab Update tsconfig
continuous-integration/drone/tag Build is failing
2024-10-25 10:22:16 +02:00
Arnie 0d74fd01bc Change the nav sticky behavior
continuous-integration/drone/tag Build is passing
2024-10-24 14:35:43 +02:00
Arnie ea5fed1bf9 Formatting
continuous-integration/drone/tag Build is passing
2024-10-24 13:20:19 +02:00
Arnie 373f01c9c2 Update skills, page breaks and add profile picture
continuous-integration/drone/tag Build is failing
2024-10-24 13:15:06 +02:00
Arnie df24388d95 Fix translation ids 2024-10-21 14:52:24 +02:00
16 changed files with 282 additions and 205 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

+58 -27
View File
@@ -3,7 +3,7 @@ import { defineMessages, useIntl } from "react-intl";
import { Content } from "../common/Content"; import { Content } from "../common/Content";
import { Headline, SubHeadline } from "../common/Headline"; import { Headline, SubHeadline } from "../common/Headline";
import { Spacer } from "../common/Spacer"; import PageBreakAvoid from "../common/PageBreakAvoid";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
const messages = defineMessages({ const messages = defineMessages({
@@ -25,44 +25,69 @@ const approxCurrentYears = {
), ),
}; };
const yourpassSkills = [ const investbaySkills = [
"DevOps", "DevOps",
"Front-End Development", "Google Cloud Platform (GCP)",
"Back-end Operations", "Amazon Web Services (AWS)",
"Software Development", "Gitlab",
"Kubernetes", "Terraform",
"TypeScript", "Cloud infrastructure",
"Go", "Cloud Applications",
"Linux", "Software Development Life Cycle (SDLC)",
"Linux Server",
"Linux System Administration", "Linux System Administration",
"Back-End Web Development", "Go",
"Git", "TypeScript",
"Nix",
"GraphQL",
"gRPC",
"PostgreSQL", "PostgreSQL",
"Node.js", "Node.js",
"React.js", "React.js",
];
const yourpassSkills = [
"DevOps",
"Software as a Service (SaaS)",
"Cloud Infrastructure",
"Kubernetes",
"Back-end Operations",
"Linux System Administration",
"Amazon Web Services (AWS)", "Amazon Web Services (AWS)",
"Amazon EKS", "Terraform",
"Go (Programming Language)",
"gRPC",
"Front-End Development",
"React.js",
"Node.js",
"TypeScript",
"Nix",
"Software Development Life Cycle (SDLC)",
"Software Development",
"Git",
"PostgreSQL",
]; ];
const yoursystemSkills = [ const yoursystemSkills = [
"DevOps", "DevOps",
"Front-End Development", "Docker Swarm",
"Linux System Administration",
"Back-end Operations", "Back-end Operations",
"Front-End Development",
"Amazon Web Services (AWS)",
"Bash",
"Linux Server",
"Terraform",
"Cloud Infrastructure",
"Cloud Applications",
"Go (Programming Language)",
"TypeScript",
"Node.js",
"Software Development Life Cycle (SDLC)",
"Software Development", "Software Development",
"Kubernetes", "Kubernetes",
"TypeScript",
"Linux",
"Linux Server",
"Linux System Administration",
"Back-End Web Development", "Back-End Web Development",
"GraphQL",
"Git", "Git",
"Node.js",
"Vue.js",
"PHP",
"Bash",
"Amazon Web Services (AWS)",
"Amazon EKS",
]; ];
const Experience: React.FC = () => { const Experience: React.FC = () => {
@@ -70,6 +95,7 @@ const Experience: React.FC = () => {
return ( return (
<> <>
<PageBreakAvoid>
<Headline level={3}>INVESTBAY s.r.o.</Headline> <Headline level={3}>INVESTBAY s.r.o.</Headline>
<SubHeadline level={4}>DevOps Architect</SubHeadline> <SubHeadline level={4}>DevOps Architect</SubHeadline>
<Content> <Content>
@@ -87,10 +113,12 @@ const Experience: React.FC = () => {
</Paragraph> </Paragraph>
<Paragraph> <Paragraph>
<strong>{intl.formatMessage(messages.skills)}:</strong>{" "} <strong>{intl.formatMessage(messages.skills)}:</strong>{" "}
{yourpassSkills.join(" · ")} {investbaySkills.join(" · ")}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
<PageBreakAvoid>
<Headline level={3}>YOUR PASS s.r.o.</Headline> <Headline level={3}>YOUR PASS s.r.o.</Headline>
<SubHeadline level={4}>DevOps Engineer</SubHeadline> <SubHeadline level={4}>DevOps Engineer</SubHeadline>
<Content> <Content>
@@ -110,9 +138,9 @@ const Experience: React.FC = () => {
{yourpassSkills.join(" · ")} {yourpassSkills.join(" · ")}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
<Spacer /> <PageBreakAvoid>
<Headline level={3}>YOUR SYSTEM s.r.o.</Headline> <Headline level={3}>YOUR SYSTEM s.r.o.</Headline>
<SubHeadline level={4}>SysOps</SubHeadline> <SubHeadline level={4}>SysOps</SubHeadline>
@@ -148,7 +176,9 @@ const Experience: React.FC = () => {
{yoursystemSkills.join(" · ")} {yoursystemSkills.join(" · ")}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
<PageBreakAvoid>
<Headline level={3}>Past experience</Headline> <Headline level={3}>Past experience</Headline>
<SubHeadline level={4}>Developer</SubHeadline> <SubHeadline level={4}>Developer</SubHeadline>
@@ -167,6 +197,7 @@ const Experience: React.FC = () => {
})} })}
</Paragraph> </Paragraph>
</Content> </Content>
</PageBreakAvoid>
</> </>
); );
}; };
+3 -2
View File
@@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import PageBreakAvoid from "../common/PageBreakAvoid";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
@@ -11,13 +12,13 @@ const Skill: React.FC<React.PropsWithChildren<SkillProps>> = (props) => {
const { children, title } = props; const { children, title } = props;
return ( return (
<div> <PageBreakAvoid>
<strong>{title}</strong> <strong>{title}</strong>
<Spacer /> <Spacer />
{React.Children.map(children, (c) => ( {React.Children.map(children, (c) => (
<Paragraph>{c}</Paragraph> <Paragraph>{c}</Paragraph>
))} ))}
</div> </PageBreakAvoid>
); );
}; };
+5 -2
View File
@@ -3,6 +3,7 @@ import { useIntl } from "react-intl";
import { SubHeadline } from "../common/Headline"; import { SubHeadline } from "../common/Headline";
import { List, ListItem } from "../common/List"; import { List, ListItem } from "../common/List";
import PageBreakAvoid from "../common/PageBreakAvoid";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import { Paragraph } from "../common/Text"; import { Paragraph } from "../common/Text";
import Skill from "./Skill"; import Skill from "./Skill";
@@ -69,12 +70,12 @@ const Skills: React.FC = () => {
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: defaultMessage:
"Deep knowledge of backend operations, mostly supporting web based applications using various technologies, programming languages and frameworks.", "Deep knowledge of backend operations, mostly supporting web based applications using various technologies, programming languages and frameworks.",
id: "Skills.backendDevelopment", id: "Skills.backendDevelopmentSum1",
})} })}
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: defaultMessage:
"In recent years, the main focus was on Go to write tooling and backend services.", "In recent years, the main focus was on Go to write tooling and backend services.",
id: "Skills.backendDevelopment", id: "Skills.backendDevelopmentSum2",
})} })}
</Skill> </Skill>
<Skill title="Infrastructure operations"> <Skill title="Infrastructure operations">
@@ -99,8 +100,10 @@ const Skills: React.FC = () => {
<List> <List>
{otherSkills.map((s) => ( {otherSkills.map((s) => (
<ListItem key={s.title}> <ListItem key={s.title}>
<PageBreakAvoid>
<strong>{s.title}</strong> <strong>{s.title}</strong>
{s.description && `: ${s.description}`} {s.description && `: ${s.description}`}
</PageBreakAvoid>
</ListItem> </ListItem>
))} ))}
</List> </List>
+1 -2
View File
@@ -38,8 +38,7 @@ const Summary: React.FC = () => {
knowledge of KNX systems. knowledge of KNX systems.
</Paragraph> </Paragraph>
<Caption> <Caption>
-- This summary was graciously generated by an AI and then rewritten and -- Summary graciously generated by an AI and curated by human
adjusted by a human
</Caption> </Caption>
</> </>
); );
@@ -23,7 +23,7 @@ const Nav = styled("nav")({
flex: "0 0 280px", flex: "0 0 280px",
width: 280, width: 280,
minHeight: "100%", minHeight: "100%",
padding: "3rem 2rem 2rem", padding: "0 2rem 2rem",
[hideNavigationMQ]: { [hideNavigationMQ]: {
display: "none", display: "none",
@@ -1,27 +1,32 @@
import styled from "@emotion/styled";
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import profile from "../../assets/profile.jpg";
import { CONTACT_EMAIL, CONTACT_PHONE } from "../../config/environment"; import { CONTACT_EMAIL, CONTACT_PHONE } from "../../config/environment";
import { Contact } from "../common/Contact"; import { Contact } from "../common/Contact";
import { List, ListItem } from "../common/List"; import { List, ListItem } from "../common/List";
import { Spacer } from "../common/Spacer"; import { Spacer } from "../common/Spacer";
import NavigationHeadline from "./NavigationHeadline"; import NavigationHeadline from "./NavigationHeadline";
const getTopOffset = (input: HTMLElement) => { const ProfileImg = styled("img")({
let el: HTMLElement | Element | null = input; borderRadius: "50%",
let o = 0; border: "2px solid white",
do { display: "block",
if (!(el instanceof HTMLElement)) { margin: "0 auto",
break; maxWidth: "100%",
} width: 150,
});
if (!isNaN(el.offsetTop)) { const navBottomPadding = 50;
o += el.offsetTop;
}
} while ((el = el.offsetParent));
return o; const TopSpacer = styled("div")({
}; height: 40,
});
const StickyWrapper = styled("div")({
position: "sticky",
});
const MainNavigation: React.FC = () => { const MainNavigation: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
@@ -34,49 +39,30 @@ const MainNavigation: React.FC = () => {
return; return;
} }
d.style.top = "0px";
let { height } = d.getBoundingClientRect(); let { height } = d.getBoundingClientRect();
const listener = () => {
const visibleArea = navBottomPadding + height;
if (window.innerHeight > visibleArea) {
d.style.top = "0px";
} else {
d.style.top = `${window.innerHeight - visibleArea}px`;
}
};
const ro = new ResizeObserver((entries) => { const ro = new ResizeObserver((entries) => {
if (entries[0]) { if (entries[0]) {
height = entries[0].contentRect.height; height = entries[0].contentRect.height;
listener();
} }
}); });
ro.observe(d); ro.observe(d);
let scrollPrev = window.scrollY; window.addEventListener("scroll", listener, { passive: true });
window.addEventListener("resize", listener, { passive: true });
const listener = () => {
const down = scrollPrev < window.scrollY;
scrollPrev = window.scrollY;
const top = getTopOffset(d);
if (window.innerHeight > height) {
d.style.marginTop = `${Math.max(window.scrollY, 0)}px`;
return;
}
if (down) {
const delta = top + height;
const wDelta = window.scrollY + window.innerHeight;
const diff = wDelta - delta;
if (diff > 0) {
const sizeDelta = Math.min(window.innerHeight - height, 0);
d.style.marginTop = `${Math.max(top + diff + sizeDelta, 0)}px`;
}
} else {
const delta = top;
const wDelta = window.scrollY;
const diff = wDelta - delta;
if (diff < 0) {
d.style.marginTop = `${Math.max(top + diff, 0)}px`;
}
}
};
window.addEventListener("scroll", listener);
return () => { return () => {
window.removeEventListener("scroll", listener); window.removeEventListener("scroll", listener);
@@ -85,7 +71,10 @@ const MainNavigation: React.FC = () => {
}, []); }, []);
return ( return (
<div ref={ref}> <StickyWrapper ref={ref}>
<TopSpacer />
<ProfileImg src={profile} alt="profile picture" />
<NavigationHeadline> <NavigationHeadline>
{intl.formatMessage({ {intl.formatMessage({
defaultMessage: "Contact", defaultMessage: "Contact",
@@ -149,7 +138,7 @@ const MainNavigation: React.FC = () => {
id: "Navigation.experience", id: "Navigation.experience",
})} })}
</NavigationHeadline> </NavigationHeadline>
</div> </StickyWrapper>
); );
}; };
@@ -0,0 +1,7 @@
import styled from "@emotion/styled";
const PageBreakAvoid = styled("div")({
pageBreakInside: "avoid",
});
export default PageBreakAvoid;
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"composite": true,
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2021", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
+5 -21
View File
@@ -1,23 +1,7 @@
{ {
"compilerOptions": { "files": [],
"target": "ES2020", "references": [
"useDefineForClassFields": true, { "path": "./tsconfig.app.json" },
"lib": ["ES2020", "DOM", "DOM.Iterable"], { "path": "./tsconfig.node.json" }
"module": "ESNext", ]
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
} }
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"composite": true,
"allowSyntheticDefaultImports": true,
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
+1 -2
View File
@@ -7,10 +7,9 @@ import (
"os" "os"
"time" "time"
"go.c3c.cz/cv/app/server/internal/version"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"go.c3c.cz/cv/app/server/internal/version"
) )
type FrontendConfigDef struct { type FrontendConfigDef struct {
@@ -14,10 +14,9 @@ import (
"strings" "strings"
"time" "time"
"go.uber.org/zap"
"go.c3c.cz/cv/app/server/internal/files" "go.c3c.cz/cv/app/server/internal/files"
"go.c3c.cz/cv/app/server/internal/version" "go.c3c.cz/cv/app/server/internal/version"
"go.uber.org/zap"
) )
type handler struct { type handler struct {
@@ -67,7 +66,6 @@ func (h *handler) serveFile(w http.ResponseWriter, r *http.Request, modtime time
gw.Reset(io.Discard) gw.Reset(io.Discard)
} }
err = gw.Close() err = gw.Close()
if err != nil { if err != nil {
h.logger.Warn("Could not close gzip writer", zap.Error(err)) h.logger.Warn("Could not close gzip writer", zap.Error(err))
return return
+1 -2
View File
@@ -10,9 +10,8 @@ import (
"net/http" "net/http"
"time" "time"
"go.uber.org/zap"
"go.c3c.cz/cv/app/server/internal/files" "go.c3c.cz/cv/app/server/internal/files"
"go.uber.org/zap"
) )
func init() { func init() {
+1 -3
View File
@@ -8,12 +8,11 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"go.uber.org/zap"
"go.c3c.cz/cv/app/server/internal/config" "go.c3c.cz/cv/app/server/internal/config"
"go.c3c.cz/cv/app/server/internal/httpserver" "go.c3c.cz/cv/app/server/internal/httpserver"
"go.c3c.cz/cv/app/server/internal/pprofserver" "go.c3c.cz/cv/app/server/internal/pprofserver"
"go.c3c.cz/cv/app/server/internal/version" "go.c3c.cz/cv/app/server/internal/version"
"go.uber.org/zap"
) )
func main() { func main() {
@@ -33,7 +32,6 @@ func main() {
var err error var err error
logger, err = config.LoggerConfig.Build() logger, err = config.LoggerConfig.Build()
if err != nil { if err != nil {
fmt.Println(notice) fmt.Println(notice)
fmt.Println(version.CommitTime.Format("Committed at 2006/01/02 15:04:05 MST")) fmt.Println(version.CommitTime.Format("Committed at 2006/01/02 15:04:05 MST"))
+19 -3
View File
@@ -8,7 +8,8 @@
{ {
formatter = nix.formatter; formatter = nix.formatter;
packages = nix.lib.forAllSystems (pkgs: packages = nix.lib.forAllSystems (
pkgs:
let let
version = "rev-${self.shortRev or self.dirtyShortRev}"; version = "rev-${self.shortRev or self.dirtyShortRev}";
package = builtins.fromJSON (builtins.readFile ./app/frontend/package.json); package = builtins.fromJSON (builtins.readFile ./app/frontend/package.json);
@@ -42,7 +43,9 @@
"-w" "-w"
"-X gopkg.c3c.cz/cv/app/server/internal/version.Tag=${version}" "-X gopkg.c3c.cz/cv/app/server/internal/version.Tag=${version}"
"-X gopkg.c3c.cz/cv/app/server/internal/version.Commit=${self.rev or self.dirtyRev}" "-X gopkg.c3c.cz/cv/app/server/internal/version.Commit=${self.rev or self.dirtyRev}"
"-X gopkg.c3c.cz/cv/app/server/internal/version.commitTime=${builtins.toString (self.lastModified or 0)}" "-X gopkg.c3c.cz/cv/app/server/internal/version.commitTime=${
builtins.toString (self.lastModified or 0)
}"
]; ];
vendorHash = "sha256-44xcyVk5KcurQLkVJv1MeAj+Pfcu53664pvVgHdyv3E="; vendorHash = "sha256-44xcyVk5KcurQLkVJv1MeAj+Pfcu53664pvVgHdyv3E=";
}; };
@@ -70,6 +73,19 @@
]; ];
commands = [ commands = [
{
# Override golangci-lint for vscode, because the extension incorrectly assumes usage of global binaries is preferred
name = "golangci-lint";
command = ''
CMD=''${1:-}
if [[ "$CMD" == "run" ]]; then
shift
${pkgs.golangci-lint}/bin/golangci-lint run --config ${nix.lib.golangci-config-file} $@
else
${pkgs.golangci-lint}/bin/golangci-lint $@
fi
'';
}
{ {
name = "lint"; name = "lint";
help = "run linters"; help = "run linters";
@@ -84,7 +100,7 @@
help = "format & fix found issues"; help = "format & fix found issues";
command = '' command = ''
${nix.lib.cd_root} ${nix.lib.cd_root}
nix fmt . nix fmt ./*.nix
${pkgs.golangci-lint}/bin/golangci-lint run --sort-results --out-format tab --config ${nix.lib.golangci-config-file} --fix --issues-exit-code 0 ./... ${pkgs.golangci-lint}/bin/golangci-lint run --sort-results --out-format tab --config ${nix.lib.golangci-config-file} --fix --issues-exit-code 0 ./...
npm --prefix app/frontend run fix npm --prefix app/frontend run fix
''; '';