mirror of
https://github.com/mmahdium/portfolio.git
synced 2026-08-16 21:14:31 +03:30
feat(resume): enhance spacing, styling, and add markdown text parsing
- Update section spacing from mb-8 to mb-12 for improved visual hierarchy - Refine heading padding and margins for consistent section styling - Adjust work experience highlights spacing and add leading-relaxed for readability - Implement markdown bold text parsing in job highlights using new composable - Add color-coded icons in resume header (red for location, green for phone, amber for email, blue for website) - Reorganize contact information into two lines for better layout organization - Standardize icon sizing with explicit w-4 h-4 dimensions across header - Create useMarkdownText composable for markdown formatting utilities - Update resume data and PDF export endpoint to support enhanced styling - Improve print styling consistency across all resume components
This commit is contained in:
@@ -9,15 +9,15 @@ const props = defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="mb-8 print:mb-5">
|
||||
<section class="mb-12 print:mb-12">
|
||||
<h2
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-1.5 mb-4 print:mb-2.5 tracking-wide">
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-2 mb-6 print:mb-6 tracking-wide">
|
||||
Skills & Qualifications
|
||||
</h2>
|
||||
|
||||
<!-- Technical Skills (categorized) -->
|
||||
<div v-if="skills?.length">
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-3 print:space-y-3">
|
||||
<div v-for="skill in skills" :key="skill.name" class="text-sm">
|
||||
<span class="font-bold text-gray-900">{{ skill.name }}: </span><span class="text-gray-700">{{
|
||||
skill.keywords.join(', ') }}</span>
|
||||
|
||||
@@ -16,13 +16,13 @@ function formatDateRange(start: string, end?: string): string {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="mb-8 print:mb-5">
|
||||
<section class="mb-12 print:mb-12">
|
||||
<h2
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-1.5 mb-4 print:mb-2.5 tracking-wide">
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-2 mb-6 print:mb-6 tracking-wide">
|
||||
Education
|
||||
</h2>
|
||||
|
||||
<div v-for="edu in education" :key="edu.institution + edu.startDate" class="mb-4 last:mb-0">
|
||||
<div v-for="edu in education" :key="edu.institution + edu.startDate" class="mb-4 last:mb-0 print:mb-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-gray-900">{{ edu.studyType }} in {{ edu.area }}</h3>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { WorkExperience } from '~/types/resume'
|
||||
import { parseMarkdownBold } from '~/composables/useMarkdownText'
|
||||
|
||||
interface Props {
|
||||
work: WorkExperience[]
|
||||
@@ -24,13 +25,13 @@ function formatDateRange(start: string, end?: string): string {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="mb-8 print:mb-5">
|
||||
<section class="mb-12 print:mb-12">
|
||||
<h2
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-1.5 mb-4 print:mb-2.5 tracking-wide">
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-2 mb-6 print:mb-6 tracking-wide">
|
||||
Work Experience
|
||||
</h2>
|
||||
|
||||
<div v-for="job in sortedWork" :key="job.company + job.startDate" class="mb-5 last:mb-0 print:mb-4">
|
||||
<div v-for="job in sortedWork" :key="job.company + job.startDate" class="mb-8 last:mb-0 print:mb-8">
|
||||
<div class="flex justify-between items-start gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-sm font-bold text-gray-900 break-words hyphens-none">{{ job.position }}</h3>
|
||||
@@ -40,11 +41,10 @@ function formatDateRange(start: string, end?: string): string {
|
||||
{{ formatDateRange(job.startDate, job.endDate) }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="mt-2.5 space-y-1.5 print:mt-1.5 print:space-y-1">
|
||||
<ul class="mt-4 space-y-2.5 print:mt-4 print:space-y-2.5">
|
||||
<li v-for="(highlight, idx) in job.highlights" :key="idx"
|
||||
class="text-sm text-gray-700 pl-4 relative before:content-['•'] before:absolute before:left-0 before:text-gray-500 before:font-bold print:before:text-gray-500 break-words hyphens-none">
|
||||
{{ highlight }}
|
||||
</li>
|
||||
class="text-sm text-gray-700 pl-4 relative before:content-['•'] before:absolute before:left-0 before:text-gray-500 before:font-bold print:before:text-gray-500 break-words hyphens-none leading-relaxed"
|
||||
v-html="parseMarkdownBold(highlight)" />
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -9,7 +9,7 @@ defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex gap-6 mb-8 print:mb-4 print:pb-1">
|
||||
<div class="flex gap-6 mb-10 print:mb-8 print:pb-2">
|
||||
<!-- Profile Photo (if provided) -->
|
||||
<div v-if="basics.image" class="flex-shrink-0">
|
||||
<img :src="basics.image" :alt="basics.name" class="w-32 h-32 object-cover rounded-full print:w-24 print:h-24" />
|
||||
@@ -20,31 +20,35 @@ defineProps<Props>()
|
||||
<h1 class="text-3xl font-bold text-gray-900 print:text-xl print:mb-0.5">{{ basics.name }}</h1>
|
||||
<p class="text-xl text-gray-600 mt-1 print:text-base print:mt-0">{{ basics.label }}</p>
|
||||
|
||||
<!-- Line 1: Location, Phone, Email -->
|
||||
<div class="mt-3 flex flex-wrap gap-x-5 gap-y-2 text-sm text-gray-700 print:mt-2 print:text-xs print:gap-x-4">
|
||||
<!-- Location -->
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<UIcon name="i-heroicons-map-pin" class="text-gray-500 flex-shrink-0" />
|
||||
<UIcon name="i-heroicons-map-pin" class="text-red-500 flex-shrink-0 w-4 h-4" />
|
||||
{{ basics.location.city }}, {{ basics.location.country }}
|
||||
</span>
|
||||
|
||||
<!-- Phone -->
|
||||
<a :href="`tel:${basics.phone}`" class="inline-flex items-center gap-1.5 hover:text-blue-600 transition-colors">
|
||||
<UIcon name="i-heroicons-phone" class="text-gray-500 flex-shrink-0" />
|
||||
<UIcon name="i-heroicons-phone" class="text-green-600 flex-shrink-0 w-4 h-4" />
|
||||
{{ basics.phone }}
|
||||
</a>
|
||||
|
||||
<!-- Email -->
|
||||
<a :href="`mailto:${basics.email}`"
|
||||
class="inline-flex items-center gap-1.5 hover:text-blue-600 transition-colors">
|
||||
<UIcon name="i-heroicons-envelope" class="text-gray-500 flex-shrink-0" />
|
||||
<UIcon name="i-heroicons-envelope" class="text-amber-500 flex-shrink-0 w-4 h-4" />
|
||||
{{ basics.email }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Line 2: Website, LinkedIn, GitHub -->
|
||||
<div class="mt-2 flex flex-wrap gap-x-5 gap-y-2 text-sm text-gray-700 print:mt-1 print:text-xs print:gap-x-4">
|
||||
<!-- Website (Portfolio) - Bold & Colored -->
|
||||
<a v-if="basics.profiles?.find(p => p.network === 'Portfolio')"
|
||||
:href="basics.profiles.find(p => p.network === 'Portfolio')!.url" target="_blank"
|
||||
class="inline-flex items-center gap-1.5 font-semibold text-blue-600 hover:text-blue-700 transition-colors print:text-blue-600">
|
||||
<UIcon name="i-heroicons-globe-alt" class="text-blue-600 flex-shrink-0" />
|
||||
<UIcon name="i-heroicons-globe-alt" class="text-blue-600 flex-shrink-0 w-4 h-4" />
|
||||
Website
|
||||
</a>
|
||||
|
||||
@@ -52,7 +56,7 @@ defineProps<Props>()
|
||||
<a v-if="basics.profiles?.find(p => p.network === 'LinkedIn')"
|
||||
:href="basics.profiles.find(p => p.network === 'LinkedIn')!.url" target="_blank"
|
||||
class="inline-flex items-center gap-1.5 hover:text-blue-600 transition-colors">
|
||||
<UIcon name="i-mdi-linkedin" class="text-gray-500 flex-shrink-0" />
|
||||
<UIcon name="i-mdi-linkedin" class="text-[#0A66C2] flex-shrink-0 w-4 h-4" />
|
||||
LinkedIn
|
||||
</a>
|
||||
|
||||
@@ -60,7 +64,7 @@ defineProps<Props>()
|
||||
<a v-if="basics.profiles?.find(p => p.network === 'GitHub')"
|
||||
:href="basics.profiles.find(p => p.network === 'GitHub')!.url" target="_blank"
|
||||
class="inline-flex items-center gap-1.5 hover:text-blue-600 transition-colors">
|
||||
<UIcon name="i-mdi-github" class="text-gray-500 flex-shrink-0" />
|
||||
<UIcon name="i-mdi-github" class="text-gray-800 flex-shrink-0 w-4 h-4" />
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -10,14 +10,14 @@ const props = defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="mb-8 print:mb-5">
|
||||
<section class="mb-12 print:mb-12">
|
||||
<h2
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-1.5 mb-4 print:mb-2.5 tracking-wide">
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-2 mb-6 print:mb-6 tracking-wide">
|
||||
Languages & Certifications
|
||||
</h2>
|
||||
|
||||
<!-- Languages -->
|
||||
<div v-if="languages?.length" class="mb-3">
|
||||
<div v-if="languages?.length" class="mb-2 print:mb-2">
|
||||
<span class="text-sm font-bold text-gray-900">Languages: </span>
|
||||
<span class="text-sm text-gray-700">
|
||||
<template v-for="(lang, idx) in languages" :key="lang.language">
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { parseMarkdownBold } from '~/composables/useMarkdownText'
|
||||
|
||||
interface Props {
|
||||
summary: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const parsedSummary = computed(() => parseMarkdownBold(props.summary))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="mb-8 print:mb-5">
|
||||
<section class="mb-12 print:mb-12">
|
||||
<h2
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-1.5 mb-3 print:mb-2 tracking-wide">
|
||||
class="text-base font-bold text-blue-700 uppercase border-b-2 border-blue-600 pb-2 mb-5 print:mb-5 tracking-wide">
|
||||
Summary
|
||||
</h2>
|
||||
<p class="text-sm text-gray-800 leading-relaxed print:leading-normal">{{ summary }}</p>
|
||||
<p class="text-sm text-gray-800 leading-loose print:leading-loose" v-html="parsedSummary" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user