78 Commits
v1.1 ... master

Author SHA1 Message Date
Manuel
fe10b0e017 Update README.md 2025-07-28 21:52:26 +02:00
146e455bb1 Update deps 2024-09-04 20:41:58 +02:00
7a57617da5 Update deps 2024-06-19 23:45:40 +02:00
4054a46516 Update deps 2024-06-14 00:40:54 +02:00
851873bc5a Update deps 2024-06-04 23:26:32 +02:00
15dccd4939 Update deps 2024-05-23 23:27:44 +02:00
76ebcdcca4 Update deps 2024-05-18 01:20:43 +02:00
c65b242a8d Update deps 2024-05-14 19:00:59 +02:00
f9b58db61f Update some titles & descriptions 2024-03-19 23:34:45 +01:00
0076490dc4 Update deps 2024-03-16 21:04:53 +01:00
ea95202f6b Update deps 2024-03-13 23:22:15 +01:00
cf03ba9662 Update deps 2024-03-02 23:35:24 +01:00
e00fb94b08 Update deps 2023-11-01 12:20:33 +01:00
ab3dd42df6 Update deps 2023-10-26 10:58:03 +02:00
7f9b2c2f53 Update deps 2023-10-12 20:06:55 +02:00
af944028e9 Update deps 2023-10-07 21:05:39 +02:00
a5a5921594 Update deps 2023-10-01 23:51:39 +02:00
6a5b1b755a Update deps 2023-09-26 20:20:24 +02:00
d8af58a8da Update deps 2023-09-24 20:38:52 +02:00
a1e8b47e43 Update deps 2023-09-22 22:24:07 +02:00
a594bc4aff Update deps 2023-09-20 11:15:50 +02:00
4ed5885f00 Update deps 2023-09-11 19:23:24 +02:00
baea30a690 Update deps 2023-09-05 23:43:47 +02:00
f5bf32edcf Update deps 2023-08-31 21:52:04 +02:00
114d4cb33e Update deps 2023-08-26 22:31:06 +02:00
4ecd3ccaed Update deps 2023-08-23 13:28:02 +02:00
96b3a237fb Update deps 2023-08-09 13:40:21 +02:00
77ae8be73d Update deps 2023-08-06 11:52:33 +02:00
176a6b73d4 Update deps 2023-07-31 21:56:02 +02:00
5d06ae512b Update deps 2023-07-28 01:24:44 +02:00
fc8ab04cde Update deps 2023-07-22 17:40:50 +02:00
517200f02e Update deps 2023-07-19 12:32:18 +02:00
e9f966d2a6 Update deps 2023-07-15 15:36:53 +02:00
07e4045198 Update deps 2023-07-14 12:00:24 +02:00
33db600f64 Update deps 2023-07-12 22:06:13 +02:00
256abf7e8c Update deps 2023-07-07 16:34:07 +02:00
289b336dfb Update deps 2023-07-04 09:14:46 +02:00
ce459e97f0 Update some headlines 2023-06-29 21:57:27 +02:00
aa0fc2579b Add node.js to skillset 2023-06-29 21:56:41 +02:00
0fb7cb24ed Update deps 2023-06-29 09:54:13 +02:00
cf786b52ff Update deps 2023-06-25 14:25:51 +02:00
4d6a372e6d Update deps 2023-06-22 10:21:42 +02:00
5576fc8327 Update deps 2023-06-17 19:18:13 +02:00
ea9306aac4 Update deps 2023-06-14 15:32:42 +02:00
84f52baf2a Update deps 2023-06-10 23:02:53 +02:00
253205baf3 Add Docker, remove WP from skillset 2023-06-09 00:31:58 +02:00
9e773db293 Update deps 2023-06-07 13:51:38 +02:00
d52bfe7353 Update deps 2023-06-01 20:41:10 +02:00
2ac82b4ad0 Update deps 2023-05-27 19:30:20 +02:00
1d30db8b63 Remove unnecessary comment 2023-05-26 17:16:56 +02:00
ace2829614 Add { "type": "module" } 2023-05-26 17:16:19 +02:00
5367707f17 Update deps 2023-05-24 12:58:45 +02:00
8dae14735e Update deps 2023-05-22 23:59:22 +02:00
2b3b639498 Update deps 2023-05-20 13:20:07 +02:00
4d164f83ab Update deps 2023-05-17 19:02:50 +02:00
7a3f41e1b4 Update deps 2023-05-16 11:03:21 +02:00
962ad5d436 Remove package @nuxtjs/axios 2023-05-11 23:58:39 +02:00
1353d9199c Update deps 2023-05-09 22:59:13 +02:00
048660c9df Add tech and update portfolio 2023-05-08 16:42:01 +02:00
06d92e19c4 Update @nuxtjs/i18n 2023-05-08 16:41:28 +02:00
c48c782154 Refactor variables 2023-05-08 16:10:37 +02:00
cca9967af6 Migrate environment vars to Nuxt runtimeConfig 2023-05-08 15:51:31 +02:00
f39e02c5b7 Add horizontal projects list layout 2023-05-08 01:09:25 +02:00
3111daaf02 Fix carousel controls obscuring the image 2023-05-08 00:51:54 +02:00
1d35d4926b Create ProjectCard.vue 2023-05-08 00:48:23 +02:00
ab50b61295 Change page content max width 2023-05-07 21:45:55 +02:00
97f3580f69 Add page transitions 2023-05-07 14:00:04 +02:00
83d6722d8c Update deps 2023-05-07 11:40:46 +02:00
2a49820127 Refactor variable names 2023-05-05 01:26:27 +02:00
273e224146 Change avatar imgs 2023-05-05 00:49:36 +02:00
1b83170b50 Update deps 2023-05-04 13:22:52 +02:00
406982f124 Update deps 2023-05-02 15:29:52 +02:00
97288d3208 Make years of expierence an dynamic value 2023-04-30 18:41:30 +02:00
d2b04f2081 VCard: add border by default 2023-04-30 18:40:38 +02:00
ce0225af9a Change default v-overlay bg color 2023-04-29 14:28:15 +02:00
48e8007717 Cleanup code 2023-04-29 14:27:21 +02:00
bccd82fcf9 Add close button 2023-04-29 14:27:04 +02:00
3f2284a17f Change default v-carousel color 2023-04-29 14:12:16 +02:00
18 changed files with 9122 additions and 4686 deletions

View File

@@ -1,3 +1,8 @@
# mave.dev
# Personal Portfolio v1
My personal website/portfolio. Built with [Nuxt](https://nuxt.com) and [Vuetify](https://vuetifyjs.com). Hosted with [GitHub Pages](https://pages.github.com/).
My first personal website/portfolio. Built with [Nuxt](https://nuxt.com) and [Vuetify](https://vuetifyjs.com). Hosted with [GitHub Pages](https://pages.github.com/).
## Newer Iterations
- [v3](https://github.com/Rakantor/personal-portfolio-v3)
- [v2](https://github.com/Rakantor/personal-portfolio-v2)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
assets/avatar_grayscale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -33,6 +33,17 @@ p a {
}
}
.page-content {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.v-overlay__scrim {
background: rgb(var(--v-theme-background)) !important;
opacity: 0.9 !important;
}
.link {
color: inherit;
text-decoration: none;
@@ -42,3 +53,26 @@ p a {
color: rgb(var(--v-theme-primary));
}
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.2s;
}
.slide-left-enter-from {
opacity: 0;
transform: translate(50px, 0);
}
.slide-left-leave-to {
opacity: 0;
transform: translate(-50px, 0);
}
.slide-right-enter-from {
opacity: 0;
transform: translate(-50px, 0);
}
.slide-right-leave-to {
opacity: 0;
transform: translate(50px, 0);
}

View File

@@ -1,14 +1,21 @@
<template>
<v-dialog v-model="show">
<v-btn
position="absolute"
location="top right"
icon="mdi-close"
@click.prevent="show = false"
style="z-index: 5000;"
></v-btn>
<v-carousel
:model-value="index"
:show-arrows="images.length > 1"
hide-delimiters
class="ma-auto"
:height="carouselHeight"
height="90vh"
>
<v-carousel-item v-for="image of images" :key="image"
:src="image"
class="img-height"
></v-carousel-item>
</v-carousel>
</v-dialog>
@@ -21,11 +28,14 @@ export default {
show: false,
images: [],
index: 0
}),
computed: {
carouselHeight () {
return window.innerHeight * 0.9
}
}
})
}
</script>
</script>
<style scoped lang="scss">
@use 'vuetify/settings';
.img-height {
height: calc(90vh - settings.$carousel-controls-size);
}
</style>

209
components/ProjectCard.vue Normal file
View File

@@ -0,0 +1,209 @@
<template>
<ImageCarousel ref="imageCarousel" />
<!-- HORIZONTAL VIEW CARD -->
<v-card v-if="view === 'horizontal' && $vuetify.display.smAndUp" class="page-content">
<div class="d-flex flex-no-wrap justify-space-between">
<v-card-text class="my-auto">
<v-avatar size="200" rounded="lg">
<v-carousel
v-model="carouselIndex"
:show-arrows="images.length > 1 ? 'hover' : false"
hide-delimiters
width="200"
height="200"
>
<v-carousel-item v-for="image of images" :key="image"
:src="`https://${$config.public.cdn}${image}`"
cover
@click="showImageCarousel"
></v-carousel-item>
</v-carousel>
</v-avatar>
</v-card-text>
<div class="d-flex flex-column" style="width: 100%;">
<v-card-item>
<span v-if="overline" class="text-overline text-primary">{{ $t(overline) }}</span>
<v-card-title class="d-flex font-weight-bold">
<span v-if="links.length > 0">
<a :href="`https://${links[0].url}`" target="_blank" class="link">
{{ title }}
</a>
</span>
<span v-else>{{ title }}</span>
<v-spacer></v-spacer>
<v-btn
v-for="link in [...links].reverse()" :key="link.url"
variant="text"
:icon="link.icon"
density="comfortable"
color="on-surface"
:href="`https://${link.url}`"
target="_blank"
class="link"
/>
</v-card-title>
<v-card-subtitle>{{ subtitle }}</v-card-subtitle>
</v-card-item>
<v-card-text>{{ $t(description) }}</v-card-text>
<v-spacer></v-spacer>
<v-card-actions class="d-flex flex-row flex-wrap justify-left align-center">
<img v-for="t of tech" :key="technologies[t].title"
:src="generateBadgen(technologies[t].title, technologies[t].icon)"
:height="20"
class="ma-1"
/>
</v-card-actions>
</div>
</div>
</v-card>
<!-- VERTICAL VIEW CARD -->
<v-card v-else height="100%" class="d-flex flex-column">
<v-carousel
v-model="carouselIndex"
:show-arrows="images.length > 1 ? 'hover' : false"
:hide-delimiters="images.length <= 1"
height="auto"
>
<v-carousel-item v-for="image of images" :key="image"
:src="`https://${$config.public.cdn}${image}`"
@click="showImageCarousel"
></v-carousel-item>
</v-carousel>
<v-card-item>
<span v-if="overline" class="text-overline text-primary">{{ $t(overline) }}</span>
<v-card-title class="d-flex font-weight-bold">
<span v-if="links.length > 0">
<a :href="`https://${links[0].url}`" target="_blank" class="link">
{{ title }}
</a>
</span>
<span v-else>{{ title }}</span>
<v-spacer></v-spacer>
<v-btn
v-for="link in [...links].reverse()" :key="link.url"
variant="text"
:icon="link.icon"
density="comfortable"
color="on-surface"
:href="`https://${link.url}`"
target="_blank"
class="link"
/>
</v-card-title>
<v-card-subtitle>{{ subtitle }}</v-card-subtitle>
</v-card-item>
<v-card-text>{{ $t(description) }}</v-card-text>
<v-spacer></v-spacer>
<v-card-actions class="d-flex flex-row flex-wrap justify-center align-center">
<img v-for="t of tech" :key="technologies[t].title"
:src="generateBadgen(technologies[t].title, technologies[t].icon)"
:height="20"
class="ma-1"
/>
<!-- colored badges
<img v-for="t of tech" :key="technologies[t].title"
:src="`https://badgen.net/badge/icon/${technologies[t].title}?icon=${technologies[t].icon}${technologies[t].color}&label`"
:height="20"
class="ma-1"
/>
-->
<!-- colored badges w/ links to brand websites
<a v-for="t of tech" :key="technologies[t].title"
:href="`https://${technologies[t].projectUrl}`"
target="_blank"
>
<img v-for="t of tech" :key="technologies[t].title"
:src="`https://badgen.net/badge/icon/${technologies[t].title}?icon=${technologies[t].icon}${technologies[t].color}&label`"
:height="20"
class="ma-1"
/>
</a>
-->
</v-card-actions>
</v-card>
</template>
<script>
import { badgen } from 'badgen'
import { siAndroid, siAmazonaws, siMicrosoftazure, siFirebase, siGithub,
siHeroku, siCoffeescript, siYoutubegaming, siMysql, siNuxtdotjs, siPhp,
siJavascript, siVuedotjs, siVuetify, siIbmwatson, siWordpress, siTypescript } from 'simple-icons'
export default {
name: 'PortfolioPage',
props: {
view: {
type: String,
required: true
},
title: String,
subtitle: String,
description: String,
overline: String,
tech: {
type: Array,
default: []
},
images: {
type: Array,
default: []
},
links: {
type: Array,
default: []
}
},
data: () => ({
technologies: {
android: { title: 'Android', color: '3DDC84', icon: siAndroid },
aws: { title: 'AWS', color: '232F3E', icon: siAmazonaws },
azure: { title: 'Azure', color: '0078D4', icon: siMicrosoftazure },
firebase: { title: 'Firebase', color: 'FFCA28', icon: siFirebase },
ghpages: { title: 'GH Pages', color: '222222', icon: siGithub },
heroku: { title: 'Heroku', color: '430098', icon: siHeroku },
java: { title: 'Java', color: 'FF7800', icon: siCoffeescript },
js: { title: 'JavaScript', color: 'F7DF1E', icon: siJavascript },
libgdx: { title: 'libGDX', color: '990000', icon: siYoutubegaming },
mysql: { title: 'MySQL', color: '4479A1', icon: siMysql },
nuxt: { title: 'Nuxt', color: '00DC82', icon: siNuxtdotjs },
php: { title: 'PHP', color: '777BB4', icon: siPhp },
ts: { title: 'TypeScript', color: '3178C6', icon: siTypescript },
vue: { title: 'Vue', color: '4FC08D', icon: siVuedotjs },
vuetify: { title: 'Vuetify', color: '1867C0', icon: siVuetify },
watson: { title: 'Watson', color: 'BE95FF', icon: siIbmwatson },
wordpress: { title: 'WordPress', color: '21759B', icon: siWordpress }
},
carouselIndex: []
}),
methods: {
/*
* https://github.com/badgen/badgen
*/
generateBadgen (label, icon) {
// const iconColor = icon.hex
const iconColor = 'FFFFFF' // white
const iconSvg = icon.svg.replace('<path ', `<path fill="#${iconColor}" `) // Change SVG icon color
const svg = badgen({
label: '',
status: label,
// color: iconColor,
color: this.$vuetify.theme.current.colors.backgroundTertiary.slice(1), // Remove leading '#' from hex string
style: 'classic', // Can be 'classic' or 'flat'
icon: `data:image/svg+xml;utf8,${encodeURIComponent(iconSvg)}`
})
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`
},
showImageCarousel () {
this.$refs.imageCarousel.images = this.images.map(i => `https://${this.$config.public.cdn+i}`)
this.$refs.imageCarousel.index = this.carouselIndex
this.$refs.imageCarousel.show = true
}
}
}
</script>

View File

@@ -1,42 +1,119 @@
export default defineI18nConfig(nuxt => ({
export default defineI18nConfig(() => ({
legacy: false,
locale: 'en',
messages: {
en: {
bi: 'BI', // Business Informatics
bioBody: 'My passion for programming ignited in 2007, as I delved into SQL in an attempt to set up\
a private server for my favorite {mmorpg} - a venture that proved successful.\
Eager to dive deeper into the world of coding, I attended a {htl}\
specializing in IT in 2009, where I learned the basics of C, Java, HTML and CSS.\
Some years later, I took my passion further by heading to university, where I got my degree in {bi},\
specializing in web and mobile app development.\
Over the past {yearsOfExp} years, self-study and hands-on experience with personal projects\
have been the cornerstone of my learning journey.',
bioTitle: 'My Journey as a Developer',
bioSubtitle: 'Here\'s some of the tech that I\'ve worked with before:',
getInTouch: 'Get in touch',
greeting: 'Hey. I\'m Manuel.',
iuGamerApp: `An Android app designed to help a group of board game enthusiasts\
headerAbout: 'About',
headerContact: 'Contact',
headerHome: 'Home',
headerWork: 'Projects',
introduction: 'I\'m a Software Developer from Vienna, Austria.\
I have a passion for crafting a wide range of applications.\
Feel free to explore my {portfolio} to see some of my projects.',
imprint: 'Imprint',
iuGamerApp: 'An Android app designed to help a group of board game enthusiasts\
better organize their regular evening game sessions.\
Users will be reliably informed about the time and location of the next appointment.\
They can suggest games, vote on suggested games, rate past appointments,\
and communicate with each other via an integrated chat feature.`,
iuQuizApp: 'An online quiz system that supports distance learning students at IU in solidifying their learning content in preparation for the exam. It enables students to cooperatively and collaboratively find answers to subject-related questions. The focus is particularly on working together and playing/learning together. Similar to the popular app Quizduel, students can, but do not have to, play against each other.',
and communicate with each other via an integrated chat feature.',
iuQuizApp: 'An online quiz system that supports distance learning students at IU\
in solidifying their learning content in preparation for the exam.\
It enables students to cooperatively and collaboratively find answers to subject-related questions.\
The focus is particularly on working together and playing/learning together.\
Similar to the popular app Quizduel, students can, but do not have to, play against each other.',
lang: 'Languages',
languagesFrameworks: 'Languages, Frameworks & Libraries',
os: 'Operating Systems',
personalWebsite: 'My first portfolio website.',
pmb: 'A 2D game inspired by the 2000s Pokémon games. Built from scratch! Features include animated battles (online multiplayer), overworld sprites, custom maps built with Tiled, game sound, 3 difficulty levels and so much more.',
toriiJava: 'A Japanese vocabulary learning tool that harnesses the power of spaced repetition to make memorizing new words a breeze.',
toriiWeb: `Version 2 of my Japanese vocabulary learning tool brings significant enhancements\
over its predecessor. New features include offline functionality (thanks to utilizing IndexedDB),\
an integrated dictionary for single-click word addition,\
additional review methods for learned words,\
a review forecast chart for planning study sessions,\
and improved word search and statistics for better user insights.`
pmb: 'A 2D game inspired by the 2000s Pokémon games. Built from scratch!\
Features include animated NPCs, objects and battles (including online multiplayer),\
custom maps built in Tiled, game sound, 3 difficulty levels and so much more.',
portfolio: 'portfolio',
portfolioTitle: 'Featured Projects',
toolsPlatforms: 'Technologies & Tools',
toriiInfo: '25,000+ registered users!',
toriiJava: 'A Japanese vocabulary learning tool that harnesses the power of spaced repetition\
to make memorizing new words a breeze. It combines a straightforward interface with robust features.\
Designed for both casual learners and those preparing for the Japanese Language Proficiency Test (JLPT),\
Torii offers specialized vocabulary lists and review methods.\
Key features include audio reviews, font randomization, progress tracking,\
and automatic cloud synchronization.',
toriiWeb: 'Version 2 of my Japanese vocabulary learning tool introduces significant enhancements.\
Offline functionality is now available (through the utilization of IndexedDB).\
The web app features an integrated dictionary for efficient word addition,\
expanded review methods for enhancing vocabulary retention,\
a review forecast chart for effective study session planning,\
and enhancements to the word search and statistics functions for improved user insights.'
},
de: {
bi: 'WI', // Wirtschaftsinformatik
bioBody: 'Den Einstieg in die Programmierung habe ich 2007 gemacht, als ich mich mit SQL beschäftigt habe,\
in dem Versuch, einen Privatserver für mein Lieblings-{mmorpg} aufzusetzen - mit Erfolg.\
Nach dem Gymnasium habe ich mich 2009 dazu entschlossen, eine auf IT spezialisierte {htl} zu besuchen,\
wo ich die Grundlagen in C, Java, HTML and CSS gelernt habe.\
Ein paar Jahre später habe ich {bi} studiert - Wahlpflichtfächer Web- und Mobile-App-Development.\
Den Großteil meines Wissens habe ich mir im Zuge persönlicher Projekte in den letzten {yearsOfExp} Jahren\
autodidaktisch angeeignet.',
bioTitle: 'Mein Werdegang als Developer',
bioSubtitle: 'Einige der Technologien, mit denen ich gearbeitet habe:',
getInTouch: 'Kontaktieren',
greeting: 'Hey. Ich bin Manuel.',
headerAbout: 'Über mich',
headerContact: 'Kontakt',
headerHome: 'Home',
headerWork: 'Projekte',
introduction: 'Ich bin ein Software Developer aus Wien, Österreich.\
Ich entwickle ganz unterschiedliche Applikationen.\
Die wichtigsten Projekte habe ich in meinem {portfolio} aufgelistet.',
imprint: 'Impressum',
iuGamerApp: `Eine Android App, die einer Gruppe von Brettspielfans dabei helfen soll,\
ihre regelmäßigen abendlichen Spieltermine besser zu organisieren.\
Benutzer werden zuverlässig über Zeit & Ort des nächsten Termins informiert.\
Sie können Spiele vorschlagen und über Vorschläge abstimmen, vergangene Termine bewerten\
und sich über einen integrierten Chat gegenseitig Nachrichten zukommen lassen.`,
iuQuizApp: 'Ein Online-Quizsystem, das Studierende des Fernstudiums der IU bei der Festigung der Lerninhalte zur Vorbereitung auf die Klausur unterstützt. Es ermöglicht Studierenden, kooperativ und kollaborativ Antworten zu fachlichen Fragen zu finden. Das miteinander bzw. das gemeinsame Spielen/Erarbeiten steht dabei besonders im Fokus. Ähnlich wie bei der populären App Quizduell kann, jedoch muss nicht gegeneinander gespielt werden.',
iuQuizApp: 'Ein Online-Quizsystem, das Fernstudenten der IU bei\
der Festigung der Lerninhalte zur Vorbereitung auf die Klausur unterstützt.\
Es ermöglicht Studierenden, kooperativ und kollaborativ Antworten zu fachlichen Fragen zu finden.\
Das miteinander bzw. das gemeinsame Spielen/Erarbeiten steht dabei besonders im Fokus.\
Ähnlich wie bei der populären App Quizduell kann, jedoch muss nicht gegeneinander gespielt werden.',
lang: 'Sprachen',
languagesFrameworks: 'Sprachen, Frameworks & Bibliotheken',
os: 'Betriebssysteme',
personalWebsite: 'Meine erste Portfolio-Website.',
pmb: 'A 2D game inspired by the 2000s Pokémon games. Built from scratch! Features animated battles (online multiplayer), overworld sprites, custom maps, sound, 3 difficulty levels and so much more.',
toriiJava: 'DEU',
toriiWeb: `Version 2 meiner Vokabeltrainer-App bringt bedeutende Verbesserungen\
gegenüber seinem Vorgänger. Neue Funktionen umfassen Offline-Funktionalität (dank der Nutzung von IndexedDB),\
ein integriertes Wörterbuch für das Hinzufügen neuer Wörter mit nur einem Klick,\
zusätzliche Überprüfungsmethoden für gelernte Vokabeln,\
ein Vorschau-Diagramm zur Planung von Lernsitzungen sowie\
verbesserte Wortsuche und Statistiken.`
pmb: 'Ein 2D Game, inspiriert von den Pokémon Game Boy Spielen der 2000er.\
Funktionen umfassen animierte NPCs, Objekte und Kämpfe (inklusive Online-Multiplayer),\
einzigartige Maps erstellt mit Tiled, Sound, 3 Schwierigkeitsstufen und jede Menge mehr.',
portfolio: 'Portfolio',
portfolioTitle: 'Ausgewählte Projekte',
toolsPlatforms: 'Technologien & Tools',
toriiInfo: '25.000+ registrierte Benutzer!',
toriiJava: 'Eine japanische Vokabeltrainer-App, die die Spaced Repetition Lernmethode nutzt, um\
den Lernprozess neuer Wörter so effektiv und effizient wie möglich zu gestalten.\
Die App verbindet eine simple Benutzeroberfläche mit leistungsstarken Funktionen.\
Torii eignet sich dank spezialisierter Vokabellisten und verschiedenen Wiederholungsmethoden\
sowohl für Casual-Learners als auch zur Vorbereitung auf den\
Japanese Language Proficiency Test (JLPT).\
Zu den wichtigsten Funktionen zählen Audio-Reviews, Font-Randomization, Progress-Tracking,\
und automatische Cloud-Synchronisation.',
toriiWeb: `Version 2 meiner Vokabeltrainer-App bietet deutliche Verbesserungen im Vergleich zur\
vorherigen Version. Zu den neuen Funktionen zählen die Offline-Nutzung durch Einsatz von IndexedDB,\
ein integriertes Wörterbuch, das das Hinzufügen neuer Wörter mit einem einzigen Klick ermöglicht,\
erweiterte Überprüfungsoptionen für bereits gelernte Vokabeln,\
ein Vorschau-Diagramm zur effektiven Planung von Lerneinheiten sowie optimierte Funktionen für\
die Wortsuche und Statistiken zur besseren Nachverfolgung des Lernfortschritts.`
}
}
}))

View File

@@ -28,7 +28,7 @@
outlined
color="primary"
prepend-icon="mdi-email-outline"
:href="`mailto:<${$myEmail}>`"
:href="`mailto:<${$config.public.myEmail}>`"
>
Contact
</v-btn>
@@ -43,10 +43,10 @@
style="cursor: pointer"
@click.native="$router.push('/')"
>
<img src="~/assets/avatar_transparent.png" width="55" />
<img src="~/assets/avatar_blueish.png" width="55" />
</v-avatar>
</template>
<!-- v-app-bar-title class="text-subtitle-1" v-text="$myName" /-->
<!-- v-app-bar-title class="text-subtitle-1" v-text="$config.public.myName" /-->
<v-spacer />
<v-app-bar-nav-icon
v-if="smAndDown"
@@ -56,7 +56,7 @@
<v-btn :ripple="false" to="/">{{ $t('headerHome') }}</v-btn>
<v-btn :ripple="false" to="/bio">{{ $t('headerAbout') }}</v-btn>
<v-btn :ripple="false" to="/portfolio">{{ $t('headerWork') }}</v-btn>
<v-btn :ripple="false" :href="`mailto:<${$myEmail}>`">{{ $t('headerContact') }}</v-btn>
<v-btn :ripple="false" :href="`mailto:<${$config.public.myEmail}>`">{{ $t('headerContact') }}</v-btn>
</v-btn-toggle>
</v-app-bar>
<v-main>
@@ -86,7 +86,7 @@
</v-col>
<v-col cols="4" class="text-center my-auto">
<span class="text-caption text-no-wrap">
&copy; {{ new Date().getFullYear() }} {{ $myName }}
&copy; {{ new Date().getFullYear() }} {{ $config.public.myName }}
</span>
</v-col>
<v-col cols="4" class="my-auto" :class="$vuetify.display.mobile ? 'text-right' : 'text-center'">

View File

@@ -0,0 +1,8 @@
export default defineNuxtRouteMiddleware((to, from) => {
if (typeof to.meta.pageTransition === 'object' && typeof from.meta.pageTransition === 'object') {
if (typeof to.meta.id !== 'number' || typeof from.meta.id !== 'number') return
const transition = to.meta.id > from.meta.id ? 'slide-left' : 'slide-right'
to.meta.pageTransition.name = from.meta.pageTransition.name = transition
}
})

View File

@@ -2,23 +2,26 @@ import vuetify from 'vite-plugin-vuetify'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
runtimeConfig: {
public: {
cdn: 'd29l6egdxvgg9c.cloudfront.net/',
myName: 'Manuel Veigel',
myEmail: 'maveigel@gmail.com',
}
},
ssr: false,
css: [
'vuetify/styles',
'~/assets/variables.scss'
],
vite: {
ssr: {
noExternal: ['vuetify'] // add the vuetify vite plugin
}
},
build: {
transpile: ['vuetify']
},
app: {
head: {
// titleTemplate: '%s | Home',
@@ -40,14 +43,10 @@ export default defineNuxtConfig({
],
}
},
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
// ['@nuxtjs/axios', { proxyHeaders: false }],
'@nuxtjs/i18n',
async (options, nuxt) => {
nuxt.hooks.hook('vite:extendConfig', config =>
@@ -56,126 +55,7 @@ export default defineNuxtConfig({
)
}
],
i18n: {
// { vueI18n: './i18n.config.ts' }
// TODO: Revert to config file once the bug in 8.0.0-beta.11 has been fixed
// https://github.com/nuxt-modules/i18n/issues/1990
vueI18n: {
legacy: false,
locale: 'en',
messages: {
en: {
bi: 'BI', // Business Informatics
bioBody: 'My interest in programming was sparked in 2007 when I began tinkering with SQL\
in an attempt to setup a private server for my favorite {mmorpg} (it was a success!)\
Eager to dive deeper into the world of coding, I attended a {htl}\
specializing in IT in 2009, where I learned C, Java, HTML and CSS.\
Some years later, I pursued a degree in {bi} with a specialization\
in Web and App development. I have acquired the majority of my knowledge through self-teaching\
while working on personal projects over the past ~15 years.',
bioTitle: 'My Journey as a Developer',
bioSubtitle: 'Here\'s some of the tech that I\'ve worked with before:',
getInTouch: 'Get in touch',
greeting: 'Hey. I\'m Manuel.',
headerAbout: 'About',
headerContact: 'Contact',
headerHome: 'Home',
headerWork: 'Projects',
introduction: 'I\'m a Software Developer based in Vienna, Austria.\
I develop various types of applications.\
Explore my {portfolio} to view a showcase of my projects.',
imprint: 'Imprint',
iuGamerApp: 'An Android app designed to help a group of board game enthusiasts\
better organize their regular evening game sessions.\
Users will be reliably informed about the time and location of the next appointment.\
They can suggest games, vote on suggested games, rate past appointments,\
and communicate with each other via an integrated chat feature.',
iuQuizApp: 'An online quiz system that supports distance learning students at IU\
in solidifying their learning content in preparation for the exam.\
It enables students to cooperatively and collaboratively find answers to subject-related questions.\
The focus is particularly on working together and playing/learning together.\
Similar to the popular app Quizduel, students can, but do not have to, play against each other.',
lang: 'Languages',
languagesFrameworks: 'Languages & Frameworks',
os: 'Operating Systems',
personalWebsite: 'My first portfolio website.',
pmb: 'A 2D game inspired by the 2000s Pokémon games. Built from scratch!\
Features include animated NPCs, objects and battles (including online multiplayer),\
custom maps built in Tiled, game sound, 3 difficulty levels and so much more.',
portfolio: 'portfolio',
portfolioTitle: 'Some of my projects',
toolsPlatforms: 'Tools & Platforms',
toriiJava: 'A Japanese vocabulary learning tool that harnesses the power of spaced repetition\
to make memorizing new words a breeze. It combines a straightforward interface with robust features.\
Designed for both casual learners and those preparing for the Japanese Language Proficiency Test (JLPT),\
Torii offers specialized vocabulary lists and review methods.\
Key features include audio reviews, font randomization, progress tracking,\
and automatic cloud synchronization.',
toriiWeb: 'Version 2 of my Japanese vocabulary learning tool brings significant enhancements\
over its predecessor. New features include offline functionality (thanks to utilizing IndexedDB),\
an integrated dictionary for single-click word addition,\
additional review methods for learned words,\
a review forecast chart for planning study sessions,\
and improved word search and statistics for better user insights.'
},
de: {
bi: 'WI', // Wirtschaftsinformatik
bioBody: 'Den Einstieg in die Programmierung habe ich 2007 gemacht, als ich mich mit SQL beschäftigt habe,\
in dem Versuch, einen Privatserver für mein Lieblings-{mmorpg} aufzusetzen (erfolgreich!)\
Nach dem Gymnasium habe ich mich 2009 dazu entschlossen, eine auf IT spezialisierte {htl} zu besuchen,\
wo ich die Grundlagen in C, Java, HTML and CSS gelernt habe.\
Ein paar Jahre später habe ich {bi} studiert - Wahlpflichtfächer Web- und App-Development.\
Den Großteil meines Wissens habe ich mir im Zuge persönlicher Projekte in den letzten ~15 Jahren\
autodidaktisch angeeignet.',
bioTitle: 'Mein Werdegang als Developer',
bioSubtitle: 'Einige der Technologien, mit denen ich gearbeitet habe:',
getInTouch: 'Kontaktieren',
greeting: 'Hey. Ich bin Manuel.',
headerAbout: 'Über mich',
headerContact: 'Kontakt',
headerHome: 'Home',
headerWork: 'Projekte',
introduction: 'Ich bin ein Software Developer aus Wien, Österreich.\
Ich entwickle ganz unterschiedliche Applikationen.\
Die wichtigsten Projekte habe ich in meinem {portfolio} aufgelistet.',
imprint: 'Impressum',
iuGamerApp: `Eine Android App, die einer Gruppe von Brettspielfans dabei helfen soll,\
ihre regelmäßigen abendlichen Spieltermine besser zu organisieren.\
Benutzer werden zuverlässig über Zeit & Ort des nächsten Termins informiert.\
Sie können Spiele vorschlagen und über Vorschläge abstimmen, vergangene Termine bewerten\
und sich über einen integrierten Chat gegenseitig Nachrichten zukommen lassen.`,
iuQuizApp: 'Ein Online-Quizsystem, das Fernstudenten der IU bei\
der Festigung der Lerninhalte zur Vorbereitung auf die Klausur unterstützt.\
Es ermöglicht Studierenden, kooperativ und kollaborativ Antworten zu fachlichen Fragen zu finden.\
Das miteinander bzw. das gemeinsame Spielen/Erarbeiten steht dabei besonders im Fokus.\
Ähnlich wie bei der populären App Quizduell kann, jedoch muss nicht gegeneinander gespielt werden.',
lang: 'Sprachen',
languagesFrameworks: 'Sprachen & Frameworks',
os: 'Betriebssysteme',
personalWebsite: 'Meine erste Portfolio-Website.',
pmb: 'Ein 2D Game, inspiriert von den Pokémon Game Boy Spielen der 2000er.\
Funktionen umfassen animierte NPCs, Objekte und Kämpfe (inklusive Online-Multiplayer),\
einzigartige Maps erstellt mit Tiled, Sound, 3 Schwierigkeitsstufen und jede Menge mehr.',
portfolio: 'Portfolio',
portfolioTitle: 'Ausgewählte Projekte',
toolsPlatforms: 'Tools & Plattformen',
toriiJava: 'Eine japanische Vokabeltrainer-App, die die Spaced Repetition Lernmethode nutzt, um\
den Lernprozess neuer Wörter so effektiv und effizient wie möglich zu gestalten.\
Die App verbindet eine simple Benutzeroberfläche mit leistungsstarken Funktionen.\
Torii eignet sich dank spezialisierter Vokabellisten und verschiedenen Wiederholungsmethoden\
sowohl für Casual-Learners als auch zur Vorbereitung auf den\
Japanese Language Proficiency Test (JLPT).\
Zu den wichtigsten Funktionen zählen Audio-Reviews, Font-Randomization, Progress-Tracking,\
und automatische Cloud-Synchronisation.',
toriiWeb: `Version 2 meiner Vokabeltrainer-App bringt bedeutende Verbesserungen\
gegenüber seinem Vorgänger. Neue Funktionen umfassen Offline-Funktionalität (dank der Nutzung von IndexedDB),\
ein integriertes Wörterbuch für das Hinzufügen neuer Wörter mit nur einem Klick,\
zusätzliche Überprüfungsmethoden für gelernte Vokabeln,\
ein Vorschau-Diagramm zur Planung von Lernsessions sowie\
verbesserte Wortsuche und Statistiken.`
}
}
}
vueI18n: './i18n.config.ts'
}
})

12663
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
"name": "personal-portfolio",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
@@ -11,11 +12,10 @@
},
"devDependencies": {
"@mdi/font": "^7.0.96",
"@nuxtjs/i18n": "8.0.0-beta.10",
"nuxt": "^3.4.0"
"@nuxtjs/i18n": "npm:@nuxtjs/i18n-edge",
"nuxt": "^3.4.3"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"badgen": "^3.2.2",
"lodash-es": "^4.17.21",
"sass": "^1.56.0",

View File

@@ -1,60 +1,74 @@
<template>
<v-row>
<v-col cols="12">
<span class="text-h5 text-md-h4 text-high-emphasis">{{ $t('bioTitle') }}</span>
</v-col>
<v-col cols="12">
<v-sheet color="transparent" class="text-medium-emphasis">
<i18n-t
keypath="bioBody"
tag="p"
>
<template v-slot:mmorpg>
<NuxtLink :href="mmorpgWikiUrl" target="_blank">MMORPG</NuxtLink>
</template>
<template v-slot:htl>
<NuxtLink :href="htlWikiUrl" target="_blank">HTL</NuxtLink>
</template>
<template v-slot:bi>
<NuxtLink :href="biWikiUrl" target="_blank">{{ $t('bi') }}</NuxtLink>
</template>
</i18n-t>
<br/>
<p>{{ $t('bioSubtitle') }}</p>
</v-sheet>
</v-col>
<v-col v-for="set, ind in sets" :key="ind" cols="12">
<v-card>
<v-card-item>
<v-card-title>{{ $t(set.title) }}</v-card-title>
</v-card-item>
<v-card-text class="text-center">
<v-row>
<v-col v-for="(v, i) in set.data" :key="i" cols="12">
<v-row justify="center">
<v-col cols="auto" v-for="(value, index) in v" :key="index">
<v-btn
variant="text"
:size="getButtonSize(value.level)"
color="primary"
class="mx-1"
:href="`https://${value.url}`"
target="_blank"
>
<v-icon :icon="value.icon" :size="getButtonSize(value.level) - 10"></v-icon>
</v-btn>
<span class="text-overline text-primary d-flex flex-column ma-0 pa-0">{{ value.title }}</span>
</v-col>
</v-row>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
<div class="page-content">
<v-row>
<v-col cols="12">
<span class="text-h5 text-md-h4 text-high-emphasis">
{{ $t('bioTitle') }}
</span>
</v-col>
<v-col cols="12">
<v-sheet color="transparent" class="text-medium-emphasis">
<i18n-t keypath="bioBody" tag="p">
<template v-slot:mmorpg>
<NuxtLink :href="mmorpgWikiUrl" target="_blank">MMORPG</NuxtLink>
</template>
<template v-slot:htl>
<NuxtLink :href="htlWikiUrl" target="_blank">HTL</NuxtLink>
</template>
<template v-slot:bi>
<NuxtLink :href="biWikiUrl" target="_blank">{{ $t('bi') }}</NuxtLink>
</template>
<template v-slot:yearsOfExp>
{{ yearsOfExp }}
</template>
</i18n-t>
<br/>
<p>{{ $t('bioSubtitle') }}</p>
</v-sheet>
</v-col>
<v-col v-for="set, ind in sets" :key="ind" cols="12">
<v-card>
<v-card-item>
<v-card-title>{{ $t(set.title) }}</v-card-title>
</v-card-item>
<v-card-text class="text-center">
<v-row>
<v-col v-for="(v, i) in set.data" :key="i" cols="12">
<v-row justify="center">
<v-col cols="auto" v-for="(value, index) in v" :key="index">
<v-btn
variant="text"
:size="brandIconSize(value.emphasis)"
color="primary"
class="mx-1"
:href="`https://${value.url}`"
target="_blank"
>
<v-icon :icon="value.icon" :size="brandIconSize(value.emphasis) - 10"></v-icon>
</v-btn>
<span class="text-overline text-primary d-flex flex-column ma-0 pa-0">
{{ value.title }}
</span>
</v-col>
</v-row>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
definePageMeta({
id: 2,
pageTransition: {
name: 'slide',
mode: 'out-in'
}
})
import _groupBy from 'lodash-es/groupBy'
export default {
@@ -64,76 +78,92 @@ export default {
htlWikiUrl: 'https://en.wikipedia.org/wiki/H%C3%B6here_Technische_Lehranstalt',
biWikiUrl: 'https://en.wikipedia.org/wiki/Business_informatics',
languages: [
{ title: 'Java', icon: 'mdi-language-java', url: 'java.com', level: 3 },
{ title: 'C/C++', icon: 'mdi-language-cpp', url: 'isocpp.org', level: 1 },
{ title: 'Python', icon: 'mdi-language-python', url: 'python.org', level: 1 },
{ title: 'JavaScript (ES6+)', icon: 'mdi-language-javascript', url: 'javascript.com', level: 3 },
{ title: 'TypeScript', icon: 'mdi-language-typescript', url: 'typescriptlang.org/', level: 1 },
{ title: 'HTML', icon: 'mdi-language-html5', url: 'html.spec.whatwg.org/multipage', level: 2 },
{ title: 'CSS', icon: 'mdi-language-css3', url: 'w3.org/Style/CSS', level: 2 },
{ title: 'PHP', icon: 'mdi-language-php', url: 'php.net', level: 2 },
{ title: 'SQL', icon: 'mdi-database', url: 'iso.org/standard/63555.html', level: 2 },
{ title: 'Lua', icon: 'mdi-language-lua', url: 'lua.org', level: 2 }
{ title: 'Java', icon: 'mdi-language-java', url: 'java.com', emphasis: 'high' },
{ title: 'C/C++', icon: 'mdi-language-cpp', url: 'isocpp.org', emphasis: 'low' },
{ title: 'Python', icon: 'mdi-language-python', url: 'python.org', emphasis: 'low' },
{ title: 'JavaScript (ES6+)', icon: 'mdi-language-javascript', url: 'javascript.com', emphasis: 'high' },
{ title: 'TypeScript', icon: 'mdi-language-typescript', url: 'typescriptlang.org/', emphasis: 'low' },
{ title: 'HTML', icon: 'mdi-language-html5', url: 'html.spec.whatwg.org/multipage', emphasis: 'medium' },
{ title: 'CSS', icon: 'mdi-language-css3', url: 'w3.org/Style/CSS', emphasis: 'medium' },
{ title: 'PHP', icon: 'mdi-language-php', url: 'php.net', emphasis: 'medium' },
{ title: 'SQL', icon: 'mdi-database', url: 'iso.org/standard/63555.html', emphasis: 'medium' },
{ title: 'Lua', icon: 'mdi-language-lua', url: 'lua.org', emphasis: 'medium' }
],
frameworks: [
{ title: 'Android', icon: 'mdi-android', url: 'android.com', level: 2 },
{ title: 'Vue.js', icon: 'mdi-vuejs', url: 'vuejs.org', level: 3 },
{ title: 'Nuxt', icon: 'mdi-nuxt', url: 'nuxt.com', level: 3 },
{ title: 'Vuetify', icon: 'mdi-vuetify', url: 'vuetifyjs.com', level: 3 },
{ title: 'Bootstrap', icon: 'mdi-bootstrap', url: 'bootstrap-vue.org', level: 1 },
{ title: 'libGDX', icon: 'mdi-alpha-l-box-outline', url: 'libgdx.com', level: 1 }
{ title: 'Android', icon: 'mdi-android', url: 'android.com', emphasis: 'medium' },
{ title: 'Vue.js', icon: 'mdi-vuejs', url: 'vuejs.org', emphasis: 'high' },
{ title: 'Nuxt', icon: 'mdi-nuxt', url: 'nuxt.com', emphasis: 'high' },
{ title: 'Vuetify', icon: 'mdi-vuetify', url: 'vuetifyjs.com', emphasis: 'high' },
{ title: 'Bootstrap', icon: 'mdi-bootstrap', url: 'bootstrap-vue.org', emphasis: 'low' },
{ title: 'libGDX', icon: 'mdi-alpha-l-box-outline', url: 'libgdx.com', emphasis: 'low' }
],
tech: [
{ title: 'Amazon Web Services', icon: 'mdi-aws', url: 'aws.amazon.com', level: 3 },
{ title: 'Firebase', icon: 'mdi-firebase', url: 'firebase.google.com', level: 3 },
{ title: 'Azure', icon: 'mdi-microsoft-azure', url: 'azure.microsoft.com', level: 1 },
{ title: 'Heroku', icon: 'brands:heroku', url: 'heroku.com', level: 1 },
{ title: 'WordPress', icon: 'mdi-wordpress', url: 'wordpress.com', level: 1 },
{ title: 'Unity', icon: 'mdi-unity', url: 'unity.com', level: 1 },
{ title: 'Unreal Engine', icon: 'mdi-unreal', url: 'unrealengine.com', level: 1 }
{ title: 'Amazon Web Services', icon: 'mdi-aws', url: 'aws.amazon.com', emphasis: 'high' },
{ title: 'Firebase', icon: 'mdi-firebase', url: 'firebase.google.com', emphasis: 'high' },
{ title: 'Node.js', icon: 'mdi-nodejs', url: 'nodejs.org', emphasis: 'medium' },
{ title: 'Docker', icon: 'mdi-docker', url: 'docker.com', emphasis: 'medium' },
{ title: 'Azure', icon: 'mdi-microsoft-azure', url: 'azure.microsoft.com', emphasis: 'low' },
{ title: 'Heroku', icon: 'brands:heroku', url: 'heroku.com', emphasis: 'low' },
// { title: 'WordPress', icon: 'mdi-wordpress', url: 'wordpress.com', emphasis: 'low' },
{ title: 'Unity', icon: 'mdi-unity', url: 'unity.com', emphasis: 'low' },
{ title: 'Unreal Engine', icon: 'mdi-unreal', url: 'unrealengine.com', emphasis: 'low' }
],
os: [
{ title: 'Windows', icon: 'mdi-microsoft-windows', url: 'microsoft.com/windows', level: 3 },
{ title: 'Apple macOS', icon: 'brands:apple', url: 'apple.com/macos', level: 3 },
{ title: 'Linux Mint', icon: 'mdi-linux-mint', url: 'linuxmint.com', level: 3 },
{ title: 'Fedora Workstation', icon: 'mdi-fedora', url: 'getfedora.org', level: 3 },
{ title: 'Arch Linux', icon: 'mdi-arch', url: 'archlinux.org', level: 1 },
{ title: 'Ubuntu', icon: 'mdi-ubuntu', url: 'ubuntu.com', level: 1 },
{ title: 'Apple iOS', icon: 'mdi-apple-ios', url: 'apple.com/ios', level: 1 },
{ title: 'Android', icon: 'mdi-android', url: 'android.com', level: 1 },
{ title: 'Windows', icon: 'mdi-microsoft-windows', url: 'microsoft.com/windows', emphasis: 'medium' },
{ title: 'macOS', icon: 'brands:apple', url: 'apple.com/macos', emphasis: 'medium' },
{ title: 'Linux Mint', icon: 'mdi-linux-mint', url: 'linuxmint.com', emphasis: 'medium' },
{ title: 'Fedora', icon: 'mdi-fedora', url: 'getfedora.org', emphasis: 'medium' },
{ title: 'Arch Linux', icon: 'mdi-arch', url: 'archlinux.org', emphasis: 'low' },
{ title: 'Ubuntu', icon: 'mdi-ubuntu', url: 'ubuntu.com', emphasis: 'low' },
{ title: 'iOS', icon: 'mdi-apple-ios', url: 'apple.com/ios', emphasis: 'low' },
{ title: 'Android', icon: 'mdi-android', url: 'android.com', emphasis: 'low' },
]
}),
computed: {
sets () {
const languages = _groupBy([...this.languages, ...this.frameworks], e => e.level)
// const frameworks = _groupBy(this.frameworks, e => e.level)
const tech = _groupBy(this.tech, e => e.level)
const os = _groupBy(this.os, e => e.level)
const languages = _groupBy([...this.languages, ...this.frameworks], e => e.emphasis)
// const frameworks = _groupBy(this.frameworks, e => e.emphasis)
const tech = _groupBy(this.tech, e => e.emphasis)
const os = _groupBy(this.os, e => e.emphasis)
function compareEmphasis (x, y) {
const map = { low: 1, medium: 2, high: 3 }
return map[y] - map[x]
}
return [
{
title: 'languagesFrameworks',
data: Object.keys(languages).sort().reverse().map(e => languages[e])
data: Object.keys(languages).sort(compareEmphasis).map(e => languages[e])
},
{
title: 'toolsPlatforms',
data: Object.keys(tech).sort().reverse().map(e => tech[e])
data: Object.keys(tech).sort(compareEmphasis).map(e => tech[e])
},
{
title: 'os',
data: Object.keys(os).sort().reverse().map(e => os[e])
data: Object.keys(os).sort(compareEmphasis).map(e => os[e])
}
]
},
skills () {
return [...this.languages, ...this.frameworks, ...this.tech, ...this.os]
yearsOfExp () {
// startingYear = The year I started my first truly personal project (predecessor of Menacing Blue).
// Fun fact: I used Notepad++ and javac in cmd for coding rather than an IDE for like the first 6-12 months.
const startingYear = 2011
const currentYear = new Date().getFullYear()
return currentYear - startingYear
}
},
methods: {
getButtonSize (level) {
switch (level) {
case 1: return this.$vuetify.display.smAndDown ? 32 : 48
case 2: return this.$vuetify.display.smAndDown ? 48 : 64
default: return this.$vuetify.display.smAndDown ? 64 : 96
brandIconSize (emphasis) {
const large = this.$vuetify.display.smAndDown ? 64 : 96
const medium = this.$vuetify.display.smAndDown ? 48 : 64
const small = this.$vuetify.display.smAndDown ? 32 : 48
switch (emphasis) {
case 'low': return small
case 'medium': return medium
default: return large
}
}
}

View File

@@ -1,13 +1,14 @@
<template>
<div>
<v-card class="mb-2">
<v-card-item>
<v-card-title>Offenlegung nach § 25 Mediengesetz</v-card-title>
</v-card-item>
<v-card-text>
<strong>Medieninhaber:</strong><br />
{{ $myName }}<br />
{{ $config.public.myName }}<br />
Wien, Österreich<br />
{{ $myEmail }}<br />
{{ $config.public.myEmail }}<br />
</v-card-text>
<v-card-text>
Zweck dieser Website: Präsentation des Medieninhabers.
@@ -45,11 +46,11 @@
<p>Sollten Sie Fragen zum Datenschutz oder zur Verarbeitung personenbezogener Daten haben, finden Sie nachfolgend die
Kontaktdaten der verantwortlichen Person bzw. Stelle:<br />
<span style="font-weight: 400">
{{ $myName }}<br />
{{ $config.public.myName }}<br />
Wien, Österreich
</span>
<br />
E-Mail: <a :href="`mailto:${$myEmail}`">{{ $myEmail }}</a>
E-Mail: <a :href="`mailto:${$config.public.myEmail}`">{{ $config.public.myEmail }}</a>
</p>
<h2 id="rechte-dsgvo">Rechte laut Datenschutz-Grundverordnung</h2>
<p>Gemäß Artikel 13, 14 DSGVO informieren wir Sie über die folgenden Rechte, die Ihnen zustehen, damit es zu einer
@@ -960,11 +961,17 @@
</p>
</v-card-text>
</v-card>
</div>
</template>
<script>
definePageMeta({
id: 4,
pageTransition: false
})
export default {
name: 'ImprintPage',
name: 'ImprintPage'
}
</script>

View File

@@ -22,7 +22,7 @@
variant="outlined"
color="primary"
prepend-icon="mdi-email-outline"
:href="`mailto:<${$myEmail}>`"
:href="`mailto:<${$config.public.myEmail}>`"
>
{{ $t('getInTouch') }}
</v-btn>
@@ -31,6 +31,14 @@
</template>
<script>
definePageMeta({
id: 1,
pageTransition: {
name: 'slide',
mode: 'out-in'
}
})
export default {
name: 'IndexPage'
}

View File

@@ -1,104 +1,81 @@
<template>
<ImageCarousel ref="imageCarousel" />
<v-row>
<v-col cols="12">
<span class="text-h5 font-weight-medium">{{ $t('portfolioTitle') }}</span>
</v-col>
<v-col v-for="(project, index) of projects" :key="index" cols="12" lg="4" md="6">
<v-card height="100%" class="d-flex flex-column">
<v-carousel
v-model="carouselIndex[index]"
:show-arrows="project.images.length > 1 ? 'hover' : false"
:hide-delimiters="project.images.length <= 1"
hide-delimiter-background
height="auto"
<div>
<v-row>
<v-col cols="12">
<v-sheet
width="100%"
color="transparent"
class="d-flex flex-no-wrap justify-space-between"
:class="view === 'horizontal' ? 'page-content' : ''"
>
<v-carousel-item v-for="image of project.images" :key="image"
:src="`${cdn}${image}`"
:class="project.class"
@click="showImageCarousel(project.images, carouselIndex[index])"
></v-carousel-item>
</v-carousel>
<v-card-item>
<v-card-title class="d-flex">
<span>{{ project.title }}</span>
<v-spacer></v-spacer>
<v-btn
v-if="project.repoUrl"
variant="text"
icon="mdi-github"
density="comfortable"
color="on-surface"
:href="`https://${project.repoUrl}`"
target="_blank"
class="link"
/>
<v-btn
v-if="project.projectUrl"
variant="text"
icon="mdi-open-in-new"
density="comfortable"
color="on-surface"
:href="`https://${project.projectUrl}`"
target="_blank"
class="link"
/>
</v-card-title>
<v-card-subtitle>{{ project.subtitle }}</v-card-subtitle>
</v-card-item>
<v-card-text>{{ $t(project.description) }}</v-card-text>
<v-spacer></v-spacer>
<v-card-actions class="d-flex flex-row flex-wrap justify-center align-center">
<img v-for="t of project.tech" :key="tech[t].title"
:src="generateBadgen(tech[t].title, tech[t].iconUrl)"
:height="20"
class="ma-1"
/>
<!-- colored badges
<img v-for="t of project.tech" :key="tech[t].title"
:src="`https://badgen.net/badge/icon/${tech[t].title}?icon=${tech[t].iconUrl}${tech[t].color}&label`"
:height="20"
class="ma-1"
/>
-->
<!-- colored badges w/ links to brand websites
<a v-for="t of project.tech" :key="tech[t].title"
:href="`https://${tech[t].projectUrl}`"
target="_blank"
<span class="text-h5 font-weight-medium">
{{ $t('portfolioTitle') }}
</span>
<v-btn-toggle
v-model="selectedViewIndex"
mandatory
density="compact"
color="primary"
>
<img v-for="t of project.tech" :key="tech[t].title"
:src="`https://badgen.net/badge/icon/${tech[t].title}?icon=${tech[t].iconUrl}${tech[t].color}&label`"
:height="20"
class="ma-1"
/>
</a>
-->
</v-card-actions>
</v-card>
</v-col>
</v-row>
<v-btn>
<v-icon icon="mdi-view-sequential" color="white"></v-icon>
</v-btn>
<v-btn>
<v-icon icon="mdi-view-column" color="white"></v-icon>
</v-btn>
</v-btn-toggle>
</v-sheet>
</v-col>
<v-col v-for="(project, index) of projects" :key="index"
:cols="viewCols.cols"
:sm="viewCols.sm"
:md="viewCols.md"
:lg="viewCols.lg"
:xl="viewCols.xl"
>
<ProjectCard
:view="view"
:title="project.title"
:subtitle="project.subtitle"
:description="project.description"
:overline="project.overline"
:tech="project.tech"
:images="project.images"
:links="project.links"
/>
</v-col>
</v-row>
</div>
</template>
<script>
import { badgen } from 'badgen'
import { siAndroid, siAmazonaws, siMicrosoftazure, siFirebase, siGithub,
siHeroku, siCoffeescript, siYoutubegaming, siMysql, siNuxtdotjs, siPhp,
siVuedotjs, siVuetify, siIbmwatson, siWordpress } from 'simple-icons'
definePageMeta({
id: 3,
pageTransition: {
name: 'slide',
mode: 'out-in'
}
})
const runtimeConfig = useRuntimeConfig()
export default {
name: 'PortfolioComponent',
name: 'PortfolioPage',
data: () => ({
cdn: 'https://d29l6egdxvgg9c.cloudfront.net/',
selectedViewIndex: 0,
views: {
horizontal: 0,
vertical: 1
},
projects: [
{
title: 'Torii SRS (v2-beta)',
subtitle: 'Progressive Web App',
{
title: 'Torii SRS (v2)',
subtitle: 'Progressive Web App (SPA)',
description: 'toriiWeb',
tech: ['vue2', 'vuetify2', 'mysql', 'php', 'aws', 'azure', 'watson', 'heroku'],
projectUrl: 'beta.torii-srs.com',
tech: ['js', 'vue', 'vuetify', 'mysql', 'php', 'aws', 'azure', 'watson', 'heroku'],
links: [
{ url: 'beta.torii-srs.com', icon: 'mdi-open-in-new' }
],
images: [
'torii-v2-01.jpg', 'torii-v2-03.jpg', 'torii-v2-04.jpg',
'torii-v2-05.jpg', 'torii-v2-06.jpg', 'torii-v2-07.jpg',
@@ -107,10 +84,13 @@ export default {
},
{
title: 'Torii SRS (v1)',
subtitle: 'Cross-platform app',
subtitle: 'Cross-Platform App',
description: 'toriiJava',
overline: 'toriiInfo',
tech: ['java', 'libgdx', 'mysql', 'php', 'aws', 'wordpress'],
projectUrl: 'torii-srs.com',
links: [
{ url: 'torii-srs.com', icon: 'mdi-open-in-new' }
],
images: [
'torii-v1-1.jpg', 'torii-v1-2.png', 'torii-v1-3.png',
'torii-v1-4.png', 'torii-v1-5.png', 'torii-v1-6.png'
@@ -118,148 +98,69 @@ export default {
},
{
title: 'IU Quiz App',
subtitle: 'Web App',
subtitle: 'Web App (SPA)',
description: 'iuQuizApp',
tech: ['nuxt2', 'vuetify2', 'firebase'],
repoUrl: 'github.com/Rakantor/iu-quiz-app',
projectUrl: 'iu-quiz-app.web.app',
tech: ['js', 'vue', 'nuxt', 'vuetify', 'firebase'],
links: [
{ url: 'iu-quiz-app.web.app', icon: 'mdi-open-in-new' },
{ url: 'github.com/Rakantor/iu-quiz-app', icon: 'mdi-github' },
{ url: `${runtimeConfig.public.cdn}iu-quiz-app-projektbericht.pdf`, icon: 'mdi-file-pdf-box' }
],
images: ['iu-quiz-app-2.jpg']
},
{
title: 'Menacing Blue',
subtitle: 'Cross-platform app',
subtitle: 'Cross-Platform App',
description: 'pmb',
tech: ['java', 'libgdx'],
images: ['pmb-6.png', 'pmb-1.png', 'pmb-2.png', 'pmb-3.png', 'pmb-4.png', 'pmb-5.png', 'pmb-7.jpg']
},
{
title: 'Personal Website',
subtitle: 'Web App',
subtitle: 'Web App (SPA)',
description: 'personalWebsite',
tech: ['nuxt3', 'vuetify3', 'ghpages'],
repoUrl: 'github.com/Rakantor/personal-portfolio',
projectUrl: 'mave.dev',
tech: ['ts', 'vue', 'nuxt', 'vuetify', 'ghpages'],
links: [
{ url: 'mave.dev', icon: 'mdi-open-in-new' },
{ url: 'github.com/Rakantor/personal-portfolio', icon: 'mdi-github' }
],
images: ['personal-website-1.jpg'],
class: 'pa-2'
},
{
title: 'IU Gamer App',
subtitle: 'Android app',
subtitle: 'Android App',
description: 'iuGamerApp',
tech: ['java', 'android', 'firebase'],
repoUrl: 'github.com/Rakantor/iubh-gamer-app',
links: [
{ url: 'github.com/Rakantor/iubh-gamer-app', icon: 'mdi-github' }
],
images: ['iu-gamer-app-1.jpg', 'iu-gamer-app-2.jpg']
}
],
tech: {
android: {
title: 'Android',
color: '3DDC84',
iconUrl: siAndroid,
},
aws: {
title: 'AWS',
color: '232F3E',
iconUrl: siAmazonaws,
},
azure: {
title: 'Azure',
color: '0078D4',
iconUrl: siMicrosoftazure,
},
firebase: {
title: 'Firebase',
color: 'FFCA28',
iconUrl: siFirebase,
},
ghpages: {
title: 'GH Pages',
color: '222222',
iconUrl: siGithub,
},
heroku: {
title: 'Heroku',
color: '430098',
iconUrl: siHeroku,
},
java: {
title: 'Java',
color: 'FF7800',
iconUrl: siCoffeescript,
},
libgdx: {
title: 'libGDX',
color: '990000',
iconUrl: siYoutubegaming,
},
mysql: {
title: 'MySQL',
color: '4479A1',
iconUrl: siMysql,
},
nuxt2: {
title: 'Nuxt 2',
color: '00DC82',
iconUrl: siNuxtdotjs,
},
nuxt3: {
title: 'Nuxt 3',
color: '00DC82',
iconUrl: siNuxtdotjs,
},
php: {
title: 'PHP',
color: '777BB4',
iconUrl: siPhp,
},
vue2: {
title: 'Vue 2',
color: '4FC08D',
iconUrl: siVuedotjs,
},
vuetify2: {
title: 'Vuetify 2',
color: '1867C0',
iconUrl: siVuetify,
},
vuetify3: {
title: 'Vuetify 3',
color: '1867C0',
iconUrl: siVuetify,
},
watson: {
title: 'Watson',
color: 'BE95FF',
iconUrl: siIbmwatson,
},
wordpress: {
title: 'WordPress',
color: '21759B',
iconUrl: siWordpress,
}
},
carouselIndex: []
]
}),
methods: {
generateBadgen (label, iconUrl) {
// const iconColor = iconUrl.hex
const iconColor = 'FFFFFF' // white
const iconSvg = iconUrl.svg.replace('<path ', `<path fill="#${iconColor}" `)
const svg = badgen({
label: '',
status: label,
// color: iconColor,
color: this.$vuetify.theme.current.colors.backgroundTertiary.slice(1),
style: 'classic',
icon: `data:image/svg+xml;utf8,${encodeURIComponent(iconSvg)}`
})
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`
computed: {
cdn () {
return this.$config.public.cdn
},
showImageCarousel (images, carouselIndex) {
this.$refs.imageCarousel.images = images.map(i => this.cdn+i)
this.$refs.imageCarousel.index = carouselIndex
this.$refs.imageCarousel.show = true
viewCols () {
const horizontal = {
cols: 12,
sm: 12,
md: 12,
lg: 12,
xl: 12
}
const vertical = {
cols: 12,
sm: 6,
md: 6,
lg: 4,
xl: 4
}
return this.view === 'horizontal' ? horizontal : vertical
},
view () {
return this.selectedViewIndex === this.views.horizontal && this.$vuetify.display.smAndUp ? 'horizontal' : 'vertical'
}
}
}

View File

@@ -47,8 +47,12 @@ export default defineNuxtPlugin(nuxtApp => {
blueprint: md3,
defaults: {
VCard: {
border: 'sm',
elevation: 0
},
VCarousel: {
color: 'primary'
}
},
theme: {
// customVariables: ['~/assets/variables.scss'],
@@ -67,8 +71,4 @@ export default defineNuxtPlugin(nuxtApp => {
})
nuxtApp.vueApp.use(vuetify)
// Define global variables
nuxtApp.provide('myName', 'Manuel Veigel')
nuxtApp.provide('myEmail', 'maveigel@gmail.com')
})