Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions website/src/pages/webinars.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@
color: #fff !important;
}

.cancelledBadge {
display: inline-block;
font-size: 0.8rem;
font-weight: 600;
color: var(--ifm-color-warning-darkest, #7a5900);
background: var(--ifm-color-warning-lightest, #fff3cd);
border: 1px solid var(--ifm-color-warning-dark, #d4a017);
border-radius: 4px;
padding: 0.35rem 0.9rem;
}

@media (prefers-reduced-motion: reduce) {
.joinBtn {
transition: none;
Expand Down
85 changes: 52 additions & 33 deletions website/src/pages/webinars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface MonthlyAgendaResult {
month?: string;
items: AgendaItem[];
loading: boolean;
cancelled?: boolean;
}

interface EventType {
Expand All @@ -46,7 +47,7 @@ interface EventType {
joinHref: string;
agendaBasePath: string;
agendaMode: "monthly" | "static";
getNextDate: () => Date;
getNextDate: (after?: Date) => Date;
pastRecordingsHref?: string;
}

Expand Down Expand Up @@ -85,14 +86,13 @@ const eventTypes: EventType[] = [
// --- Date Helpers ----------------------------------------------------------

// Returns the upcoming 3rd Wednesday (or today if today is the 3rd Wednesday).
function getNextThirdWednesday(): Date {
// Pass `after` to find the next occurrence on or after that date.
function getNextThirdWednesday(after?: Date): Date {
const PST_OFFSET_MS = 8 * 60 * 60 * 1000;
const nowUTC = Date.now();
const nowPST = new Date(nowUTC - PST_OFFSET_MS);

const year = nowPST.getUTCFullYear();
const month = nowPST.getUTCMonth();
const today = nowPST.getUTCDate();
const referencePST = new Date((after?.getTime() ?? Date.now()) - PST_OFFSET_MS);
const year = referencePST.getUTCFullYear();
const month = referencePST.getUTCMonth();
const day = referencePST.getUTCDate();

const thirdWed = (y: number, m: number): Date => {
const first = new Date(Date.UTC(y, m, 1));
Expand All @@ -101,7 +101,7 @@ function getNextThirdWednesday(): Date {
};

const candidate = thirdWed(year, month);
if (candidate.getUTCDate() >= today && candidate.getUTCMonth() === month) {
if (candidate.getUTCDate() >= day && candidate.getUTCMonth() === month) {
return new Date(candidate.getUTCFullYear(), candidate.getUTCMonth(), candidate.getUTCDate());
}
const nextMonth = month + 1;
Expand All @@ -112,33 +112,34 @@ function getNextThirdWednesday(): Date {
return new Date(result.getUTCFullYear(), result.getUTCMonth(), result.getUTCDate());
}

// Returns the next Architecture Review Hour date.
// Returns the next Kube & Tell date.
// Known upcoming dates: June 11, 2026. After that, assume 2nd Thursday monthly.
function getNextKubeAndTellDate(): Date {
// Pass `after` to find the next occurrence on or after that date.
function getNextKubeAndTellDate(after?: Date): Date {
const PST_OFFSET_MS = 8 * 60 * 60 * 1000;
const nowUTC = Date.now();
const nowPST = new Date(nowUTC - PST_OFFSET_MS);
const todayMs = Date.UTC(nowPST.getUTCFullYear(), nowPST.getUTCMonth(), nowPST.getUTCDate());
const referencePST = new Date((after?.getTime() ?? Date.now()) - PST_OFFSET_MS);
const year = referencePST.getUTCFullYear();
const month = referencePST.getUTCMonth();
const day = referencePST.getUTCDate();
const referenceDayMs = Date.UTC(year, month, day);

// Known fixed date
const knownMs = Date.UTC(2026, 5, 11); // June 11, 2026
if (knownMs >= todayMs) {
if (knownMs >= referenceDayMs) {
return new Date(2026, 5, 11);
}

// Fallback: 2nd Thursday of each month
const year = nowPST.getUTCFullYear();
const month = nowPST.getUTCMonth();
const today = nowPST.getUTCDate();

const secondThursday = (y: number, m: number): Date => {
const first = new Date(Date.UTC(y, m, 1));
const offset = (4 - first.getUTCDay() + 7) % 7;
return new Date(Date.UTC(y, m, 1 + offset + 7));
};

const candidate = secondThursday(year, month);
if (candidate.getUTCDate() >= today && candidate.getUTCMonth() === month) {
const isCurrentMonthCandidate = candidate.getUTCMonth() === month;
const isUpcomingCandidate = candidate.getUTCDate() >= day;
if (isCurrentMonthCandidate && isUpcomingCandidate) {
return new Date(candidate.getUTCFullYear(), candidate.getUTCMonth(), candidate.getUTCDate());
}
const nextMonth = month + 1;
Expand All @@ -162,8 +163,10 @@ function formatDate(d: Date): string {
function parseAgendaMarkdown(raw: string): {
month?: string;
items: AgendaItem[];
cancelled?: boolean;
} {
let month: string | undefined;
let cancelled: boolean | undefined;
let content = raw.trim();
if (content.startsWith("---")) {
const end = content.indexOf("\n---", 3);
Expand All @@ -173,6 +176,8 @@ function parseAgendaMarkdown(raw: string): {
front.split(/\r?\n/).forEach((line) => {
const m = line.match(/^month:\s*(.+)$/i);
if (m) month = m[1].trim();
const c = line.match(/^cancelled:\s*(.+)$/i);
if (c) cancelled = /true/i.test(c[1].trim());
});
}
}
Expand Down Expand Up @@ -217,7 +222,7 @@ function parseAgendaMarkdown(raw: string): {
if (featured) item.featured = true;
return item;
});
return { month, items: finalized };
return { month, items: finalized, cancelled };
}

// Hook to load agenda markdown for current month, falling back to latest prior month.
Expand Down Expand Up @@ -258,6 +263,7 @@ function useMonthlyAgenda(basePath: string): MonthlyAgendaResult {
setState({
items: parsed.items,
month: parsed.month,
cancelled: parsed.cancelled,
loading: false,
});
})
Expand Down Expand Up @@ -309,7 +315,7 @@ function useStaticAgenda(filePath: string): MonthlyAgendaResult {
.then((text) => {
if (cancelled) return;
const parsed = parseAgendaMarkdown(text);
setState({ items: parsed.items, month: parsed.month, loading: false });
setState({ items: parsed.items, month: parsed.month, cancelled: parsed.cancelled, loading: false });
})
.catch(() => {
if (!cancelled) setState({ items: [], loading: false });
Expand Down Expand Up @@ -343,10 +349,14 @@ function Hero(): ReactNode {
function EventSection({ event }: { event: EventType }): ReactNode {
const monthlyResult = useMonthlyAgenda(event.agendaMode === "monthly" ? event.agendaBasePath : "");
const staticResult = useStaticAgenda(event.agendaMode === "static" ? event.agendaBasePath : "");
const { month, items, loading } = event.agendaMode === "monthly" ? monthlyResult : staticResult;
const { month, items, loading, cancelled } = event.agendaMode === "monthly" ? monthlyResult : staticResult;
const label = month || "Latest";
const empty = !loading && items.length === 0;
const nextDate = event.getNextDate();
// When cancelled, advance to the next month's occurrence using the same date logic.
const rawNextDate = event.getNextDate();
const nextDate = cancelled
? event.getNextDate(new Date(rawNextDate.getFullYear(), rawNextDate.getMonth() + 1, 1))
: rawNextDate;

return (
<section className={styles.agendaSection}>
Expand All @@ -372,29 +382,38 @@ function EventSection({ event }: { event: EventType }): ReactNode {
<a href={event.icsHref} className={styles.calendarLink}>
Add to calendar
</a>
<Link
className={`button button--primary button--sm ${styles.joinBtn}`}
to={event.joinHref}
>
Join Now
</Link>
{cancelled ? (
<span className={styles.cancelledBadge}>No call this month</span>
) : (
<Link
className={`button button--primary button--sm ${styles.joinBtn}`}
to={event.joinHref}
>
Join Now
</Link>
)}
</div>
</div>
<div className={styles.sectionHeader}>
<Heading as="h2">
Agenda - {label}
</Heading>
{loading && (
{!cancelled && loading && (
<span className={styles.loadingBadge}>loading&hellip;</span>
)}
</div>
{empty && (
{cancelled && (
<p className={styles.emptyState}>
There is no {event.label} call this month. The next session is listed above.
</p>
)}
{!cancelled && empty && (
<p className={styles.emptyState}>
No agenda has been published yet. Once an agenda file is added it
appears here automatically.
</p>
)}
{!empty && (
{!cancelled && !empty && (
<ul className={styles.agendaList}>
{items.map((item, idx) => (
<li key={idx} className={styles.agendaListItem}>
Expand Down
22 changes: 22 additions & 0 deletions website/static/webinars/agenda/2026-07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
month: July 2026
cancelled: false
---

# Agenda

## Welcome & Announcements

## Community Content Showcase

## Feature Deepdive: Safe multi-cluster upgrades at a glance
Presenter: Yoo Bin Shin, Software Eng
Comment thread
sanketbakshi1981 marked this conversation as resolved.

## Feature Deepdive: AKS on BareMetal
Presenter: Rishi Mody, Senior PM (AKS)

## Feature Roadmap

## Q&A Session

Description: Open discussion with the AKS team
15 changes: 4 additions & 11 deletions website/static/webinars/agenda/kube-and-tell.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
---
month: June 2026
month: July 2026
cancelled: true
---

# Agenda

## Architecture Review Hour
Description: Platform Management at a large enterprise
## No Call for July 2026
Description: Summer Break

## Introduction
Presenter: Paul Yu, Host

## Architecture Review: Platform management at a large enterprise
Description: Architecture Walkthrough, Review & Discussion. If we were doing it again, how would we do it?

## Live Q&A
Description: Open discussion with the AKS architecture team
Loading