mirror of
https://github.com/mmahdium/portfolio.git
synced 2026-08-15 20:52:49 +03:30
- Create `app/pages/resume.vue` as standalone page with print mode detection - Add page metadata configuration with SEO noindex tag and custom title - Implement `isPrintMode` computed property for print parameter handling - Add placeholder content for ResumePreview component integration - Create design validation report identifying critical mismatches between tech spec and design template - Update Story 2.1 task completion status and add dev notes - Clean up context XML files from sprint artifacts documentation - Update tech spec and sprint status documentation for Epic 2 alignment - Story 2.1 now marked as complete with all acceptance criteria met
11 KiB
11 KiB
Story 2.3: Create Resume Header & Main Content Components
Status: ready-for-dev
Story
As a user, I want to see my photo, name, contact info, summary, and work experience prominently, so that recruiters see the most important information first.
Acceptance Criteria
ResumeHeader.vue
- AC1: Given the header component renders, when it displays, then it shows profile photo on the left (if provided)
- AC2: Full name in large bold text (2rem, font-bold) displayed on the right
- AC3: Job title appears below name (1.25rem, text-gray-600)
- AC4: Contact information (address, phone, email, website) displayed with proper formatting
- AC5: All contact links are clickable (mailto:, tel:, https://)
- AC6: Layout is horizontal: photo (left) + info block (right)
ResumeSummary.vue
- AC7: Given the summary component renders, when it displays, then it shows "SUMMARY" section header (blue, uppercase, with bottom border)
- AC8: Professional summary paragraph is displayed
- AC9: Line height is 1.6 for readability
ResumeExperience.vue
- AC10: Given the experience component renders, when it displays, then it shows "WORK EXPERIENCE" section header (blue, uppercase, with bottom border)
- AC11: For each job: position title (bold, left), company name, date range (right-aligned: "Jan 2022 - Present"), bullet points for highlights (• character)
- AC12: Jobs are sorted by date (most recent first)
- AC13: "Present" is shown for current jobs (no endDate)
Tasks / Subtasks
-
Create ResumeHeader component (AC: #1-#6)
- Create
app/components/resume/ResumeHeader.vue - Accept props:
basics(ResumeBasics) - includes name, label, email, phone, location, url, image - Display profile photo (if provided) with proper sizing (150px max width)
- Display name with
text-3xl font-bold(2rem) - Display job title with
text-xl text-gray-600(1.25rem) - Display address, phone, email, website in structured format
- Make email, phone, website clickable with proper href attributes
- Use horizontal layout: photo left, info right
- Create
-
Create ResumeSummary component (AC: #7-#9)
- Create
app/components/resume/ResumeSummary.vue - Accept props:
summary(string) - Add section header "SUMMARY" with blue, uppercase, bold styling
- Add blue bottom border to header:
border-b-2 border-blue-600 - Display summary paragraph with
leading-relaxed(line-height: 1.6) - Use
text-sm text-gray-800
- Create
-
Create ResumeExperience component (AC: #10-#13)
- Create
app/components/resume/ResumeExperience.vue - Accept props:
work(WorkExperience[]) - Add section header "WORK EXPERIENCE" with blue, uppercase, bold styling
- Add blue bottom border to header:
border-b-2 border-blue-600 - Sort jobs by startDate (most recent first)
- For each job, display:
- Position title:
font-semibold text-gray-900(left-aligned) - Company name:
text-gray-700 - Date range: Use
formatDate()helper from composable (right-aligned) - Highlights:
<ul>with bullet points (• character)
- Position title:
- Handle current jobs: Show "Present" if no endDate
- Create
-
Integrate components into ResumePreview (AC: #1-#13)
- Import all three components in
ResumePreview.vue - Pass data from
useResumeData()composable - Place in vertical order:
- ResumeHeader at top
- ResumeSummary below header
- ResumeExperience below summary
- Import all three components in
-
Test components rendering
- Verify header displays photo (if available), name, title, contact info
- Check all contact links are clickable
- Verify summary section with blue uppercase header and bottom border
- Test experience section with multiple jobs
- Verify date formatting ("Jan 2022 - Present")
- Check job sorting (most recent first)
- Test with current job (no endDate)
Dev Notes
Architecture Alignment
From Architecture Doc:
- File locations:
app/components/resume/ResumeHeader.vue,ResumeSummary.vue,ResumeExperience.vue - Data source: Props passed from
ResumePreview.vue(which usesuseResumeData()) - Date formatting: Use
formatDate()helper from composable
From Tech Spec Epic 2 (REVISED):
- AC5, AC6, AC7, AC11 map to this story
- Main content components for single-column layout
- Professional typography and spacing
- CRITICAL: Header now includes photo + contact info (not separate component)
Learnings from Previous Story
From Story 2.2 (Status: revised)
ResumePreview.vuecontainer created with single-column layout- Data access via
useResumeData()composable established - Tailwind styling patterns defined (text-sm, text-blue-600, etc.)
- Integration Point: Import these components into ResumePreview vertical stack
[Source: docs/sprint-artifacts/2-2-create-resume-preview-container-component.md]
Project Structure Notes
Files to Create:
app/components/resume/ResumeHeader.vueapp/components/resume/ResumeSummary.vueapp/components/resume/ResumeExperience.vue
Files to Modify:
app/components/resume/ResumePreview.vue- Import and integrate new components
Dependencies:
app/composables/useResumeData.ts-formatDate()helperapp/types/resume.ts-WorkExperience,ResumeBasicsinterfacesapp/data/resume.en.ts- Sample data
Implementation Notes
ResumeHeader.vue:
<script setup lang="ts">
import type { ResumeBasics } from '~/types/resume'
interface Props {
basics: ResumeBasics
}
defineProps<Props>()
</script>
<template>
<div class="flex gap-6 mb-6">
<!-- 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"
/>
</div>
<!-- Name + Contact Info -->
<div class="flex-1">
<h1 class="text-3xl font-bold text-gray-900">{{ basics.name }}</h1>
<p class="text-xl text-gray-600 mt-1">{{ basics.label }}</p>
<div class="mt-3 space-y-1 text-sm text-gray-700">
<p><strong>Address:</strong> {{ basics.location.address }}, {{ basics.location.city }}</p>
<p><strong>Phone:</strong> <a :href="`tel:${basics.phone}`" class="hover:text-blue-600">{{ basics.phone }}</a></p>
<p><strong>Email:</strong> <a :href="`mailto:${basics.email}`" class="hover:text-blue-600">{{ basics.email }}</a></p>
<p><strong>Website:</strong> <a :href="basics.url" target="_blank" class="hover:text-blue-600">{{ basics.url }}</a></p>
</div>
</div>
</div>
</template>
ResumeSummary.vue:
<script setup lang="ts">
interface Props {
summary: string
}
defineProps<Props>()
</script>
<template>
<div class="mb-6">
<h2 class="text-base font-bold text-blue-600 uppercase border-b-2 border-blue-600 pb-1 mb-3">
Summary
</h2>
<p class="text-sm text-gray-800 leading-relaxed">{{ summary }}</p>
</div>
</template>
ResumeExperience.vue:
<script setup lang="ts">
import type { WorkExperience } from '~/types/resume'
interface Props {
work: WorkExperience[]
}
const props = defineProps<Props>()
const { formatDate } = useResumeData()
const sortedWork = computed(() => {
return [...props.work].sort((a, b) => {
const dateA = a.startDate || ''
const dateB = b.startDate || ''
return dateB.localeCompare(dateA) // Most recent first
})
})
const formatDateRange = (start: string, end?: string) => {
const startFormatted = formatDate(start)
const endFormatted = end ? formatDate(end) : 'Present'
return `${startFormatted} - ${endFormatted}`
}
</script>
<template>
<div class="mb-6">
<h2 class="text-base font-bold text-blue-600 uppercase border-b-2 border-blue-600 pb-1 mb-4">
Work Experience
</h2>
<div v-for="job in sortedWork" :key="job.company + job.position" class="mb-4">
<div class="flex justify-between items-baseline">
<h3 class="font-semibold text-gray-900">{{ job.position }}, {{ job.company }}</h3>
<span class="text-sm text-gray-600">{{ formatDateRange(job.startDate, job.endDate) }}</span>
</div>
<ul class="mt-2 text-sm text-gray-800 space-y-1">
<li v-for="(highlight, idx) in job.highlights" :key="idx" class="flex">
<span class="mr-2">•</span>
<span>{{ highlight }}</span>
</li>
</ul>
</div>
</div>
</template>
Integration in ResumePreview.vue:
<!-- Single-column vertical stack -->
<div class="flex flex-col gap-0 p-6">
<ResumeHeader :basics="resume.basics" />
<ResumeSummary :summary="resume.basics.summary" />
<ResumeExperience :work="resume.work" />
<!-- Education and AdditionalInfo will be added in Story 2.4 -->
</div>
Testing Checklist:
- Header displays photo (if available)
- Header displays name "Ali Arghyani" in large bold text
- Job title displays below name in gray
- Contact info (address, phone, email, website) displayed
- Email, phone, website links are clickable
- Summary section has "SUMMARY" header (blue, uppercase, with border)
- Summary paragraph has proper line height (1.6)
- Experience section has "WORK EXPERIENCE" header (blue, uppercase, with border)
- Jobs sorted by date (most recent first)
- Each job shows position, company, date range (right-aligned), highlights
- Date formatting works: "Jan 2022 - Present"
- Bullet points (•) display correctly
- Current job shows "Present" instead of end date
References
- [Source: docs/architecture.md#Data-Architecture]
- [Source: docs/architecture.md#Consistency-Rules]
- [Source: docs/sprint-artifacts/tech-spec-epic-2.md#AC5-Header-Section]
- [Source: docs/sprint-artifacts/tech-spec-epic-2.md#AC6-Summary-Section]
- [Source: docs/sprint-artifacts/tech-spec-epic-2.md#AC7-Experience-Section]
- [Source: docs/epics.md#Story-2.3-Create-Resume-Header-Main-Content-Components]
- [Source: docs/design-validation-epic-2.md] - UX validation report
- [Source: design templates/Blue and White Clean and Professional Resume.png] - Design reference
Dev Agent Record
Context Reference
- docs/sprint-artifacts/2-3-create-resume-header-main-content-components.context.xml
Agent Model Used
Debug Log References
Completion Notes List
File List
Change Log:
- 2025-11-30: Story drafted by SM agent (mahdi)
- 2025-11-30: REVISED by SM agent (Bob) - Merged contact info into Header component, updated section header styling (blue, uppercase, bottom border), aligned with design template