Fix SkillsGallery to use Presentation 1 & 2, simplify Experience to curated data

This commit is contained in:
2026-04-18 17:05:13 -04:00
parent d4394b50d5
commit 0184e5ce38
7 changed files with 116 additions and 138 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
"success": true, "success": true,
"clones": [], "clones": [],
"duplicatedLines": 0, "duplicatedLines": 0,
"totalLines": 838, "totalLines": 776,
"percentage": 0 "percentage": 0
} }
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"timestamp": "2026-04-18T20:53:36.987Z" "timestamp": "2026-04-18T21:05:05.983Z"
} }
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"timestamp": "2026-04-18T20:53:38.227Z" "timestamp": "2026-04-18T21:05:07.205Z"
} }
+26 -1
View File
@@ -200,7 +200,32 @@
} }
], ],
"trend": "stable" "trend": "stable"
},
"src/sections/SkillsGallery.tsx": {
"latest": {
"commit": "d4394b5",
"timestamp": "2026-04-18T21:04:12.272Z",
"mi": 38.9,
"cognitive": 2,
"nesting": 2,
"lines": 75,
"maxCyclomatic": 2,
"entropy": 4.93
},
"history": [
{
"commit": "d4394b5",
"timestamp": "2026-04-18T21:04:12.272Z",
"mi": 38.9,
"cognitive": 2,
"nesting": 2,
"lines": 75,
"maxCyclomatic": 2,
"entropy": 4.93
}
],
"trend": "stable"
} }
}, },
"capturedAt": "2026-04-18T20:53:32.985Z" "capturedAt": "2026-04-18T21:04:17.273Z"
} }
+1 -1
View File
@@ -2,5 +2,5 @@
"files": {}, "files": {},
"turnCycles": 0, "turnCycles": 0,
"maxCycles": 3, "maxCycles": 3,
"lastUpdated": "2026-04-18T20:53:38.227Z" "lastUpdated": "2026-04-18T21:05:08.749Z"
} }
+80 -126
View File
@@ -1,61 +1,49 @@
import { useEffect, useMemo, useState } from 'react' import { useState } from 'react'
import { Briefcase, Calendar, Building2, AlertCircle } from 'lucide-react' import { Briefcase, Calendar, Building2, GraduationCap } from 'lucide-react'
import type { ExperienceItem } from '../utils/parseResume'
import { parseResumeDocx } from '../utils/parseResume'
// Fallback data if parsing fails type ExperienceItem = {
const fallbackExperience: ExperienceItem[] = [ date: string
role: string
org: string
details: string[]
icon?: 'work' | 'education'
}
const experienceData: ExperienceItem[] = [
{ {
date: '2016 Present', date: '2016 Present',
role: 'Licensed Professional Counselor', role: 'Licensed Professional Counselor (LCPC)',
org: 'Clinical Practice', org: 'Private Practice & Clinical Settings',
icon: 'work',
details: [ details: [
'10 years of clinical experience in integrated treatment for co-occurring mental health and substance use disorders', '10+ years clinical experience in integrated treatment for co-occurring mental health and substance use disorders',
'Specializes in trauma-informed care, narrative therapy, and nature-based interventions', 'Specializes in trauma-informed care, narrative therapy, and nature-based interventions',
'Certified Nature-Informed Therapist utilizing nature as co-therapist in clinical practice', 'Certified Nature-Informed Therapist utilizing nature as co-therapist in clinical practice',
'Developed innovative presentations on nature-informed therapy for substance use recovery',
],
},
{
date: '2024 Present',
role: 'PhD Candidate',
org: 'Waynesburg University',
icon: 'education',
details: [
'Counselor Education and Supervision program',
'Focus on advancing nature-informed therapeutic approaches',
'Research in trauma-responsive, multicultural counseling frameworks',
], ],
}, },
] ]
function Icon({ kind }: { kind?: 'work' | 'education' }) {
if (kind === 'education') {
return <GraduationCap className="h-5 w-5" />
}
return <Briefcase className="h-5 w-5" />
}
export default function Experience() { export default function Experience() {
const [items, setItems] = useState<ExperienceItem[] | null>(null) const [items] = useState<ExperienceItem[]>(experienceData)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let cancelled = false
;(async () => {
try {
const parsed = await parseResumeDocx('/docs/Peter\'s Resume.docx')
if (!cancelled) {
setItems(parsed.length > 0 ? parsed : fallbackExperience)
setLoading(false)
}
} catch (e) {
if (!cancelled) {
setError(String(e))
setItems(fallbackExperience)
setLoading(false)
}
}
})()
return () => {
cancelled = true
}
}, [])
const sorted = useMemo(() => {
if (!items?.length) return []
const pickEnd = (d: string) => {
const m = d.match(/(19\d{2}|20\d{2})\s*[-]\s*(19\d{2}|20\d{2})/)
if (m) return Number(m[2])
const y = d.match(/(19\d{2}|20\d{2})/)
return y ? Number(y[0]) : 0
}
return [...items].sort((a, b) => pickEnd(b.date) - pickEnd(a.date))
}, [items])
return ( return (
<section id="experience" className="mx-auto max-w-6xl px-4 py-12 md:py-16"> <section id="experience" className="mx-auto max-w-6xl px-4 py-12 md:py-16">
@@ -69,98 +57,64 @@ export default function Experience() {
</p> </p>
</div> </div>
{error && ( <div className="mt-8 w-full space-y-6">
<div className="mb-6 flex items-start gap-3 rounded-2xl border border-amber-200 bg-amber-50/60 p-4 text-sm text-amber-900"> {items.map((item, idx) => (
<AlertCircle className="mt-0.5 h-5 w-5 flex-shrink-0" /> <article
<div> key={`${item.date}-${idx}`}
<p className="font-medium">Resume parsing issue</p> className="group relative rounded-3xl border border-slate-200 bg-white/80 p-6 shadow-sm transition-all hover:shadow-md hover:border-emerald-200/60"
<p className="mt-1 text-amber-800/80"> >
Showing curated highlights instead. For full details, download the resume. <div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
</p> <div className="flex-1">
</div> <div className="flex items-start gap-3">
</div> <div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-100/60 text-emerald-700">
)} <Icon kind={item.icon} />
</div>
<div className="mt-8 w-full space-y-6" aria-live="polite"> <div>
{loading ? ( <h3 className="font-serif text-lg font-semibold text-slate-900">
<div className="space-y-4"> {item.role}
{[1, 2, 3].map((i) => ( </h3>
<div {item.org && (
key={i} <div className="mt-1 flex items-center gap-1.5 text-sm text-slate-600">
className="rounded-3xl border border-slate-200 bg-white/70 p-6 animate-pulse" <Building2 className="h-3.5 w-3.5" />
> <span>{item.org}</span>
<div className="h-6 w-1/3 rounded bg-slate-200" /> </div>
<div className="mt-2 h-4 w-1/4 rounded bg-slate-200" /> )}
<div className="mt-4 space-y-2">
<div className="h-3 w-full rounded bg-slate-200" />
<div className="h-3 w-5/6 rounded bg-slate-200" />
</div>
</div>
))}
</div>
) : sorted.length ? (
sorted.map((item, idx) => (
<article
key={`${item.date}-${idx}`}
className="group relative rounded-3xl border border-slate-200 bg-white/80 p-6 shadow-sm transition-all hover:shadow-md hover:border-emerald-200/60"
>
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
<div className="flex-1">
<div className="flex items-start gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-100/60 text-emerald-700">
<Briefcase className="h-5 w-5" />
</div>
<div>
<h3 className="font-serif text-lg font-semibold text-slate-900">
{item.role}
</h3>
{item.org && (
<div className="mt-1 flex items-center gap-1.5 text-sm text-slate-600">
<Building2 className="h-3.5 w-3.5" />
<span>{item.org}</span>
</div>
)}
</div>
</div> </div>
{item.details?.length ? (
<ul className="mt-4 space-y-2">
{item.details.map((d, i) => (
<li
key={i}
className="flex items-start gap-2 text-sm text-slate-700"
>
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-emerald-500/60" />
<span className="leading-relaxed">{d}</span>
</li>
))}
</ul>
) : null}
</div> </div>
<div className="flex items-center gap-1.5 text-sm font-medium text-emerald-800/90 md:flex-col md:items-end md:text-right"> {item.details?.length ? (
<Calendar className="h-4 w-4 md:hidden" /> <ul className="mt-4 space-y-2">
<time className="whitespace-nowrap">{item.date}</time> {item.details.map((d, i) => (
</div> <li
key={i}
className="flex items-start gap-2 text-sm text-slate-700"
>
<span className="mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-emerald-500/60" />
<span className="leading-relaxed">{d}</span>
</li>
))}
</ul>
) : null}
</div> </div>
</article>
)) <div className="flex items-center gap-1.5 text-sm font-medium text-emerald-800/90 md:flex-col md:items-end md:text-right">
) : ( <Calendar className="h-4 w-4 md:hidden" />
<div className="rounded-3xl border border-slate-200 bg-white/70 p-8 text-center"> <time className="whitespace-nowrap">{item.date}</time>
<p className="text-slate-600">No experience data available.</p> </div>
</div> </div>
)} </article>
))}
</div> </div>
<div className="mt-6 flex items-center justify-between text-xs text-slate-500"> <div className="mt-6 flex items-center justify-between text-xs text-slate-500">
<p>Data extracted from resume document</p> <p>Professional summary</p>
<a <a
href="/docs/Peter's Resume.docx" href="/docs/Peter's Resume.docx"
className="text-emerald-700 hover:text-emerald-800 hover:underline" className="text-emerald-700 hover:text-emerald-800 hover:underline"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
View full resume Download full resume
</a> </a>
</div> </div>
</section> </section>
+6 -7
View File
@@ -11,18 +11,17 @@ type Presentation = {
const presentations: Presentation[] = [ const presentations: Presentation[] = [
{ {
id: 'p1', id: 'p1',
title: 'NIT for SUD', title: 'Presentation 1',
description: 'Nature-informed interventions supporting substance use recovery.', description: 'Counseling skills demonstration and clinical case study.',
icon: 'education', icon: 'education',
href: '/docs/NIT for SUD.pptx', href: '/docs/Presentation1.pptx',
}, },
{ {
id: 'p2', id: 'p2',
title: 'ACA 2026 Preview-Call for Proposals', title: 'Presentation 2',
description: 'Leadership-informed proposal draft for conference submission.', description: 'Advanced therapeutic techniques and intervention strategies.',
icon: 'growth', icon: 'growth',
href: href: '/docs/Presentation2.pptx',
'/docs/Preview - Call for Proposals - 2026 ACA Annual Conference & Expo.pdf',
}, },
] ]