Skip to content

Commit 7a3278e

Browse files
Mattclaude
andcommitted
Add opening/closing address slot types and fix session track mapping
- Add opening_address and closing_address slot types - Hide speaker photo/name for slots without speakers - Hide view details button for opening/closing addresses - Fix track_id to read from session level (not just slot level) - Handle room_change slots as breaks - Handle slots with title but no sessions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 41f996e commit 7a3278e

3 files changed

Lines changed: 65 additions & 32 deletions

File tree

src/lib/components/Tag.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
<script lang="ts">
22
interface Props {
3-
type: 'keynote' | 'talk' | 'tutorial';
3+
type: 'keynote' | 'talk' | 'tutorial' | 'opening' | 'closing';
44
}
55
66
let { type }: Props = $props();
77
88
const labels: Record<string, string> = {
99
keynote: 'Keynote',
1010
talk: 'Talk',
11-
tutorial: 'Tutorial'
11+
tutorial: 'Tutorial',
12+
opening: 'Opening',
13+
closing: 'Closing'
1214
};
1315
</script>
1416

@@ -39,4 +41,9 @@
3941
.tag--tutorial {
4042
background: var(--color-tag-tutorial);
4143
}
44+
45+
.tag--opening,
46+
.tag--closing {
47+
background: var(--color-tag-keynote);
48+
}
4249
</style>

src/lib/components/TalkCard.svelte

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
speaker: string;
1717
speakerPhoto: string;
1818
synopsis: string;
19-
tag: 'keynote' | 'talk' | 'tutorial';
19+
tag: 'keynote' | 'talk' | 'tutorial' | 'opening' | 'closing';
2020
social: Social;
2121
}
2222
@@ -55,12 +55,14 @@
5555
</span>
5656
{/if}
5757
</div>
58-
<img
59-
src={talk.speakerPhoto}
60-
alt=""
61-
class="speaker-photo"
62-
loading="lazy"
63-
/>
58+
{#if talk.speakerPhoto}
59+
<img
60+
src={talk.speakerPhoto}
61+
alt=""
62+
class="speaker-photo"
63+
loading="lazy"
64+
/>
65+
{/if}
6466
</div>
6567

6668
<div class="talk-content">
@@ -72,30 +74,34 @@
7274
{/if}
7375
</div>
7476
<h3 class="talk-title">{talk.title}</h3>
75-
<p class="speaker-name">{talk.speaker}</p>
77+
{#if talk.speaker}
78+
<p class="speaker-name">{talk.speaker}</p>
79+
{/if}
7680
</div>
7781

78-
<button
79-
class="synopsis-toggle"
80-
onclick={toggleExpanded}
81-
aria-expanded={expanded}
82-
aria-controls="synopsis-{talk.id}"
83-
>
84-
{expanded ? 'Hide details' : 'View details'}
85-
<svg
86-
class="chevron"
87-
class:rotated={expanded}
88-
width="16"
89-
height="16"
90-
viewBox="0 0 16 16"
91-
fill="none"
92-
aria-hidden="true"
82+
{#if talk.tag !== 'opening' && talk.tag !== 'closing'}
83+
<button
84+
class="synopsis-toggle"
85+
onclick={toggleExpanded}
86+
aria-expanded={expanded}
87+
aria-controls="synopsis-{talk.id}"
9388
>
94-
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
95-
</svg>
96-
</button>
89+
{expanded ? 'Hide details' : 'View details'}
90+
<svg
91+
class="chevron"
92+
class:rotated={expanded}
93+
width="16"
94+
height="16"
95+
viewBox="0 0 16 16"
96+
fill="none"
97+
aria-hidden="true"
98+
>
99+
<path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
100+
</svg>
101+
</button>
102+
{/if}
97103

98-
{#if expanded}
104+
{#if expanded && talk.tag !== 'opening' && talk.tag !== 'closing'}
99105
<div id="synopsis-{talk.id}" class="synopsis">
100106
<p>{talk.synopsis}</p>
101107

src/routes/schedule/+page.server.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface ApiSpeaker {
3939

4040
interface ApiSession {
4141
id: string;
42+
track_id: string | null;
4243
title: string | null;
4344
description: string | null;
4445
speaker: ApiSpeaker;
@@ -66,9 +67,11 @@ function formatTime(time: string): string {
6667
return time.slice(0, 5);
6768
}
6869

69-
function mapSlotTypeToTag(slotType: string): 'keynote' | 'talk' | 'tutorial' {
70+
function mapSlotTypeToTag(slotType: string): 'keynote' | 'talk' | 'tutorial' | 'opening' | 'closing' {
7071
if (slotType === 'keynote') return 'keynote';
7172
if (slotType === 'tutorial') return 'tutorial';
73+
if (slotType === 'opening_address') return 'opening';
74+
if (slotType === 'closing_address') return 'closing';
7275
return 'talk';
7376
}
7477

@@ -82,7 +85,7 @@ function transformApiData(slots: ApiSlot[], apiTracks: ApiTrack[]) {
8285
const time = formatTime(slot.start_time);
8386
const duration = calculateDuration(slot.start_time, slot.end_time);
8487

85-
if (slot.is_break) {
88+
if (slot.is_break || slot.slot_type === 'room_change') {
8689
transformedBreaks.push({
8790
time,
8891
duration,
@@ -94,7 +97,9 @@ function transformApiData(slots: ApiSlot[], apiTracks: ApiTrack[]) {
9497
const session = slot.sessions[i];
9598
let trackId: string;
9699

97-
if (slot.track_id) {
100+
if (session.track_id) {
101+
trackId = session.track_id;
102+
} else if (slot.track_id) {
98103
trackId = slot.track_id;
99104
} else if (slot.sessions.length > 1 && mainTracks.length > 0) {
100105
trackId = mainTracks[i % mainTracks.length]?.id || mainTracks[0].id;
@@ -121,6 +126,21 @@ function transformApiData(slots: ApiSlot[], apiTracks: ApiTrack[]) {
121126
social: {}
122127
});
123128
}
129+
} else if (slot.title) {
130+
// Slot with title but no sessions (e.g., Closing Address)
131+
const trackId = slot.track_id || mainTracks[0]?.id || apiTracks[0]?.id || '';
132+
transformedTalks.push({
133+
id: slot.id,
134+
track: trackId,
135+
time,
136+
duration,
137+
title: slot.title,
138+
speaker: '',
139+
speakerPhoto: '',
140+
synopsis: '',
141+
tag: mapSlotTypeToTag(slot.slot_type),
142+
social: {}
143+
});
124144
}
125145
}
126146

0 commit comments

Comments
 (0)