first commit

This commit is contained in:
2026-08-05 15:26:50 +05:30
parent c000e5dc95
commit 2c8c394795
126 changed files with 10824 additions and 302 deletions

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildActivity} from '@/lib/mock/dashboard';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildActivity(q.storeId, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildKpis} from '@/lib/mock/dashboard';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildKpis(q.range, q.storeId, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildPeakHours} from '@/lib/mock/dashboard';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildPeakHours(q.storeId), q);
}

View File

@@ -0,0 +1,20 @@
import type {NextRequest} from 'next/server';
import type {Granularity} from '@/lib/api/contracts';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildPeriodPerformance} from '@/lib/mock/analytics';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
const raw = req.nextUrl.searchParams.get('granularity');
const granularity: Granularity = raw === 'monthly' ? 'monthly' : 'weekly';
return ok(
wantsEmpty(q) ? [] : buildPeriodPerformance(granularity, q.storeId, q.nowMs),
q,
);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildRewardUsage} from '@/lib/mock/analytics';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildRewardUsage(q.storeId, q.range), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildStoreComparison} from '@/lib/mock/analytics';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildStoreComparison(q.range, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildTimeseries} from '@/lib/mock/dashboard';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildTimeseries(q.range, q.storeId, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildLytActivity} from '@/lib/mock/lyts';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildLytActivity(q.storeId, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildRedemptions} from '@/lib/mock/lyts';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildRedemptions(q.storeId, q.range, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildRewards} from '@/lib/mock/lyts';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildRewards(q.storeId, q.range, q.nowMs), q);
}

View File

@@ -0,0 +1,28 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate} from '@/lib/api/server';
import {readProfile} from '@/lib/mock/settings';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(readProfile(), q);
}
/**
* Accepts a profile patch and echoes the merged result.
*
* There is no persistence layer yet, so this deliberately does NOT pretend to
* save — it validates the shape and returns what the merged record would be,
* which is enough for the form to exercise its success path honestly.
*/
export async function PATCH(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
const patch = (await req.json()) as Record<string, unknown>;
return ok({...readProfile(), ...patch}, q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildAttendance} from '@/lib/mock/staff';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildAttendance(q.storeId, q.range, q.nowMs), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildStaff} from '@/lib/mock/staff';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildStaff(q.storeId, q.range), q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate} from '@/lib/api/server';
import {buildStaffSummary} from '@/lib/mock/staff';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(buildStaffSummary(q.storeId, q.range), q);
}

View File

@@ -0,0 +1,21 @@
import type {NextRequest} from 'next/server';
import {fail, ok, parseQuery, simulate} from '@/lib/api/server';
import {buildStore} from '@/lib/mock/stores';
export const dynamic = 'force-dynamic';
export async function GET(
req: NextRequest,
{params}: {params: Promise<{storeId: string}>},
) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
const {storeId} = await params;
const store = buildStore(storeId, q.range, q.nowMs);
if (!store) {
return fail('not_found', `No store with id "${storeId}"`, 404);
}
return ok(store, q);
}

View File

@@ -0,0 +1,12 @@
import type {NextRequest} from 'next/server';
import {ok, parseQuery, simulate, wantsEmpty} from '@/lib/api/server';
import {buildStores} from '@/lib/mock/stores';
export const dynamic = 'force-dynamic';
export async function GET(req: NextRequest) {
const q = parseQuery(req);
const simulated = await simulate(q);
if (simulated) return simulated;
return ok(wantsEmpty(q) ? [] : buildStores(q.range, q.nowMs), q);
}