diff --git a/package.json b/package.json index 0b48a83..8a1f8d6 100644 --- a/package.json +++ b/package.json @@ -39,5 +39,8 @@ "onlyBuiltDependencies": [ "esbuild" ] + }, + "dependencies": { + "@lucide/svelte": "^0.507.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 721151f..cc6ae09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + '@lucide/svelte': + specifier: ^0.507.0 + version: 0.507.0(svelte@5.28.2) devDependencies: '@eslint/compat': specifier: ^1.2.5 @@ -64,7 +68,7 @@ importers: version: 5.8.3 typescript-eslint: specifier: ^8.20.0 - version: 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) vite: specifier: ^6.2.6 version: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) @@ -310,6 +314,11 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@lucide/svelte@0.507.0': + resolution: {integrity: sha512-kXIg/2IHEo1bAwZG94fjA9O6+E3Sv5aQrW2yyPf4JvCYIUIF8DGLIQI4vfjoUyNq2dEX4cI33pZarzGOHr0npg==} + peerDependencies: + svelte: ^5 + '@modelcontextprotocol/sdk@1.11.0': resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} engines: {node: '>=18'} @@ -606,51 +615,51 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@typescript-eslint/eslint-plugin@8.31.1': - resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + '@typescript-eslint/eslint-plugin@8.32.0': + resolution: {integrity: sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.31.1': - resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + '@typescript-eslint/parser@8.32.0': + resolution: {integrity: sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.31.1': - resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} + '@typescript-eslint/scope-manager@8.32.0': + resolution: {integrity: sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.31.1': - resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + '@typescript-eslint/type-utils@8.32.0': + resolution: {integrity: sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.31.1': - resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} + '@typescript-eslint/types@8.32.0': + resolution: {integrity: sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.31.1': - resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} + '@typescript-eslint/typescript-estree@8.32.0': + resolution: {integrity: sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.31.1': - resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} + '@typescript-eslint/utils@8.32.0': + resolution: {integrity: sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.31.1': - resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} + '@typescript-eslint/visitor-keys@8.32.0': + resolution: {integrity: sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} accepts@2.0.0: @@ -1635,8 +1644,8 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - typescript-eslint@8.31.1: - resolution: {integrity: sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==} + typescript-eslint@8.32.0: + resolution: {integrity: sha512-UMq2kxdXCzinFFPsXc9o2ozIpYCCOiEC46MG3yEh5Vipq6BO27otTtEBZA1fQ66DulEUgE97ucQ/3YY66CPg0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1900,6 +1909,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@lucide/svelte@0.507.0(svelte@5.28.2)': + dependencies: + svelte: 5.28.2 + '@modelcontextprotocol/sdk@1.11.0': dependencies: content-type: 1.0.5 @@ -2157,14 +2170,14 @@ snapshots: '@types/resolve@1.20.2': {} - '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/type-utils': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 eslint: 9.26.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 @@ -2174,27 +2187,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 debug: 4.4.0 eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.31.1': + '@typescript-eslint/scope-manager@8.32.0': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 - '@typescript-eslint/type-utils@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 eslint: 9.26.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.3) @@ -2202,12 +2215,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.31.1': {} + '@typescript-eslint/types@8.32.0': {} - '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.32.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -2218,20 +2231,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.31.1': + '@typescript-eslint/visitor-keys@8.32.0': dependencies: - '@typescript-eslint/types': 8.31.1 + '@typescript-eslint/types': 8.32.0 eslint-visitor-keys: 4.2.0 accepts@2.0.0: @@ -3183,11 +3196,11 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.1 - typescript-eslint@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: diff --git a/src/app.html b/src/app.html index 77a5ff5..b5ec995 100644 --- a/src/app.html +++ b/src/app.html @@ -6,7 +6,7 @@ %sveltekit.head% - +
%sveltekit.body%
diff --git a/src/lib/index.ts b/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/linkedin.jpg b/src/lib/linkedin.jpg new file mode 100644 index 0000000..f2d6755 Binary files /dev/null and b/src/lib/linkedin.jpg differ diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b93e9ba..251a765 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,4 +4,6 @@ let { children } = $props(); -{@render children()} +
+ {@render children()} +
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index cc88df0..e3b3211 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,2 +1,26 @@ -

Welcome to SvelteKit

-

Visit svelte.dev/docs/kit to read the documentation

+ + +
+ + + +
diff --git a/src/routes/Badge.svelte b/src/routes/Badge.svelte new file mode 100644 index 0000000..09258c7 --- /dev/null +++ b/src/routes/Badge.svelte @@ -0,0 +1,7 @@ + + +
+ {@render children()} +
diff --git a/src/routes/Card.svelte b/src/routes/Card.svelte new file mode 100644 index 0000000..4bebec1 --- /dev/null +++ b/src/routes/Card.svelte @@ -0,0 +1,7 @@ + + +
+ {@render children()} +
diff --git a/src/routes/JobCard.svelte b/src/routes/JobCard.svelte new file mode 100644 index 0000000..361e1ca --- /dev/null +++ b/src/routes/JobCard.svelte @@ -0,0 +1,40 @@ + + + +
+

{title} @ {company}

+

{start} - {end}

+
+

Job Description

+

+ {jobDescription} +

+

Accomplishments

+ +
diff --git a/src/routes/PageHeader.svelte b/src/routes/PageHeader.svelte new file mode 100644 index 0000000..e480b9c --- /dev/null +++ b/src/routes/PageHeader.svelte @@ -0,0 +1,35 @@ + + + +
+
+
+

Developer Resume For Christian Rannes

+

+ Full Stack Developer Passionate about infrastructure, DevOps and Developer Experience +

+
+ +
+
+ a happy guy +
+
+
diff --git a/src/routes/qiData.ts b/src/routes/qiData.ts new file mode 100644 index 0000000..483c272 --- /dev/null +++ b/src/routes/qiData.ts @@ -0,0 +1,49 @@ +import type { Accomplishment } from './types'; + +export const qiDescription = + 'As a Full Stack Developer at Quantified Impacts I have touched every part of our code, from infrastructure and CI/CD to our Frontend in react/typescript and our backend in Django/Python. I am fast at learning new things, and now to tackle challenges, and come up with fast, durable and scaleable solutions. When we need to research or develop POCs for new features, I am often assigned the lead for these tasks. '; + +export const qiAccomplish: Accomplishment[] = [ + { + title: 'Planned and facilitated maturation of infrastructure.', + description: + 'When I started with Quantified Impacts we had our application running in two container apps, and a postgresql db, which was all exposed to the world wide web. I planned out our new infrastructure and hired a consultant to do the initial implementation in terraform, securing all services in virtual networks, connected with private end-points and accesible only through Azure Frontdoor. This is all declared using Terraform, so we can easily spin up another environment.', + tags: ['Terraform', 'Azure'] + }, + { + title: 'Implemented CI/CD Pipeline', + description: + "As we're a small team that wants to get our newest features out to our users as fast as possible, I set up a build and release pipeline using github actions.", + tags: ['Github Actions', 'Azure'] + }, + { + title: 'External API v1', + description: + 'I built our initial external api as an application in our Django backend. It is used by our largest clients, who are making hundreds of thousands requests overnight. The API has full test coverage of unit tests and End2End tests written i pytest.', + tags: ['Python', 'Django', 'REST'] + }, + { + title: 'Redesign of our datamodels and database', + description: + 'In January started planning a big rewrite of our backend and data models, to facilitate millions of carbon footprint calculations, storing calculation parameters, history and changelog for all objects.', + tags: ['Postgresql', 'Django', 'Python'] + }, + { + title: 'Migration of frontend to React Router 7 framework and SSR', + description: + 'After a big rewrite of our backend, we decided to move to the newest version of React Router and use it as framework, which required an almost full rewrite of our frontend. While rewriting the frontend we also adopted Shadcn for ui components and tailwindcss for styling.', + tags: ['React Router', 'React', 'typescript', 'Tailwind'] + }, + { + title: 'External API v2', + description: + 'The second iteration of our external API is written in FastAPI for fast development speed, and better performance compared to Django.', + tags: ['FastAPI', 'Pydantic', 'Python', 'REST'] + }, + { + title: 'Data fetching service from OKOBAUDAT', + description: + 'A container job (Cron job) that fetches thousands of EPD reports from OKOBAUDAT every night, transform them to fit our data models, and inserts or update them in our database. The service is written in Golang.', + tags: ['Golang', 'Azure', 'Cron Jobs'] + } +]; diff --git a/src/routes/rannesDevData.ts b/src/routes/rannesDevData.ts new file mode 100644 index 0000000..ff69ff1 --- /dev/null +++ b/src/routes/rannesDevData.ts @@ -0,0 +1,19 @@ +import type { Accomplishment } from './types'; + +export const rannesDescription = + 'As I got a request for a web site, and another for a code review I took the opportunity to start a little consultant business. I develop solutions and web sites for restaurants, and provide technical consulting for early stage start ups. My production applications are deployed to a k3s cluster that are running on a couple of Hetzner vps.'; + +export const rannesAccomplish: Accomplishment[] = [ + { + title: 'Developed, Deployed and maintaining Calma.dk', + description: + 'The web page is developed in Svelte with integrated CMS (Sanity.io) which allows the client to customize all text, and menus on the page. I also wrote a program in Golang which parses their winelist from an excel sheet (their stock take list), and uploads it to a s3 bucket from where its served to the user on the web page. This enables the client to make instant changes to their website, including their winelist. The page is built mobile first, and scales for all devices. It is deployed to my k3s cluster in hetzner', + tags: ['Svelte', 'Sveltekit', 'AWS', 'k3s', 'Object Storage', 'CMS'] + }, + { + title: 'Developed, deployed and maintaining Sauerwine.dk', + description: + 'The web page is developed in Svelte and integrated with CMS (Sanity.io) so the client can change all text content on the web page. It is deployed to my k3s cluster in hetzner.', + tags: ['Svelte', 'Sveltekit', 'k3s', 'CMS'] + } +]; diff --git a/src/routes/types.ts b/src/routes/types.ts new file mode 100644 index 0000000..df2aef2 --- /dev/null +++ b/src/routes/types.ts @@ -0,0 +1,5 @@ +export type Accomplishment = { + title: string; + description: string; + tags: string[]; +};