Compare commits

..

2 Commits

Author SHA1 Message Date
45bef480d7 fix(deps): update nextjs monorepo to v16
Some checks failed
Check / Lint and Check (push) Failing after 18s
Check / Lint and Check (pull_request) Failing after 16s
2025-10-28 19:00:46 +00:00
4b93109582 add block duration and test
All checks were successful
Check / Lint and Check (push) Successful in 33s
2025-10-27 12:53:56 +01:00
5 changed files with 111 additions and 64 deletions

View File

@@ -1,7 +1,8 @@
{ {
"eventDates": ["2025-09-05", "2025-10-25", "2025-12-06", "1999-01-01"], "eventDates": ["2025-09-05", "2025-10-25", "2025-12-06", "1999-01-01"],
"cutoffTime": "23:00", "cutoffTime": "23:00",
"blockDurationHours": 6,
"message": "Sign-ups are closed for today's event. Please come back tomorrow.", "message": "Sign-ups are closed for today's event. Please come back tomorrow.",
"internalComment": "Add event dates in YYYY-MM-DD format. Signups will be disabled after 11pm (23:00) on these dates by default." "internalComment": "Add event dates in YYYY-MM-DD format. Signups will be disabled after cutoffTime (23:00) for blockDurationHours (6 hours)."
} }

View File

@@ -0,0 +1,40 @@
import { isSignupBlocked } from "./signup-time-check";
// Test helper to check different scenarios
function testScenario(description: string, date: Date, expected: boolean) {
const result = isSignupBlocked(date);
const status = result.blocked === expected ? "✅ PASS" : "❌ FAIL";
console.log(`${status} ${description}`);
console.log(` Expected blocked: ${expected}, Got: ${result.blocked}`);
if (result.message) {
console.log(` Message: ${result.message}`);
}
console.log();
}
// Run tests
console.log("Testing signup blocking logic...\n");
// October 25, 2025 at 22:00 (before cutoff)
testScenario("Oct 25 at 22:00 - Before cutoff", new Date("2025-10-25T22:00:00"), false);
// October 25, 2025 at 23:00 (exactly at cutoff)
testScenario("Oct 25 at 23:00 - At cutoff time", new Date("2025-10-25T23:00:00"), true);
// October 25, 2025 at 23:30 (during block period)
testScenario("Oct 25 at 23:30 - During block period", new Date("2025-10-25T23:30:00"), true);
// October 26, 2025 at 02:00 (3 hours into block)
testScenario("Oct 26 at 02:00 - 3 hours into block", new Date("2025-10-26T02:00:00"), true);
// October 26, 2025 at 04:59 (just before block ends)
testScenario("Oct 26 at 04:59:59 - Just before block ends", new Date("2025-10-26T04:59:59"), true);
// October 26, 2025 at 05:00 (block period over)
testScenario("Oct 26 at 05:00 - Block period ended", new Date("2025-10-26T05:00:00"), false);
// October 26, 2025 at 12:00 (well after block)
testScenario("Oct 26 at 12:00 - Well after block", new Date("2025-10-26T12:00:00"), false);
// Random non-event date
testScenario("Oct 20 at 23:00 - Non-event date", new Date("2025-10-20T23:00:00"), false);

View File

@@ -1,17 +1,23 @@
import eventConfig from "@/event-dates.json"; import eventConfig from "@/event-dates.json";
export function isSignupBlocked(): { blocked: boolean; message?: string } { export function isSignupBlocked(currentTime?: Date): { blocked: boolean; message?: string } {
const now = new Date(); const now = currentTime || new Date();
const currentDate = now.toISOString().split("T")[0]; // YYYY-MM-DD format const cutoffTime = eventConfig.cutoffTime || "15:00";
const currentTime = now.toTimeString().slice(0, 5); // HH:MM format const blockDurationHours = eventConfig.blockDurationHours || 6;
// Check if today is an event date // Check each event date to see if we're in a block period
const isEventDay = eventConfig.eventDates.includes(currentDate); for (const eventDate of eventConfig.eventDates) {
// Parse the event date and cutoff time in local timezone
const [year, month, day] = eventDate.split("-").map(Number);
const [hours, minutes] = cutoffTime.split(":").map(Number);
const blockStart = new Date(year, month - 1, day, hours, minutes, 0, 0);
if (isEventDay) { // Calculate when the block period ends (using wall-clock hours to handle DST correctly)
// Check if current time is after the cutoff time (default 15:00 / 3pm) const blockEnd = new Date(blockStart);
const cutoffTime = eventConfig.cutoffTime || "15:00"; blockEnd.setHours(blockStart.getHours() + blockDurationHours);
if (currentTime >= cutoffTime) {
// Check if current time is within the block period
if (now >= blockStart && now < blockEnd) {
return { return {
blocked: true, blocked: true,
message: eventConfig.message, message: eventConfig.message,

View File

@@ -37,7 +37,7 @@
"@types/react": "19.2.2", "@types/react": "19.2.2",
"@types/react-dom": "19.2.2", "@types/react-dom": "19.2.2",
"eslint": "9.38.0", "eslint": "9.38.0",
"eslint-config-next": "16.0.0", "eslint-config-next": "16.0.1",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"postcss": "8.5.6", "postcss": "8.5.6",
"tailwindcss": "4.1.16", "tailwindcss": "4.1.16",

104
pnpm-lock.yaml generated
View File

@@ -40,10 +40,10 @@ importers:
version: 0.548.0(react@19.2.0) version: 0.548.0(react@19.2.0)
next: next:
specifier: ^16.0.0 specifier: ^16.0.0
version: 16.0.0(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) version: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
next-plausible: next-plausible:
specifier: ^3.12.4 specifier: ^3.12.4
version: 3.12.4(next@16.0.0(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) version: 3.12.4(next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
postcss-flexbugs-fixes: postcss-flexbugs-fixes:
specifier: ^5.0.2 specifier: ^5.0.2
version: 5.0.2(postcss@8.5.6) version: 5.0.2(postcss@8.5.6)
@@ -88,8 +88,8 @@ importers:
specifier: 9.38.0 specifier: 9.38.0
version: 9.38.0(jiti@2.6.1) version: 9.38.0(jiti@2.6.1)
eslint-config-next: eslint-config-next:
specifier: 16.0.0 specifier: 16.0.1
version: 16.0.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) version: 16.0.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)
eslint-config-prettier: eslint-config-prettier:
specifier: 10.1.8 specifier: 10.1.8
version: 10.1.8(eslint@9.38.0(jiti@2.6.1)) version: 10.1.8(eslint@9.38.0(jiti@2.6.1))
@@ -690,56 +690,56 @@ packages:
'@napi-rs/wasm-runtime@0.2.12': '@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@next/env@16.0.0': '@next/env@16.0.1':
resolution: {integrity: sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==} resolution: {integrity: sha512-LFvlK0TG2L3fEOX77OC35KowL8D7DlFF45C0OvKMC4hy8c/md1RC4UMNDlUGJqfCoCS2VWrZ4dSE6OjaX5+8mw==}
'@next/eslint-plugin-next@16.0.0': '@next/eslint-plugin-next@16.0.1':
resolution: {integrity: sha512-IB7RzmmtrPOrpAgEBR1PIQPD0yea5lggh5cq54m51jHjjljU80Ia+czfxJYMlSDl1DPvpzb8S9TalCc0VMo9Hw==} resolution: {integrity: sha512-g4Cqmv/gyFEXNeVB2HkqDlYKfy+YrlM2k8AVIO/YQVEPfhVruH1VA99uT1zELLnPLIeOnx8IZ6Ddso0asfTIdw==}
'@next/swc-darwin-arm64@16.0.0': '@next/swc-darwin-arm64@16.0.1':
resolution: {integrity: sha512-/CntqDCnk5w2qIwMiF0a9r6+9qunZzFmU0cBX4T82LOflE72zzH6gnOjCwUXYKOBlQi8OpP/rMj8cBIr18x4TA==} resolution: {integrity: sha512-R0YxRp6/4W7yG1nKbfu41bp3d96a0EalonQXiMe+1H9GTHfKxGNCGFNWUho18avRBPsO8T3RmdWuzmfurlQPbg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@16.0.0': '@next/swc-darwin-x64@16.0.1':
resolution: {integrity: sha512-hB4GZnJGKa8m4efvTGNyii6qs76vTNl+3dKHTCAUaksN6KjYy4iEO3Q5ira405NW2PKb3EcqWiRaL9DrYJfMHg==} resolution: {integrity: sha512-kETZBocRux3xITiZtOtVoVvXyQLB7VBxN7L6EPqgI5paZiUlnsgYv4q8diTNYeHmF9EiehydOBo20lTttCbHAg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@16.0.0': '@next/swc-linux-arm64-gnu@16.0.1':
resolution: {integrity: sha512-E2IHMdE+C1k+nUgndM13/BY/iJY9KGCphCftMh7SXWcaQqExq/pJU/1Hgn8n/tFwSoLoYC/yUghOv97tAsIxqg==} resolution: {integrity: sha512-hWg3BtsxQuSKhfe0LunJoqxjO4NEpBmKkE+P2Sroos7yB//OOX3jD5ISP2wv8QdUwtRehMdwYz6VB50mY6hqAg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@16.0.0': '@next/swc-linux-arm64-musl@16.0.1':
resolution: {integrity: sha512-xzgl7c7BVk4+7PDWldU+On2nlwnGgFqJ1siWp3/8S0KBBLCjonB6zwJYPtl4MUY7YZJrzzumdUpUoquu5zk8vg==} resolution: {integrity: sha512-UPnOvYg+fjAhP3b1iQStcYPWeBFRLrugEyK/lDKGk7kLNua8t5/DvDbAEFotfV1YfcOY6bru76qN9qnjLoyHCQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@16.0.0': '@next/swc-linux-x64-gnu@16.0.1':
resolution: {integrity: sha512-sdyOg4cbiCw7YUr0F/7ya42oiVBXLD21EYkSwN+PhE4csJH4MSXUsYyslliiiBwkM+KsuQH/y9wuxVz6s7Nstg==} resolution: {integrity: sha512-Et81SdWkcRqAJziIgFtsFyJizHoWne4fzJkvjd6V4wEkWTB4MX6J0uByUb0peiJQ4WeAt6GGmMszE5KrXK6WKg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@16.0.0': '@next/swc-linux-x64-musl@16.0.1':
resolution: {integrity: sha512-IAXv3OBYqVaNOgyd3kxR4L3msuhmSy1bcchPHxDOjypG33i2yDWvGBwFD94OuuTjjTt/7cuIKtAmoOOml6kfbg==} resolution: {integrity: sha512-qBbgYEBRrC1egcG03FZaVfVxrJm8wBl7vr8UFKplnxNRprctdP26xEv9nJ07Ggq4y1adwa0nz2mz83CELY7N6Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@16.0.0': '@next/swc-win32-arm64-msvc@16.0.1':
resolution: {integrity: sha512-bmo3ncIJKUS9PWK1JD9pEVv0yuvp1KPuOsyJTHXTv8KDrEmgV/K+U0C75rl9rhIaODcS7JEb6/7eJhdwXI0XmA==} resolution: {integrity: sha512-cPuBjYP6I699/RdbHJonb3BiRNEDm5CKEBuJ6SD8k3oLam2fDRMKAvmrli4QMDgT2ixyRJ0+DTkiODbIQhRkeQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@16.0.0': '@next/swc-win32-x64-msvc@16.0.1':
resolution: {integrity: sha512-O1cJbT+lZp+cTjYyZGiDwsOjO3UHHzSqobkPNipdlnnuPb1swfcuY6r3p8dsKU4hAIEO4cO67ZCfVVH/M1ETXA==} resolution: {integrity: sha512-XeEUJsE4JYtfrXe/LaJn3z1pD19fK0Q6Er8Qoufi+HqvdO4LEPyCxLUt4rxA+4RfYo6S9gMlmzCMU2F+AatFqQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@@ -1707,8 +1707,8 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'} engines: {node: '>=10'}
eslint-config-next@16.0.0: eslint-config-next@16.0.1:
resolution: {integrity: sha512-DWKT1YAO9ex2rK0/EeiPpKU++ghTiG59z6m08/ReLRECOYIaEv17maSCYT8zmFQLwIrY5lhJ+iaJPQdT4sJd4g==} resolution: {integrity: sha512-wNuHw5gNOxwLUvpg0cu6IL0crrVC9hAwdS/7UwleNkwyaMiWIOAwf8yzXVqBBzL3c9A7jVRngJxjoSpPP1aEhg==}
peerDependencies: peerDependencies:
eslint: '>=9.0.0' eslint: '>=9.0.0'
typescript: '>=3.3.1' typescript: '>=3.3.1'
@@ -2317,8 +2317,8 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
next@16.0.0: next@16.0.1:
resolution: {integrity: sha512-nYohiNdxGu4OmBzggxy9rczmjIGI+TpR5vbKTsE1HqYwNm1B+YSiugSrFguX6omMOKnDHAmBPY4+8TNJk0Idyg==} resolution: {integrity: sha512-e9RLSssZwd35p7/vOa+hoDFggUZIUbZhIUSLZuETCwrCVvxOs87NamoUzT+vbcNAL8Ld9GobBnWOA6SbV/arOw==}
engines: {node: '>=20.9.0'} engines: {node: '>=20.9.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -3794,34 +3794,34 @@ snapshots:
'@tybys/wasm-util': 0.10.0 '@tybys/wasm-util': 0.10.0
optional: true optional: true
'@next/env@16.0.0': {} '@next/env@16.0.1': {}
'@next/eslint-plugin-next@16.0.0': '@next/eslint-plugin-next@16.0.1':
dependencies: dependencies:
fast-glob: 3.3.1 fast-glob: 3.3.1
'@next/swc-darwin-arm64@16.0.0': '@next/swc-darwin-arm64@16.0.1':
optional: true optional: true
'@next/swc-darwin-x64@16.0.0': '@next/swc-darwin-x64@16.0.1':
optional: true optional: true
'@next/swc-linux-arm64-gnu@16.0.0': '@next/swc-linux-arm64-gnu@16.0.1':
optional: true optional: true
'@next/swc-linux-arm64-musl@16.0.0': '@next/swc-linux-arm64-musl@16.0.1':
optional: true optional: true
'@next/swc-linux-x64-gnu@16.0.0': '@next/swc-linux-x64-gnu@16.0.1':
optional: true optional: true
'@next/swc-linux-x64-musl@16.0.0': '@next/swc-linux-x64-musl@16.0.1':
optional: true optional: true
'@next/swc-win32-arm64-msvc@16.0.0': '@next/swc-win32-arm64-msvc@16.0.1':
optional: true optional: true
'@next/swc-win32-x64-msvc@16.0.0': '@next/swc-win32-x64-msvc@16.0.1':
optional: true optional: true
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
@@ -4849,9 +4849,9 @@ snapshots:
escape-string-regexp@4.0.0: {} escape-string-regexp@4.0.0: {}
eslint-config-next@16.0.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): eslint-config-next@16.0.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@next/eslint-plugin-next': 16.0.0 '@next/eslint-plugin-next': 16.0.1
eslint: 9.38.0(jiti@2.6.1) eslint: 9.38.0(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1))
@@ -5501,15 +5501,15 @@ snapshots:
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
next-plausible@3.12.4(next@16.0.0(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): next-plausible@3.12.4(next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
dependencies: dependencies:
next: 16.0.0(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react: 19.2.0 react: 19.2.0
react-dom: 19.2.0(react@19.2.0) react-dom: 19.2.0(react@19.2.0)
next@16.0.0(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): next@16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
dependencies: dependencies:
'@next/env': 16.0.0 '@next/env': 16.0.1
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001751 caniuse-lite: 1.0.30001751
postcss: 8.4.31 postcss: 8.4.31
@@ -5517,14 +5517,14 @@ snapshots:
react-dom: 19.2.0(react@19.2.0) react-dom: 19.2.0(react@19.2.0)
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0) styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 16.0.0 '@next/swc-darwin-arm64': 16.0.1
'@next/swc-darwin-x64': 16.0.0 '@next/swc-darwin-x64': 16.0.1
'@next/swc-linux-arm64-gnu': 16.0.0 '@next/swc-linux-arm64-gnu': 16.0.1
'@next/swc-linux-arm64-musl': 16.0.0 '@next/swc-linux-arm64-musl': 16.0.1
'@next/swc-linux-x64-gnu': 16.0.0 '@next/swc-linux-x64-gnu': 16.0.1
'@next/swc-linux-x64-musl': 16.0.0 '@next/swc-linux-x64-musl': 16.0.1
'@next/swc-win32-arm64-msvc': 16.0.0 '@next/swc-win32-arm64-msvc': 16.0.1
'@next/swc-win32-x64-msvc': 16.0.0 '@next/swc-win32-x64-msvc': 16.0.1
sharp: 0.34.4 sharp: 0.34.4
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'