Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Deploy to GitHub Pages

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Generate static site
run: bun run generate

- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/vue-recreation'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./.output/public
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# FixeQ Portfolio - Git Ignore Rules

# Nuxt
.nuxt
.output
dist
node_modules
.env

# Development files
.vscode/
.idea/
Expand Down
15 changes: 15 additions & 0 deletions app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<NuxtPage />
</template>

<script setup>
useHead({
htmlAttrs: {
lang: 'en',
class: 'scroll-smooth'
},
bodyAttrs: {
class: 'bg-dark text-white'
}
})
</script>
Binary file removed assets/projects/SamFirmUpdater/banner.png
Binary file not shown.
1,770 changes: 1,770 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions components/AboutSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<section id="about" class="py-20 px-6 relative">
<div class="container mx-auto max-w-5xl">
<h2 class="text-5xl font-display font-bold text-center mb-4">
About <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary">Me</span>
</h2>
<p class="text-center text-gray-400 mb-12">Get to know me better</p>

<div class="bg-gradient-to-br from-white/10 to-white/5 rounded-3xl p-8 md:p-12 border border-white/20 shadow-2xl backdrop-blur-sm relative overflow-hidden group">
<!-- Animated border gradient -->
<div class="absolute inset-0 bg-gradient-to-r from-primary via-secondary to-primary opacity-0 group-hover:opacity-20 blur-xl transition-opacity duration-500"></div>

<div class="relative z-10">
<p class="text-lg md:text-xl text-gray-300 leading-relaxed mb-4 text-center max-w-3xl mx-auto">
{{ data?.developer?.description }}
</p>
<p class="text-base text-gray-400 text-center mb-10 italic">
Also loves to <span class="text-primary font-semibold">vibecode</span> – coding with AI assistance and good vibes ✨🤖
</p>

<div class="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8">
<div v-for="(stat, idx) in stats" :key="stat.label"
class="text-center p-6 rounded-2xl bg-gradient-to-br from-white/5 to-transparent border border-white/10 hover:border-primary/50 transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-primary/20 group/stat"
:style="{ animationDelay: `${idx * 100}ms` }">
<div :class="stat.label === 'Location' ? 'text-4xl md:text-5xl font-bold text-white mb-3 group-hover/stat:scale-110 transition-transform' : 'text-4xl md:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary mb-3 group-hover/stat:scale-110 transition-transform'">
{{ stat.value }}
</div>
<div class="text-sm md:text-base text-gray-400 group-hover/stat:text-gray-300 transition-colors">{{ stat.label }}</div>
</div>
</div>

<!-- Achievements -->
<div class="mt-12 grid md:grid-cols-2 gap-4">
<div v-for="achievement in data?.achievements" :key="achievement.title"
class="flex items-start gap-4 p-4 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 hover:border-primary/30 transition-all duration-300 group/achievement">
<div class="text-3xl">{{ achievement.icon }}</div>
<div>
<h4 class="font-bold text-lg mb-1 group-hover/achievement:text-primary transition-colors">{{ achievement.title }}</h4>
<p class="text-sm text-gray-400">{{ achievement.description }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

<script setup>
const props = defineProps({
data: Object
})

const stats = computed(() => [
{ label: 'Experience', value: props.data?.developer?.experience },
{ label: 'Projects', value: props.data?.developer?.projectsCompleted },
{ label: 'Technologies', value: props.data?.developer?.technologiesMastered },
{ label: 'Location', value: '🇵🇱' }
])
</script>
72 changes: 72 additions & 0 deletions components/AppFooter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<footer id="contact" class="py-20 px-6 bg-gradient-to-t from-white/5 to-transparent border-t border-white/10 relative overflow-hidden">
<!-- Animated background -->
<div class="absolute inset-0 bg-[radial-gradient(circle_at_center,#00D9FF10_0%,transparent_70%)] animate-pulse-slow"></div>

<div class="container mx-auto max-w-4xl text-center relative z-10">
<h2 class="text-5xl font-display font-bold mb-4">
Get in <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary">Touch</span>
</h2>

<p class="text-xl text-gray-400 mb-12">
Interested in working together? Let's connect!
</p>

<div class="flex flex-wrap gap-4 justify-center mb-12">
<component
v-for="social in data?.socials"
:key="social.name"
:is="social.name === 'Discord' ? 'button' : 'a'"
:href="social.name !== 'Discord' ? social.url : undefined"
:target="social.name !== 'Discord' ? '_blank' : undefined"
:rel="social.name !== 'Discord' ? 'noopener' : undefined"
@click="social.name === 'Discord' ? openDiscordModal(social) : null"
class="group relative px-8 py-4 bg-gradient-to-r from-white/5 to-white/10 hover:from-white/10 hover:to-white/20 border border-white/10 hover:border-primary/50 rounded-xl transition-all duration-300 flex items-center gap-3 hover:scale-105 hover:shadow-xl hover:shadow-primary/20">
<div class="absolute inset-0 rounded-xl bg-gradient-to-r opacity-0 group-hover:opacity-10 blur-xl transition-opacity"
:style="{ background: social.color }"></div>
<font-awesome-icon :icon="['fab', social.icon]" class="w-6 h-6 relative z-10 group-hover:scale-110 transition-transform" />
<span class="relative z-10 font-medium group-hover:text-primary transition-colors">{{ social.name }}</span>
</component>
</div>

<!-- Discord Modal -->
<DiscordModal
:isOpen="showDiscordModal"
:username="discordUsername"
:profileUrl="discordProfileUrl"
@close="showDiscordModal = false"
/>

<div class="pt-8 border-t border-white/10">
<p class="text-gray-500 text-sm mb-2">
&copy; 2024 {{ data?.developer?.name }}
</p>
<p class="text-gray-600 text-xs">
Built with 💙 using <span class="text-primary">Nuxt 3</span>, <span class="text-secondary">Tailwind CSS</span> & <span class="text-primary">Three.js</span>
</p>
</div>
</div>
</footer>
</template>

<script setup>
const props = defineProps({
data: Object
})

const showDiscordModal = ref(false)
const discordUsername = ref('')
const discordProfileUrl = ref('')

const openDiscordModal = (social) => {
discordUsername.value = social.username || 'fixeq.dev'
discordProfileUrl.value = social.url
showDiscordModal.value = true
}
</script>

<style scoped>
.animate-pulse-slow {
animation: pulse 8s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
45 changes: 45 additions & 0 deletions components/AppHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<header class="fixed top-0 w-full bg-dark/60 backdrop-blur-xl border-b border-white/10 z-50 shadow-lg">
<nav class="container mx-auto px-6 py-4">
<div class="flex items-center justify-between">
<a href="#home" class="text-2xl md:text-3xl font-display font-bold">
<span class="text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary hover:from-secondary hover:to-primary transition-all duration-300">
FixeQ
</span>
</a>
<div class="hidden md:flex gap-8">
<a v-for="link in links" :key="link.href" :href="link.href"
class="relative text-gray-300 hover:text-primary transition-colors duration-300 font-medium group">
{{ link.label }}
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-primary to-secondary group-hover:w-full transition-all duration-300"></span>
</a>
</div>

<!-- Mobile menu button -->
<button @click="mobileMenuOpen = !mobileMenuOpen" class="md:hidden text-gray-300 hover:text-primary">
<font-awesome-icon :icon="mobileMenuOpen ? 'times' : 'bars'" class="w-6 h-6" />
</button>
</div>

<!-- Mobile menu -->
<div v-show="mobileMenuOpen" class="md:hidden mt-4 pb-4 space-y-4">
<a v-for="link in links" :key="link.href" :href="link.href"
@click="mobileMenuOpen = false"
class="block text-gray-300 hover:text-primary transition-colors duration-300 font-medium">
{{ link.label }}
</a>
</div>
</nav>
</header>
</template>

<script setup>
const links = [
{ href: '#about', label: 'About' },
{ href: '#skills', label: 'Skills' },
{ href: '#projects', label: 'Projects' },
{ href: '#contact', label: 'Contact' }
]

const mobileMenuOpen = ref(false)
</script>
86 changes: 86 additions & 0 deletions components/DiscordModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="isOpen" @click="closeModal" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div @click.stop class="bg-gradient-to-br from-[#5865F2]/20 to-[#5865F2]/10 backdrop-blur-xl border border-[#5865F2]/30 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl shadow-[#5865F2]/20">
<div class="flex justify-between items-center mb-6">
<h3 class="text-2xl font-bold text-white flex items-center gap-2">
<font-awesome-icon :icon="['fab', 'discord']" class="text-[#5865F2]" />
Discord
</h3>
<button @click="closeModal" class="text-gray-400 hover:text-white transition">
<font-awesome-icon icon="times" class="w-6 h-6" />
</button>
</div>

<div class="space-y-4">
<div class="bg-black/30 rounded-xl p-4 border border-white/10">
<p class="text-sm text-gray-400 mb-2">Username</p>
<div class="flex items-center justify-between">
<p class="text-xl font-mono text-white">{{ username }}</p>
<button @click="copyUsername" class="px-3 py-1 bg-[#5865F2]/20 hover:bg-[#5865F2]/30 text-[#5865F2] rounded-lg transition text-sm">
{{ copied ? 'Copied!' : 'Copy' }}
</button>
</div>
</div>

<a :href="profileUrl" target="_blank" rel="noopener"
class="block w-full py-3 bg-[#5865F2] hover:bg-[#5865F2]/90 text-white rounded-lg font-semibold text-center transition shadow-lg shadow-[#5865F2]/30">
Open Discord Profile
</a>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>

<script setup>
const props = defineProps({
isOpen: Boolean,
username: String,
profileUrl: String
})

const emit = defineEmits(['close'])

const copied = ref(false)

const closeModal = () => {
emit('close')
}

const copyUsername = async () => {
try {
await navigator.clipboard.writeText(props.username)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch (err) {
console.error('Failed to copy:', err)
}
}
</script>

<style scoped>
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}

.modal-enter-from,
.modal-leave-to {
opacity: 0;
}

.modal-enter-active > div,
.modal-leave-active > div {
transition: transform 0.3s ease;
}

.modal-enter-from > div,
.modal-leave-to > div {
transform: scale(0.9);
}
</style>
Loading