Added cv view (not indexed) #21

Merged
robin merged 1 commits from dev into main 2026-09-05 10:46:12 +02:00
4 changed files with 595 additions and 0 deletions
Showing only changes of commit deb0bac072 - Show all commits
+2
View File
@@ -0,0 +1,2 @@
User-agent: *
Disallow: /cv
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

+5
View File
@@ -29,6 +29,11 @@ const router = createRouter({
name: 'login',
component: () => import('../views/LoginView.vue'),
},
{
path: '/cv',
name: 'cv',
component: () => import('../views/CvView.vue'),
},
{
path: '/account',
name: 'account',
+588
View File
@@ -0,0 +1,588 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue'
import cvPhoto from '@/assets/cv-photo.jpg'
// This is a public sub-page with no link from the rest of the app, and it
// shouldn't show up in search results. index.html is shared across every
// route, so the noindex meta tag has to be added/removed here rather than
// living statically in the HTML head.
let robotsMeta: HTMLMetaElement | null = null
onMounted(() => {
robotsMeta = document.createElement('meta')
robotsMeta.name = 'robots'
robotsMeta.content = 'noindex, nofollow'
document.head.appendChild(robotsMeta)
})
onBeforeUnmount(() => {
robotsMeta?.remove()
})
interface ExperienceEntry {
company: string
location: string
title: string
dates: string
subtitle: string
bullets: string[]
}
interface EducationEntry {
school: string
dates: string
degree: string
}
interface Achievement {
title: string
desc: string
}
interface Language {
name: string
level: string
rating: number
}
interface Skill {
name: string
rating: number
}
interface AdditionalExperience {
title: string
desc: string
}
const SKILL_MAX = 8
const LANGUAGE_MAX = 5
const experience: ExperienceEntry[] = [
{
company: 'Greenbone AG',
location: 'Remote, Germany',
title: 'Senior C Software Engineer',
dates: '08/2025 - Present',
subtitle: 'Open-Source Vulnerability Management',
bullets: [
"Improved vulnerability management for customers and community users by working on open- and closed-source projects like 'gvmd', 'gsad', and 'gsa'.",
'Maintain old legacy systems in C, as well as building new components in Rust, which align with the ecosystem.',
'Improve GitHub Actions CI/CD pipelines.',
],
},
{
company: 'Aspen Technology',
location: 'Cologne, Germany',
title: 'Sr Software Developer',
dates: '04/2025 - 07/2025',
subtitle: 'Asset Optimization Software',
bullets: [
'Maintenance work and feature development for "inmation" Backend, a scalable solution for asset optimization.',
],
},
{
company: 'Ubisoft',
location: 'Düsseldorf, Germany',
title: 'Programmer',
dates: '10/2020 - 07/2024',
subtitle: 'AAA Game Developer/Publisher',
bullets: [
'Designed and implemented new backend services, resulting in less development time for business features.',
'Overseen the architecture and deployment of software solutions handling over 600k concurrent users.',
'Reduced processing time by 30% by optimizing database queries and scripts.',
'Improved system stability by 10%, resulting in increased user engagement and satisfaction.',
'Enhanced C++ Backend functionality, resulting in a 5% decrease in response times.',
],
},
{
company: '11880 Internet Services AG',
location: 'Essen, Germany',
title: 'C++ Developer',
dates: '06/2018 - 09/2020',
subtitle: '11880.com Information Portal',
bullets: [
'Maintained and developed new features of an in-memory search engine written in C++.',
'Lifting on-premises services to the cloud, resulting in 30% more uptime and reduced costs.',
],
},
{
company: 'Fluxatron Softec GmbH',
location: 'Oberhausen, Germany',
title: 'Software Developer Working Student',
dates: '03/2016 - 09/2017',
subtitle: 'Software Startup',
bullets: [
'Implement and design add-ons for Dynamics AX 365 for Business and Operations using X++.',
'Enabled operational efficiency by reducing feature deployment time by 20% through end-to-end feature implementation collaboration.',
],
},
]
const education: EducationEntry[] = [
{
school: 'Hochschule Ruhr West University of Applied Sciences',
dates: '10/2015 - 05/2018',
degree: 'Applied Informatics (no degree)',
},
{
school: 'Hans-Sachs Berufskolleg',
dates: '08/2012 - 06/2015',
degree: 'Fachabitur + Informationstechnischer Assistent',
},
]
const achievements: Achievement[] = [
{
title: 'Design New Services',
desc: 'Successfully implemented two new services within three months.',
},
{
title: 'Server Optimization Success',
desc: 'Reduced server downtime by 10% by optimizing code.',
},
{
title: 'Performance Enhancement Achievement',
desc: 'Improved application performance by 50% through code refactoring.',
},
]
const languages: Language[] = [
{ name: 'German', level: 'Native', rating: 5 },
{ name: 'English', level: 'Proficient', rating: 4 },
{ name: 'Spanish', level: 'Beginner', rating: 1 },
]
const interests: string[] = [
'Software Development',
'Linux',
'Open Source',
'Raspberry Pi',
'Rock climbing',
'Calisthenics',
]
const skills: Skill[] = [
{ name: 'Golang', rating: 5 },
{ name: 'C++17', rating: 8 },
{ name: 'C++20', rating: 5 },
{ name: 'C++ STL', rating: 7 },
{ name: 'C++ boost', rating: 4 },
{ name: 'C++ POCO', rating: 4 },
{ name: 'C (C89 to C11)', rating: 7 },
{ name: 'SQL', rating: 5 },
{ name: 'TypeScript', rating: 5 },
{ name: 'Docker', rating: 6 },
{ name: 'Linux', rating: 7 },
{ name: 'git', rating: 7 },
{ name: 'HTTP/REST', rating: 7 },
{ name: 'Amazon Web Services (AWS)', rating: 4 },
{ name: 'Node.js', rating: 4 },
{ name: 'Vue.js', rating: 5 },
{ name: 'CMake', rating: 5 },
{ name: 'MongoDB', rating: 4 },
{ name: 'Python', rating: 6 },
{ name: 'Rust', rating: 4 },
{ name: 'C#', rating: 3 },
{ name: 'ELK stack', rating: 3 },
{ name: 'DevOps', rating: 5 },
{ name: 'CI/CD with gitlab/jenkins', rating: 6 },
{ name: 'GitHub Actions', rating: 6 },
{ name: 'Jetbrains IDEs', rating: 7 },
{ name: 'VSCode', rating: 5 },
{ name: 'Visual Studio', rating: 5 },
{ name: 'vim/neovim', rating: 5 },
{ name: 'MS Windows', rating: 5 },
{ name: 'perf', rating: 5 },
{ name: 'valgrind', rating: 4 },
{ name: 'C++ SFML', rating: 6 },
{ name: 'OpenGL', rating: 4 },
{ name: 'Vulkan', rating: 2 },
{ name: 'DirectX', rating: 2 },
{ name: 'HTML5/CSS3', rating: 4 },
{ name: 'Claude Code', rating: 4 },
]
const additionalExperiences: AdditionalExperience[] = [
{
title: 'Minecraft Mod',
desc: 'Around 2012 I have developed and published the "Blood Mod" for Minecraft. The mod was pretty popular at the time, being downloaded thousands of times.',
},
{
title: 'Buy script generator',
desc: 'Around 2013, I have developed a tool to generate so-called "buy scripts" for Counter-Strike. This was my first experience creating C++ GUI applications, using the Win32 API.',
},
{
title: 'Small 2D games',
desc: 'Around 2014-2015 I became interested in game development and have created a few simple 2D games using C++ and SFML.',
},
{
title: 'Self-hosted services',
desc: 'For years, I have been hosting my own git and password manager at home, utilizing a Raspberry Pi with Docker. In addition, I have a VPS with nginx and WireGuard, for access from anywhere around the world.',
},
]
</script>
<template>
<main class="page cv-page">
<div class="profile-top">
<img class="avatar" :src="cvPhoto" alt="Portrait of Robin Dittmar" />
<div>
<h1>Robin Dittmar</h1>
<span class="tag tag-accent">Software Developer</span>
</div>
</div>
<section class="card info-card">
<h4>Contacts</h4>
<p class="row">
<span>Phone</span>
<strong class="mono-num">+49159/01668799</strong>
</p>
<p class="row">
<span>Email</span>
<strong>robindittmar@gmail.com</strong>
</p>
<p class="row">
<span>Location</span>
<strong>Düsseldorf</strong>
</p>
<p class="row">
<span>LinkedIn</span>
<a href="https://www.linkedin.com/in/robindittmar-948424310/" target="_blank" rel="noopener"
>robindittmar</a
>
</p>
<p class="row">
<span>GitHub</span>
<a href="https://github.com/rdttmr" target="_blank" rel="noopener">rdttmr</a>
</p>
</section>
<h1 class="section-heading">Summary</h1>
<section class="card info-card">
<p class="summary-text">
Passionate Software Developer professional with over 8 years of experience in backend
development and software architecture. Expert in C++, strong TypeScript skills, seasoned
Python experience and aspiring Go professional. My key achievements include designing and
implementing new backend services resulting in reduced development time, and optimizing
systems to handle over 600k concurrent users.
</p>
</section>
<h1 class="section-heading">Experience</h1>
<ul class="cards">
<li v-for="job in experience" :key="job.company + job.dates">
<article class="card job-card">
<div class="job-card-head">
<h3>{{ job.company }}</h3>
<span class="mono-num job-dates">{{ job.dates }}</span>
</div>
<p class="job-title">{{ job.title }} · {{ job.location }}</p>
<p class="job-subtitle">{{ job.subtitle }}</p>
<ul class="job-bullets">
<li v-for="(bullet, idx) in job.bullets" :key="idx">{{ bullet }}</li>
</ul>
</article>
</li>
</ul>
<h1 class="section-heading">Education</h1>
<ul class="cards">
<li v-for="edu in education" :key="edu.school">
<article class="card job-card">
<div class="job-card-head">
<h3>{{ edu.school }}</h3>
<span class="mono-num job-dates">{{ edu.dates }}</span>
</div>
<p class="job-subtitle">{{ edu.degree }}</p>
</article>
</li>
</ul>
<h1 class="section-heading">Key Achievements</h1>
<section class="card info-card">
<div v-for="item in achievements" :key="item.title" class="achievement-row">
<h4>{{ item.title }}</h4>
<p>{{ item.desc }}</p>
</div>
</section>
<h1 class="section-heading">Languages</h1>
<section class="card info-card">
<div v-for="lang in languages" :key="lang.name" class="rating-row">
<p class="row">
<span>{{ lang.name }}</span>
<span class="tag">{{ lang.level }}</span>
</p>
<div class="meter meter-lg">
<div class="meter-fill" :style="{ width: (lang.rating / LANGUAGE_MAX) * 100 + '%' }"></div>
</div>
</div>
</section>
<h1 class="section-heading">Industry Experience</h1>
<section class="card info-card">
<div class="skill-grid">
<div v-for="skill in skills" :key="skill.name" class="skill-row">
<span class="skill-name">{{ skill.name }}</span>
<div class="meter">
<div class="meter-fill" :style="{ width: (skill.rating / SKILL_MAX) * 100 + '%' }"></div>
</div>
</div>
</div>
</section>
<h1 class="section-heading">Additional Experience</h1>
<ul class="cards">
<li v-for="item in additionalExperiences" :key="item.title">
<article class="card job-card">
<h3>{{ item.title }}</h3>
<p class="job-subtitle">{{ item.desc }}</p>
</article>
</li>
</ul>
<h1 class="section-heading">Interests</h1>
<section class="card info-card">
<div class="tags">
<span v-for="interest in interests" :key="interest" class="tag">{{ interest }}</span>
</div>
</section>
</main>
</template>
<style scoped>
.cv-page {
padding-bottom: 2.5rem;
}
@media (min-width: 768px) {
.cv-page {
max-width: 780px;
}
}
.cv-page > h1:first-child {
font-size: 1.35rem;
}
.profile-top {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.25rem;
}
.profile-top h1 {
font-size: 1.4rem;
margin-bottom: 0.4rem;
}
.avatar {
width: 76px;
height: 76px;
border-radius: 50%;
object-fit: cover;
border: 1px solid var(--c-border);
box-shadow: var(--shadow-sm);
flex-shrink: 0;
}
.section-heading {
font-size: 1.1rem;
margin: 1.75rem 0 0.75rem;
}
.info-card {
padding: 1rem 1.1rem;
}
.info-card h4 {
font-size: 0.85rem;
margin-bottom: 0.75rem;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.75rem;
font-size: 0.85rem;
margin-bottom: 0.4rem;
color: var(--c-text);
}
.row:last-child {
margin-bottom: 0;
}
.row strong {
color: var(--c-heading);
}
.row a {
color: var(--c-accent-strong);
}
.summary-text {
font-size: 0.88rem;
color: var(--c-text);
}
.achievement-row + .achievement-row {
margin-top: 0.85rem;
}
.achievement-row h4 {
font-size: 0.85rem;
color: var(--c-heading);
margin-bottom: 0.15rem;
}
.achievement-row p {
font-size: 0.82rem;
color: var(--c-text-soft);
}
.rating-row + .rating-row {
margin-top: 0.85rem;
}
/* rating meters */
.meter {
width: 100%;
height: 6px;
border-radius: 999px;
background: var(--c-bg-mute);
overflow: hidden;
}
.meter-fill {
height: 100%;
border-radius: 999px;
background: var(--c-accent);
}
.meter-lg {
height: 7px;
}
/* cards list (experience / education / additional experience) */
.cards {
display: flex;
flex-direction: column;
gap: 0.6rem;
list-style: none;
padding: 0;
margin: 0;
}
.job-card {
padding: 0.9rem 1.1rem;
}
.job-card-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.75rem;
flex-wrap: wrap;
}
.job-card-head h3 {
font-size: 1.02rem;
}
.job-dates {
font-size: 0.75rem;
color: var(--c-text-soft);
white-space: nowrap;
}
.job-title {
font-size: 0.85rem;
color: var(--c-text);
margin-top: 0.15rem;
}
.job-subtitle {
font-size: 0.82rem;
font-style: italic;
color: var(--c-text-soft);
margin-top: 0.3rem;
}
.job-bullets {
list-style: none;
display: flex;
flex-direction: column;
gap: 0.3rem;
margin-top: 0.6rem;
font-size: 0.85rem;
color: var(--c-text);
}
.job-bullets li {
position: relative;
padding-left: 0.9rem;
}
.job-bullets li::before {
content: '';
position: absolute;
left: 0;
top: 0.55em;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--c-accent);
}
/* skills */
.skill-grid {
display: grid;
grid-template-columns: 1fr;
gap: 0.7rem;
}
@media (min-width: 560px) {
.skill-grid {
grid-template-columns: 1fr 1fr;
gap: 0.7rem 1.5rem;
}
}
.skill-row {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.skill-name {
font-size: 0.82rem;
color: var(--c-text);
}
/* tags */
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.tag {
display: inline-flex;
font-size: 0.72rem;
font-weight: 500;
padding: 0.2rem 0.6rem;
border-radius: 999px;
background-color: var(--c-bg-mute);
color: var(--c-text-soft);
}
.tag-accent {
background-color: var(--c-accent-bg);
color: var(--c-accent-strong);
}
</style>