diff --git a/__tests__/sanity.test.tsx b/__tests__/sanity.test.tsx
index a450991..25d1b4e 100644
--- a/__tests__/sanity.test.tsx
+++ b/__tests__/sanity.test.tsx
@@ -1,10 +1,9 @@
import { render, screen } from "@testing-library/react";
-import DashboardPage from "@/app/(authenticated)/dashboard/page";
+import { TestComponent } from "./test-component";
describe("jest setup", () => {
- it("runs React + Testing Library", async () => {
- const jsx = await DashboardPage();
- render(jsx);
- expect(screen.getByText("You are admin")).toBeInTheDocument();
- });
+ it("runs React + Testing Library", () => {
+ render();
+ expect(screen.getByText("ok")).toBeInTheDocument();
+ });
});
diff --git a/__tests__/test-component.tsx b/__tests__/test-component.tsx
new file mode 100644
index 0000000..6b9a47a
--- /dev/null
+++ b/__tests__/test-component.tsx
@@ -0,0 +1,3 @@
+export function TestComponent() {
+ return
ok
;
+}
diff --git a/app/(authenticated)/services/actions.ts b/app/(authenticated)/services/actions.ts
index 438ac5c..635ea5b 100644
--- a/app/(authenticated)/services/actions.ts
+++ b/app/(authenticated)/services/actions.ts
@@ -4,7 +4,7 @@ import { revalidatePath, updateTag } from "next/cache";
import { eq } from "drizzle-orm";
import { z } from "zod";
import { db } from "@/lib/db";
-import { services } from "@/lib/db/schema";
+import { services, type ProgramSlot as DbProgramSlot } from "@/lib/db/schema";
import { requireAdmin } from "@/lib/auth/require-admin";
import { cadStringToCents } from "@/lib/money";
import {
@@ -19,10 +19,7 @@ export type ServiceActionState = {
message?: string;
} | null;
-export type ProgramSlot = {
- dayOfWeek: number;
- time: string;
-};
+export type ProgramSlot = DbProgramSlot;
export type ProgramSchedule = {
startDate: string;
@@ -201,7 +198,9 @@ export async function createService(
await db.insert(services).values({
type,
- scheduledAt: scheduledAtValue,
+ startDate: scheduledAtValue?.startDate ?? null,
+ endDate: scheduledAtValue?.endDate ?? null,
+ slots: scheduledAtValue?.slots ?? null,
durationMinutes: duration_minutes,
stripeProductId: productId,
status: "active",
@@ -330,7 +329,11 @@ export async function updateService(
const dbPatch: Partial = {};
if (duration_minutes !== undefined) dbPatch.durationMinutes = duration_minutes;
- if (scheduledAtValue !== undefined) dbPatch.scheduledAt = scheduledAtValue;
+ if (scheduledAtValue !== undefined) {
+ dbPatch.startDate = scheduledAtValue.startDate;
+ dbPatch.endDate = scheduledAtValue.endDate;
+ dbPatch.slots = scheduledAtValue.slots;
+ }
if (Object.keys(dbPatch).length > 0) {
dbPatch.updatedAt = new Date();
diff --git a/app/(authenticated)/services/queries.ts b/app/(authenticated)/services/queries.ts
index ba4ea78..1a62d40 100644
--- a/app/(authenticated)/services/queries.ts
+++ b/app/(authenticated)/services/queries.ts
@@ -3,6 +3,7 @@ import { and, asc, desc, eq, inArray } from "drizzle-orm";
import { db } from "@/lib/db";
import { profiles, services } from "@/lib/db/schema";
import { getStripeServiceData } from "@/lib/stripe";
+import type { ProgramSchedule } from "@/app/(authenticated)/services/actions";
const SERVICES_TAG = "services";
const COACHES_TAG = "coaches";
@@ -13,7 +14,7 @@ export type ServiceType = "private_lessons" | "programs";
export type ServiceView = {
id: string;
type: ServiceType;
- scheduledAt: unknown;
+ scheduledAt: ProgramSchedule | null;
durationMinutes: number;
status: ServiceStatus;
stripeProductId: string;
@@ -25,12 +26,23 @@ export type ServiceView = {
priceCurrency: string | null;
};
+function rowToSchedule(
+ row: typeof services.$inferSelect,
+): ProgramSchedule | null {
+ if (!row.startDate || !row.endDate) return null;
+ return {
+ startDate: row.startDate,
+ endDate: row.endDate,
+ slots: row.slots ?? [],
+ };
+}
+
async function buildServiceView(row: typeof services.$inferSelect): Promise {
const stripeData = await getStripeServiceData(row.stripeProductId);
return {
id: row.id,
type: row.type,
- scheduledAt: row.scheduledAt,
+ scheduledAt: rowToSchedule(row),
durationMinutes: row.durationMinutes,
status: row.status,
stripeProductId: row.stripeProductId,
diff --git a/app/(authenticated)/services/service-dialog.tsx b/app/(authenticated)/services/service-dialog.tsx
index 407905b..84a980b 100644
--- a/app/(authenticated)/services/service-dialog.tsx
+++ b/app/(authenticated)/services/service-dialog.tsx
@@ -93,19 +93,6 @@ function fromISODate(value: string | undefined): Date | undefined {
return new Date(y, m - 1, d);
}
-function isProgramSchedule(value: unknown): value is ProgramSchedule {
- if (!value || typeof value !== "object") return false;
- const v = value as Partial;
- return (
- typeof v.startDate === "string" &&
- typeof v.endDate === "string" &&
- Array.isArray(v.slots) &&
- v.slots.every(
- (s) => typeof s?.dayOfWeek === "number" && typeof s?.time === "string",
- )
- );
-}
-
function DateRangePicker({
value,
onChange,
@@ -316,10 +303,7 @@ export function ServiceDialog(props: Props) {
const showForm = !isEdit || service !== null;
- const initialSchedule =
- service && isProgramSchedule(service.scheduledAt)
- ? service.scheduledAt
- : null;
+ const initialSchedule = service?.scheduledAt ?? null;
return (