feat: schedule automatic form closure
This commit is contained in:
@@ -68,6 +68,9 @@ export async function getAdminDashboardData(
|
||||
const tomorrowStartSql = toPostgresTimestamp(tomorrowStart);
|
||||
const last7DaysStartSql = toPostgresTimestamp(last7DaysStart);
|
||||
const totalResponses = sql<number>`count(${submissions.id})::integer`;
|
||||
const acceptingSubmissions = sql<boolean>`${forms.isOpen} and (
|
||||
${forms.closesAt} is null or ${forms.closesAt} > ${now}
|
||||
)`;
|
||||
const responsesToday = sql<number>`count(${submissions.id}) filter (
|
||||
where ${submissions.submittedAt} >= ${todayStartSql}::timestamp
|
||||
and ${submissions.submittedAt} < ${tomorrowStartSql}::timestamp
|
||||
@@ -82,8 +85,8 @@ export async function getAdminDashboardData(
|
||||
db
|
||||
.select({
|
||||
totalForms: sql<number>`count(*)::integer`,
|
||||
openForms: sql<number>`count(*) filter (where ${forms.isOpen})::integer`,
|
||||
closedForms: sql<number>`count(*) filter (where not ${forms.isOpen})::integer`,
|
||||
openForms: sql<number>`count(*) filter (where ${acceptingSubmissions})::integer`,
|
||||
closedForms: sql<number>`count(*) filter (where not (${acceptingSubmissions}))::integer`,
|
||||
})
|
||||
.from(forms),
|
||||
db
|
||||
@@ -117,7 +120,7 @@ export async function getAdminDashboardData(
|
||||
.select({
|
||||
id: forms.id,
|
||||
title: forms.title,
|
||||
isOpen: forms.isOpen,
|
||||
isOpen: acceptingSubmissions,
|
||||
totalResponses,
|
||||
responsesToday,
|
||||
responsesLast7Days,
|
||||
@@ -125,7 +128,7 @@ export async function getAdminDashboardData(
|
||||
})
|
||||
.from(forms)
|
||||
.leftJoin(submissions, eq(submissions.formId, forms.id))
|
||||
.groupBy(forms.id, forms.title, forms.isOpen, forms.createdAt)
|
||||
.groupBy(forms.id, forms.title, forms.isOpen, forms.closesAt, forms.createdAt)
|
||||
.orderBy(
|
||||
desc(responsesLast7Days),
|
||||
desc(totalResponses),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { isFormAcceptingSubmissions } from "@/lib/form-availability";
|
||||
|
||||
const now = new Date("2026-07-30T12:00:00.000Z");
|
||||
|
||||
describe("isFormAcceptingSubmissions", () => {
|
||||
test("keeps a manually open form without a deadline open", () => {
|
||||
expect(isFormAcceptingSubmissions({ isOpen: true }, now)).toBe(true);
|
||||
});
|
||||
|
||||
test("keeps a form open before its deadline", () => {
|
||||
expect(
|
||||
isFormAcceptingSubmissions(
|
||||
{ isOpen: true, closesAt: "2026-07-30T12:00:01.000Z" },
|
||||
now,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("closes a form when its deadline is reached", () => {
|
||||
expect(
|
||||
isFormAcceptingSubmissions(
|
||||
{ isOpen: true, closesAt: "2026-07-30T12:00:00.000Z" },
|
||||
now,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test("manual closure overrides a future deadline", () => {
|
||||
expect(
|
||||
isFormAcceptingSubmissions(
|
||||
{ isOpen: false, closesAt: "2026-08-01T12:00:00.000Z" },
|
||||
now,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
export type FormAvailability = {
|
||||
isOpen: boolean;
|
||||
closesAt?: Date | string | null;
|
||||
};
|
||||
|
||||
export function isFormAcceptingSubmissions(
|
||||
form: FormAvailability,
|
||||
now = new Date(),
|
||||
) {
|
||||
if (!form.isOpen) return false;
|
||||
if (!form.closesAt) return true;
|
||||
|
||||
return new Date(form.closesAt).getTime() > now.getTime();
|
||||
}
|
||||
Reference in New Issue
Block a user