240 lines
4.7 KiB
Vue
240 lines
4.7 KiB
Vue
|
|
<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?.basics?.name }}</h1>
|
||
|
|
<h3>Works List with Presentation History</h3>
|
||
|
|
<p class="contact">
|
||
|
|
{{ resume?.basics?.email }} · {{ resume?.basics?.phone }} · {{ resume?.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" 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 {
|
||
|
|
@page {
|
||
|
|
margin: 15mm;
|
||
|
|
}
|
||
|
|
|
||
|
|
.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;
|
||
|
|
break-after: avoid;
|
||
|
|
page-break-after: avoid;
|
||
|
|
}
|
||
|
|
|
||
|
|
.work-entry {
|
||
|
|
break-inside: avoid;
|
||
|
|
page-break-inside: avoid;
|
||
|
|
}
|
||
|
|
|
||
|
|
.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>
|