portfolio/pages/works_list.vue

229 lines
4.6 KiB
Vue
Raw Normal View History

<script setup>
definePageMeta({
layout: 'plain'
})
const { data: resume } = await useFetch('/api/resume')
const { data: works } = await useFetch('/api/works')
const { data: events } = await useFetch('/api/events')
const worksByYear = computed(() => {
if (!works.value) return []
const grouped = {}
for (const work of works.value) {
const year = work.date ? new Date(work.date).getFullYear() : 'Unknown'
if (!grouped[year]) {
grouped[year] = []
}
const workEvents = events.value?.filter(e => {
if (!e.program) return false
return e.program.some(p => p.work?.toLowerCase().includes(work.title.toLowerCase()))
}) || []
grouped[year].push({
...work,
location: work.instrument_tags?.[0] || '',
events: workEvents
})
}
return Object.keys(grouped)
.sort((a, b) => b - a)
.map(year => ({
year,
works: grouped[year].sort((a, b) => new Date(b.date) - new Date(a.date))
}))
})
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
if (isNaN(date)) return dateStr
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
}
useHead({
titleTemplate: 'Michael Winter'
})
</script>
<template>
<div class="cv-container">
<header class="cv-header">
<h1>{{ resume?.[0]?.basics?.name }}</h1>
<h3>Works List with Presentation History</h3>
<p class="contact">
{{ resume?.[0]?.basics?.email }} &nbsp;·&nbsp; {{ resume?.[0]?.basics?.phone }} &nbsp;·&nbsp; {{ resume?.[0]?.basics?.website }}
</p>
</header>
<hr />
<p class="intro">
A chronological performance / exhibition history, scores, and recordings are available at<br>
www.unboundedpress.org.<br>
All scores are also published or forthcoming through Frog Peak at<br>
www.frogpeak.org/fpartists/fpwinter.html.
</p>
<!-- Works by Year -->
<section v-for="yearGroup in worksByYear" :key="yearGroup.year" class="cv-section">
<h4>{{ yearGroup.year }}</h4>
<div v-for="work in yearGroup.works" :key="work.id" class="work-entry">
<div class="work-title"><em>{{ work.title }}</em></div>
<div class="work-info" v-if="work.instrument_tags">
<span v-for="(tag, idx) in work.instrument_tags" :key="tag">
{{ tag }}{{ idx < work.instrument_tags.length - 1 ? ', ' : '' }}
</span>
</div>
<div class="work-events" v-if="work.events?.length">
<div v-for="event in work.events" :key="event._id?.$oid || event.id" class="event">
{{ event.venue?.name }}; {{ event.venue?.city }}, {{ event.venue?.state }} {{ formatDate(event.start_date) }}
</div>
</div>
</div>
</section>
</div>
</template>
<style>
.cv-container {
font-size: 12px;
width: 175mm;
margin: 40px auto;
max-width: 100%;
padding: 0 30px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.5;
color: #222;
}
.cv-header {
text-align: center;
margin-bottom: 24px;
}
.cv-header h1 {
font-size: 28px;
font-weight: 600;
margin: 0 0 4px 0;
letter-spacing: -0.5px;
}
.cv-header h3 {
font-size: 16px;
font-weight: 400;
margin: 0 0 8px 0;
color: #555;
}
.cv-header .contact {
font-size: 11px;
color: #555;
margin: 0;
}
.cv-section {
margin-bottom: 20px;
}
.cv-section h4 {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
margin: 0 0 10px 0;
padding-bottom: 4px;
border-bottom: 1px solid #ccc;
color: #222;
}
.intro {
font-size: 11px;
color: #444;
margin-bottom: 20px;
line-height: 1.5;
}
.work-entry {
margin-bottom: 10px;
margin-left: 12px;
}
.work-title {
font-size: 12px;
margin-bottom: 2px;
}
.work-info {
font-size: 11px;
color: #444;
margin-bottom: 2px;
}
.work-events {
font-size: 11px;
color: #555;
}
.event {
padding-left: 12px;
margin-top: 2px;
}
hr {
margin: 16px 0;
border: none;
border-top: 1px solid #ccc;
}
@media print {
.cv-container {
margin: 0;
padding: 15mm;
width: auto;
font-size: 10pt;
max-width: none;
box-sizing: border-box;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.cv-header h1 {
font-size: 20pt;
}
.cv-header h3 {
font-size: 12pt;
}
.cv-section h4 {
font-size: 10pt;
border-bottom: 1pt solid #999;
}
.work-title {
font-size: 10pt;
}
.work-info,
.work-events {
font-size: 9pt;
}
hr {
border-top: 1pt solid #999;
}
.work-entry,
.cv-section {
margin-left: 0;
}
}
</style>