mirror of
https://github.com/mmahdium/portfolio.git
synced 2026-08-16 21:14:31 +03:30
Epic 2: Resume Preview Page - All 5 stories completed Story 2-1: Create Resume Page Route - Add /resume standalone page with layout: false - Implement print mode detection (?print=true) - Add SEO meta tags (noindex for privacy) - Set page title "Resume - Ali Arghyani" Story 2-2: Create ResumePreview Container - Add single-column A4 container (210mm × 297mm) - Implement white background with 24px margins - Add print styles with .no-print class - Configure responsive layout Story 2-3: Create Header, Summary, Experience Components - Add ResumeHeader with profile photo, name, contact info - Add ResumeSummary with blue uppercase section header - Add ResumeExperience with sorted jobs and date formatting - Add image property to ResumeBasics interface - Integrate all components into ResumePreview Story 2-4: Create Education & Additional Info Components - Add ResumeEducation with date formatting - Add ResumeAdditionalInfo with skills, languages, certifications - Integrate components into ResumePreview Story 2-5: Create Download Button Component - Add ResumeDownloadButton FAB (fixed bottom-right) - Add download icon and "Download PDF" text - Implement print mode detection - Add placeholder click handler for Epic 3 integration - Responsive: text on desktop, icon-only on mobile Additional: - Add profile image to resume data (/img/AliProfile.webp) - Update sprint-status.yaml: all Epic 2 stories marked done Files Created: - app/pages/resume.vue - app/components/resume/ResumePreview.vue - app/components/resume/ResumeHeader.vue - app/components/resume/ResumeSummary.vue - app/components/resume/ResumeExperience.vue - app/components/resume/ResumeEducation.vue - app/components/resume/ResumeAdditionalInfo.vue - app/components/resume/ResumeDownloadButton.vue Files Modified: - app/types/resume.ts (added image property) - app/data/resume.en.ts (added profile image) - docs/sprint-artifacts/sprint-status.yaml (updated story statuses) - docs/sprint-artifacts/2-1-create-resume-page-route.md (completed) - docs/sprint-artifacts/2-2-create-resume-preview-container-component.md (completed) - docs/sprint-artifacts/2-3-create-resume-header-main-content-components.md (completed) - docs/sprint-artifacts/2-4-create-resume-sidebar-components.md (completed) - docs/sprint-artifacts/2-5-create-download-button-component.md (completed) Closes Epic 2 Closes Story 2-1, 2-2, 2-3, 2-4, 2-5
294 lines
10 KiB
Markdown
294 lines
10 KiB
Markdown
# Story 2.4: Create Resume Education & Additional Info Components
|
|
|
|
Status: done
|
|
|
|
## Story
|
|
|
|
As a user,
|
|
I want to see my education, skills, languages, and certifications organized clearly,
|
|
so that recruiters can quickly assess my qualifications.
|
|
|
|
## Acceptance Criteria
|
|
|
|
### ResumeEducation.vue
|
|
1. **AC1:** Given the education component renders, when it displays, then it shows "EDUCATION" section header (blue, uppercase, with bottom border)
|
|
2. **AC2:** For each degree: degree type and field (e.g., "Bachelor of Mechatronics Engineering with Honours")
|
|
3. **AC3:** Institution name displayed
|
|
4. **AC4:** Date range formatted and right-aligned (e.g., "Aug 2016 - Oct 2019")
|
|
5. **AC5:** Optional bullet points for achievements or coursework
|
|
|
|
### ResumeAdditionalInfo.vue
|
|
6. **AC6:** Given the additional info component renders, when it displays, then it shows "ADDITIONAL INFORMATION" section header (blue, uppercase, with bottom border)
|
|
7. **AC7:** "Technical Skills:" label with categorized or comma-separated list
|
|
8. **AC8:** "Languages:" label with fluency levels (e.g., "English, Malay, Japan")
|
|
9. **AC9:** "Certifications:" label (if present) with name and issuer
|
|
10. **AC10:** "Awards/Activities:" label (if present) with brief descriptions
|
|
|
|
## Tasks / Subtasks
|
|
|
|
- [x] Create ResumeEducation component (AC: #1-#5)
|
|
- [x] Create `app/components/resume/ResumeEducation.vue`
|
|
- [x] Accept props: `education` (Education[])
|
|
- [x] Add section header "EDUCATION" with blue, uppercase, bold styling
|
|
- [x] Add blue bottom border to header: `border-b-2 border-blue-600`
|
|
- [x] For each degree, display:
|
|
- Degree type and field: `font-semibold text-gray-900`
|
|
- Institution name: `text-gray-700`
|
|
- Date range: Use `formatDate()` helper (right-aligned)
|
|
- Optional bullet points for achievements
|
|
|
|
- [x] Create ResumeAdditionalInfo component (AC: #6-#10)
|
|
- [x] Create `app/components/resume/ResumeAdditionalInfo.vue`
|
|
- [x] Accept props: `skills` (Skill[]), `languages` (Language[]), `certificates?` (Certificate[]), `awards?` (Award[])
|
|
- [x] Add section header "ADDITIONAL INFORMATION" with blue, uppercase, bold styling
|
|
- [x] Add blue bottom border to header: `border-b-2 border-blue-600`
|
|
- [x] Display "Technical Skills:" with categorized list or comma-separated keywords
|
|
- [x] Display "Languages:" with comma-separated list and fluency
|
|
- [x] Display "Certifications:" (if provided) with name and issuer
|
|
- [x] Display "Awards/Activities:" (if provided) with descriptions
|
|
|
|
- [x] Integrate components into ResumePreview (AC: #1-#10)
|
|
- [x] Import both components in `ResumePreview.vue`
|
|
- [x] Pass data from `useResumeData()` composable
|
|
- [x] Place in vertical order (after Experience):
|
|
- ResumeEducation
|
|
- ResumeAdditionalInfo
|
|
|
|
- [x] Test components rendering
|
|
- [x] Verify education section with blue uppercase header and bottom border
|
|
- [x] Check degree, institution, date formatting
|
|
- [x] Test with multiple education entries
|
|
- [x] Verify additional info section with all subsections
|
|
- [x] Check skills categorization or comma-separated display
|
|
- [x] Test languages display
|
|
- [x] Verify optional certifications and awards (if present)
|
|
|
|
## Dev Notes
|
|
|
|
### Architecture Alignment
|
|
|
|
**From Architecture Doc:**
|
|
- File locations: `app/components/resume/ResumeEducation.vue`, `ResumeAdditionalInfo.vue`
|
|
- Data source: Props passed from `ResumePreview.vue`
|
|
- Date formatting: Use `formatDate()` helper from composable
|
|
|
|
**From Tech Spec Epic 2 (REVISED):**
|
|
- AC8, AC9 map to this story
|
|
- **CRITICAL:** Education moved from sidebar to main body
|
|
- **CRITICAL:** Skills, Languages, Certificates consolidated into single AdditionalInfo component
|
|
- No more standalone Contact, Skills, Languages components
|
|
|
|
### Learnings from Previous Story
|
|
|
|
**From Story 2.3 (Status: revised)**
|
|
- Main content components created (Header with contact, Summary, Experience)
|
|
- Section header styling established: blue, uppercase, bold, with bottom border
|
|
- Date formatting via `formatDate()` helper
|
|
- **Integration Point**: Add Education and AdditionalInfo below Experience
|
|
|
|
[Source: docs/sprint-artifacts/2-3-create-resume-header-main-content-components.md]
|
|
|
|
### Project Structure Notes
|
|
|
|
**Files to Create:**
|
|
- `app/components/resume/ResumeEducation.vue`
|
|
- `app/components/resume/ResumeAdditionalInfo.vue`
|
|
|
|
**Files to Modify:**
|
|
- `app/components/resume/ResumePreview.vue` - Import and integrate new components
|
|
|
|
**Files NOT Created (vs. old architecture):**
|
|
- ~~`ResumeContact.vue`~~ - Merged into ResumeHeader
|
|
- ~~`ResumeSkills.vue`~~ - Merged into ResumeAdditionalInfo
|
|
- ~~`ResumeLanguages.vue`~~ - Merged into ResumeAdditionalInfo
|
|
|
|
**Dependencies:**
|
|
- `app/composables/useResumeData.ts` - `formatDate()` helper
|
|
- `app/types/resume.ts` - Type interfaces
|
|
- `app/data/resume.en.ts` - Sample data
|
|
|
|
### Implementation Notes
|
|
|
|
**ResumeEducation.vue:**
|
|
```vue
|
|
<script setup lang="ts">
|
|
import type { Education } from '~/types/resume'
|
|
|
|
interface Props {
|
|
education: Education[]
|
|
}
|
|
|
|
defineProps<Props>()
|
|
const { formatDate } = useResumeData()
|
|
|
|
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">
|
|
Education
|
|
</h2>
|
|
|
|
<div v-for="edu in education" :key="edu.institution" class="mb-4">
|
|
<div class="flex justify-between items-baseline">
|
|
<h3 class="font-semibold text-gray-900">
|
|
{{ edu.studyType }} {{ edu.area }}
|
|
</h3>
|
|
<span class="text-sm text-gray-600">{{ formatDateRange(edu.startDate, edu.endDate) }}</span>
|
|
</div>
|
|
<p class="text-sm text-gray-700">{{ edu.institution }}</p>
|
|
|
|
<!-- Optional: Coursework or achievements -->
|
|
<ul v-if="edu.courses && edu.courses.length > 0" class="mt-2 text-sm text-gray-800 space-y-1">
|
|
<li v-for="(course, idx) in edu.courses" :key="idx" class="flex">
|
|
<span class="mr-2">•</span>
|
|
<span>{{ course }}</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
**ResumeAdditionalInfo.vue:**
|
|
```vue
|
|
<script setup lang="ts">
|
|
import type { Skill, Language, Certificate, Award } from '~/types/resume'
|
|
|
|
interface Props {
|
|
skills: Skill[]
|
|
languages?: Language[]
|
|
certificates?: Certificate[]
|
|
awards?: Award[]
|
|
}
|
|
|
|
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-4">
|
|
Additional Information
|
|
</h2>
|
|
|
|
<!-- Technical Skills -->
|
|
<div class="mb-3">
|
|
<p class="text-sm text-gray-900">
|
|
<strong>Technical Skills:</strong>
|
|
<span class="text-gray-700">
|
|
{{ skills.map(s => s.keywords.join(', ')).join('; ') }}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Languages -->
|
|
<div v-if="languages && languages.length > 0" class="mb-3">
|
|
<p class="text-sm text-gray-900">
|
|
<strong>Languages:</strong>
|
|
<span class="text-gray-700">
|
|
{{ languages.map(l => l.language).join(', ') }}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Certifications -->
|
|
<div v-if="certificates && certificates.length > 0" class="mb-3">
|
|
<p class="text-sm text-gray-900"><strong>Certifications:</strong></p>
|
|
<ul class="text-sm text-gray-700 space-y-1 ml-4">
|
|
<li v-for="cert in certificates" :key="cert.name">
|
|
{{ cert.name }} ({{ cert.issuer }})
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Awards/Activities -->
|
|
<div v-if="awards && awards.length > 0" class="mb-3">
|
|
<p class="text-sm text-gray-900"><strong>Awards/Activities:</strong></p>
|
|
<ul class="text-sm text-gray-700 space-y-1 ml-4">
|
|
<li v-for="award in awards" :key="award.title">
|
|
{{ award.title }} - {{ award.summary }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
**Integration in ResumePreview.vue:**
|
|
```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" />
|
|
<ResumeEducation :education="resume.education" />
|
|
<ResumeAdditionalInfo
|
|
:skills="resume.skills"
|
|
:languages="resume.languages"
|
|
:certificates="resume.certificates"
|
|
:awards="resume.awards"
|
|
/>
|
|
</div>
|
|
```
|
|
|
|
**Testing Checklist:**
|
|
- [ ] Education section has "EDUCATION" header (blue, uppercase, with border)
|
|
- [ ] Each degree shows type, field, institution, date range
|
|
- [ ] Date formatting works correctly (right-aligned)
|
|
- [ ] Optional coursework/achievements display if present
|
|
- [ ] Additional Info section has "ADDITIONAL INFORMATION" header (blue, uppercase, with border)
|
|
- [ ] Technical Skills displayed with proper formatting
|
|
- [ ] Languages displayed as comma-separated list
|
|
- [ ] Certifications displayed (if present)
|
|
- [ ] Awards/Activities displayed (if present)
|
|
- [ ] All sections align with design template
|
|
|
|
### References
|
|
|
|
- [Source: docs/architecture.md#Data-Architecture]
|
|
- [Source: docs/architecture.md#Consistency-Rules]
|
|
- [Source: docs/sprint-artifacts/tech-spec-epic-2.md#AC8-Education-Section]
|
|
- [Source: docs/sprint-artifacts/tech-spec-epic-2.md#AC9-Additional-Information-Section]
|
|
- [Source: docs/epics.md#Story-2.4-Create-Resume-Sidebar-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-4-create-resume-sidebar-components.context.xml
|
|
|
|
### Agent Model Used
|
|
|
|
<!-- Will be filled by dev agent -->
|
|
|
|
### Debug Log References
|
|
|
|
<!-- Will be filled by dev agent during implementation -->
|
|
|
|
### Completion Notes List
|
|
|
|
- Created `ResumeEducation.vue` with date formatting and proper styling
|
|
- Created `ResumeAdditionalInfo.vue` with skills, languages, certifications sections
|
|
- Integrated both components into `ResumePreview.vue`
|
|
- All sections use consistent blue uppercase headers with bottom border
|
|
|
|
### File List
|
|
|
|
- app/components/resume/ResumeEducation.vue (created)
|
|
- app/components/resume/ResumeAdditionalInfo.vue (created)
|
|
- app/components/resume/ResumePreview.vue (modified)
|
|
|
|
---
|
|
|
|
**Change Log:**
|
|
- 2025-11-30: Story drafted by SM agent (mahdi)
|
|
- 2025-11-30: **COMPLETELY REVISED** by SM agent (Bob) - Removed sidebar components (Contact, Skills, Languages), moved Education to main body, created consolidated AdditionalInfo component per new architecture and design template
|
|
- 2025-12-01: Implemented by Dev agent (Amelia) - All ACs completed, status: done
|