feature: display end times
This commit is contained in:
parent
5ee3d48434
commit
1ac42e51db
11 changed files with 26067 additions and 2 deletions
|
|
@ -114,6 +114,7 @@ export default async function EventsOverviewPage({
|
||||||
<EventRow
|
<EventRow
|
||||||
key={e.id}
|
key={e.id}
|
||||||
date={e.date}
|
date={e.date}
|
||||||
|
end={e.end}
|
||||||
title={e.title}
|
title={e.title}
|
||||||
href={e.href}
|
href={e.href}
|
||||||
location={e.location}
|
location={e.location}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ export default async function EventsPage({searchParams}: {
|
||||||
<EventRow
|
<EventRow
|
||||||
key={e.id}
|
key={e.id}
|
||||||
date={e.date}
|
date={e.date}
|
||||||
|
end={e.end}
|
||||||
title={e.title}
|
title={e.title}
|
||||||
href={e.href}
|
href={e.href}
|
||||||
location={e.location}
|
location={e.location}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,21 @@ export const EventOccurrences: CollectionConfig = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'endDateTime',
|
||||||
|
type: 'date',
|
||||||
|
index: true,
|
||||||
|
label: {
|
||||||
|
de: 'Enduhrzeit',
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayAndTime',
|
||||||
|
timeIntervals: 15,
|
||||||
|
timeFormat: 'HH:mm',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'cancelled',
|
name: 'cancelled',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export const Events = ({ events, n, schema = 'base' }: EventsProps) => {
|
||||||
<EventRow
|
<EventRow
|
||||||
key={event.href}
|
key={event.href}
|
||||||
date={event.date}
|
date={event.date}
|
||||||
|
end={event.end}
|
||||||
title={event.title}
|
title={event.title}
|
||||||
href={event.href}
|
href={event.href}
|
||||||
location={event.location}
|
location={event.location}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ type GenerateEventOccurrencesOutput = {
|
||||||
type EventLike = {
|
type EventLike = {
|
||||||
id: string
|
id: string
|
||||||
date?: string | null
|
date?: string | null
|
||||||
|
endDateTime?: string | null
|
||||||
recurrenceType?: EventRecurrenceType | null
|
recurrenceType?: EventRecurrenceType | null
|
||||||
recurrenceRules?: RecurrenceRule[] | null
|
recurrenceRules?: RecurrenceRule[] | null
|
||||||
endDate?: string | null
|
endDate?: string | null
|
||||||
|
|
@ -113,6 +114,17 @@ export const regenerateOccurrencesForEvent = async ({
|
||||||
return { created, deleted, skipped: skipped + 1 }
|
return { created, deleted, skipped: skipped + 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Derive each occurrence's end from the event's duration (end - start),
|
||||||
|
// re-anchored to the occurrence's own start. A missing or non-positive
|
||||||
|
// duration yields no end on the occurrences.
|
||||||
|
const eventEnd = event.endDateTime ? new Date(event.endDateTime) : null
|
||||||
|
const durationMs =
|
||||||
|
eventEnd && !Number.isNaN(eventEnd.getTime()) && eventEnd.getTime() > eventDate.getTime()
|
||||||
|
? eventEnd.getTime() - eventDate.getTime()
|
||||||
|
: null
|
||||||
|
const endFor = (start: Date): string | null =>
|
||||||
|
durationMs === null ? null : new Date(start.getTime() + durationMs).toISOString()
|
||||||
|
|
||||||
const recurrenceType = event.recurrenceType ?? 'none'
|
const recurrenceType = event.recurrenceType ?? 'none'
|
||||||
|
|
||||||
if (recurrenceType === 'none') {
|
if (recurrenceType === 'none') {
|
||||||
|
|
@ -124,6 +136,7 @@ export const regenerateOccurrencesForEvent = async ({
|
||||||
data: {
|
data: {
|
||||||
event: event.id,
|
event: event.id,
|
||||||
date: eventDate.toISOString(),
|
date: eventDate.toISOString(),
|
||||||
|
endDateTime: endFor(eventDate),
|
||||||
cancelled: cancelledDates.has(eventDate.toISOString()),
|
cancelled: cancelledDates.has(eventDate.toISOString()),
|
||||||
generated: true,
|
generated: true,
|
||||||
},
|
},
|
||||||
|
|
@ -144,6 +157,7 @@ export const regenerateOccurrencesForEvent = async ({
|
||||||
data: {
|
data: {
|
||||||
event: event.id,
|
event: event.id,
|
||||||
date: iso,
|
date: iso,
|
||||||
|
endDateTime: endFor(date),
|
||||||
cancelled: cancelledDates.has(iso),
|
cancelled: cancelledDates.has(iso),
|
||||||
generated: true,
|
generated: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
26001
src/migrations/20260605_113107_occurrence_end_time.json
Normal file
26001
src/migrations/20260605_113107_occurrence_end_time.json
Normal file
File diff suppressed because it is too large
Load diff
19
src/migrations/20260605_113107_occurrence_end_time.ts
Normal file
19
src/migrations/20260605_113107_occurrence_end_time.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-06-07T11:31:06.259Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-06-07T11:31:06.641Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-07-05T11:31:06.720Z';
|
||||||
|
ALTER TABLE "event_occurrence" ADD COLUMN "end_date_time" timestamp(3) with time zone;
|
||||||
|
CREATE INDEX "event_occurrence_end_date_time_idx" ON "event_occurrence" USING btree ("end_date_time");`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
DROP INDEX "event_occurrence_end_date_time_idx";
|
||||||
|
ALTER TABLE "announcement" ALTER COLUMN "date" SET DEFAULT '2026-06-07T09:51:11.853Z';
|
||||||
|
ALTER TABLE "calendar" ALTER COLUMN "date" SET DEFAULT '2026-06-07T09:51:12.181Z';
|
||||||
|
ALTER TABLE "classifieds" ALTER COLUMN "until" SET DEFAULT '2026-07-05T09:51:12.265Z';
|
||||||
|
ALTER TABLE "event_occurrence" DROP COLUMN "end_date_time";`)
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,7 @@ import * as migration_20260423_131226_add_event_end_date_time from './20260423_1
|
||||||
import * as migration_20260429_121105_collapsible_map_with_text from './20260429_121105_collapsible_map_with_text';
|
import * as migration_20260429_121105_collapsible_map_with_text from './20260429_121105_collapsible_map_with_text';
|
||||||
import * as migration_20260504_070356_next_prev_buttons from './20260504_070356_next_prev_buttons';
|
import * as migration_20260504_070356_next_prev_buttons from './20260504_070356_next_prev_buttons';
|
||||||
import * as migration_20260605_095112 from './20260605_095112';
|
import * as migration_20260605_095112 from './20260605_095112';
|
||||||
|
import * as migration_20260605_113107_occurrence_end_time from './20260605_113107_occurrence_end_time';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
|
|
@ -280,6 +281,11 @@ export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20260605_095112.up,
|
up: migration_20260605_095112.up,
|
||||||
down: migration_20260605_095112.down,
|
down: migration_20260605_095112.down,
|
||||||
name: '20260605_095112'
|
name: '20260605_095112',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260605_113107_occurrence_end_time.up,
|
||||||
|
down: migration_20260605_113107_occurrence_end_time.down,
|
||||||
|
name: '20260605_113107_occurrence_end_time'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import classNames from 'classnames'
|
||||||
type UpcomingOccurrence = {
|
type UpcomingOccurrence = {
|
||||||
id: string
|
id: string
|
||||||
date: string
|
date: string
|
||||||
|
end?: string
|
||||||
title: string
|
title: string
|
||||||
href: string
|
href: string
|
||||||
location: string
|
location: string
|
||||||
|
|
@ -204,6 +205,7 @@ export function EventPage(
|
||||||
<EventRow
|
<EventRow
|
||||||
key={o.id}
|
key={o.id}
|
||||||
date={o.date}
|
date={o.date}
|
||||||
|
end={o.end}
|
||||||
title={o.title}
|
title={o.title}
|
||||||
href={o.href}
|
href={o.href}
|
||||||
location={o.location}
|
location={o.location}
|
||||||
|
|
|
||||||
|
|
@ -1180,6 +1180,7 @@ export interface EventOccurrence {
|
||||||
id: string;
|
id: string;
|
||||||
event: string | Event;
|
event: string | Event;
|
||||||
date: string;
|
date: string;
|
||||||
|
endDateTime?: string | null;
|
||||||
cancelled: boolean;
|
cancelled: boolean;
|
||||||
generated?: boolean | null;
|
generated?: boolean | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|
@ -1932,6 +1933,7 @@ export interface EventSelect<T extends boolean = true> {
|
||||||
export interface EventOccurrenceSelect<T extends boolean = true> {
|
export interface EventOccurrenceSelect<T extends boolean = true> {
|
||||||
event?: T;
|
event?: T;
|
||||||
date?: T;
|
date?: T;
|
||||||
|
endDateTime?: T;
|
||||||
cancelled?: T;
|
cancelled?: T;
|
||||||
generated?: T;
|
generated?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ export const eventToPageProps = (
|
||||||
type EventRowItem = {
|
type EventRowItem = {
|
||||||
id: string
|
id: string
|
||||||
date: string
|
date: string
|
||||||
|
end?: string
|
||||||
title: string
|
title: string
|
||||||
href: string
|
href: string
|
||||||
location: string
|
location: string
|
||||||
|
|
@ -51,13 +52,14 @@ export const transformOccurrences = (
|
||||||
occurrences: EventOccurrence[],
|
occurrences: EventOccurrence[],
|
||||||
): EventRowItem[] => {
|
): EventRowItem[] => {
|
||||||
return occurrences
|
return occurrences
|
||||||
.map((o) => {
|
.map((o): EventRowItem | undefined => {
|
||||||
const event = typeof o.event === 'object' ? o.event : undefined
|
const event = typeof o.event === 'object' ? o.event : undefined
|
||||||
if (!event) return undefined
|
if (!event) return undefined
|
||||||
return {
|
return {
|
||||||
id: o.id,
|
id: o.id,
|
||||||
title: event.title,
|
title: event.title,
|
||||||
date: o.date,
|
date: o.date,
|
||||||
|
end: o.endDateTime ?? undefined,
|
||||||
href: `/veranstaltungen/${event.id}/${o.id}`,
|
href: `/veranstaltungen/${event.id}/${o.id}`,
|
||||||
location: typeof event.location === 'object' ? event.location.name : 'Unbekannt',
|
location: typeof event.location === 'object' ? event.location.name : 'Unbekannt',
|
||||||
cancelled: Boolean(event.cancelled || o.cancelled),
|
cancelled: Boolean(event.cancelled || o.cancelled),
|
||||||
|
|
@ -75,6 +77,7 @@ export const transformEvents = (events: Event[]): EventRowItem[] => {
|
||||||
id: e.id,
|
id: e.id,
|
||||||
title: e.title,
|
title: e.title,
|
||||||
date: e.date,
|
date: e.date,
|
||||||
|
end: e.endDateTime ?? undefined,
|
||||||
href: `/veranstaltungen/${e.id}`,
|
href: `/veranstaltungen/${e.id}`,
|
||||||
location: typeof e.location === 'object' ? e.location.name : 'Unbekannt',
|
location: typeof e.location === 'object' ? e.location.name : 'Unbekannt',
|
||||||
cancelled: e.cancelled,
|
cancelled: e.cancelled,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue