Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
176 lines
5.3 KiB
TypeScript
176 lines
5.3 KiB
TypeScript
import config from "@payload-config";
|
|
import { getPayload } from "payload";
|
|
import { headers as nextHeaders } from "next/headers";
|
|
import {
|
|
FridayCalendar,
|
|
type FridayCalendarEntry,
|
|
type FridayCalendarMission,
|
|
} from "@/components/frontend/intelligence/FridayCalendar";
|
|
import type { SideOpDialogOption } from "@/components/frontend/intelligence/CreateSideOpDialog";
|
|
import {
|
|
dateKeyOf,
|
|
nextDayKey,
|
|
parseDateKey,
|
|
wallTimeToInstant,
|
|
zonedDateKey,
|
|
} from "@/lib/intelligence/fridayTime";
|
|
|
|
export const metadata = {
|
|
title: "Friday Operations",
|
|
};
|
|
|
|
const MAX_SCHEDULED_AHEAD_MISSIONS = 2;
|
|
|
|
const FRIDAYS_SHOWN = 8;
|
|
|
|
function nextFridays(count: number): string[] {
|
|
const fridays: string[] = [];
|
|
const cursor = zonedDateKey(new Date());
|
|
const [y, m, d] = cursor.split("-").map(Number);
|
|
const utc = new Date(Date.UTC(y, m - 1, d));
|
|
while (fridays.length < count) {
|
|
if (utc.getUTCDay() === 5) {
|
|
fridays.push(dateKeyOf(utc.getUTCFullYear(), utc.getUTCMonth() + 1, utc.getUTCDate()));
|
|
}
|
|
utc.setUTCDate(utc.getUTCDate() + 1);
|
|
}
|
|
return fridays;
|
|
}
|
|
|
|
export default async function FridayCalendarPage() {
|
|
const payload = await getPayload({ config });
|
|
|
|
const { user } = await payload.auth({
|
|
headers: await nextHeaders(),
|
|
canSetHeaders: false,
|
|
});
|
|
|
|
const fridays = nextFridays(FRIDAYS_SHOWN);
|
|
const first = parseDateKey(fridays[0]);
|
|
const last = parseDateKey(nextDayKey(fridays[fridays.length - 1]));
|
|
const rangeStart = wallTimeToInstant(first.year, first.month, first.day);
|
|
const rangeEnd = wallTimeToInstant(last.year, last.month, last.day);
|
|
|
|
const missionsRes = await payload.find({
|
|
collection: "missions",
|
|
where: {
|
|
"classification.startDateTime": {
|
|
greater_than_equal: rangeStart.toISOString(),
|
|
less_than: rangeEnd.toISOString(),
|
|
},
|
|
},
|
|
sort: "classification.startDateTime",
|
|
depth: 1,
|
|
limit: 200,
|
|
user,
|
|
overrideAccess: false,
|
|
select: {
|
|
name: true,
|
|
codeName: true,
|
|
operationType: true,
|
|
ownershipAndStatus: { status: true },
|
|
classification: { startDateTime: true, map: true },
|
|
createdBy: true,
|
|
},
|
|
});
|
|
|
|
const entries: FridayCalendarEntry[] = fridays.map((fridayKey) => {
|
|
const missions: FridayCalendarMission[] = missionsRes.docs
|
|
.filter((m) => zonedDateKey(new Date(m.classification.startDateTime)) === fridayKey)
|
|
.map((m) => {
|
|
const map = m.classification.map;
|
|
const createdBy = m.createdBy;
|
|
const creator =
|
|
typeof createdBy === "object" && createdBy !== null
|
|
? (createdBy as { displayName?: string | null; username?: string | null })
|
|
: null;
|
|
return {
|
|
id: m.id,
|
|
name: m.name,
|
|
codeName: m.codeName,
|
|
status: m.ownershipAndStatus.status,
|
|
startDateTime: m.classification.startDateTime,
|
|
operationType: m.operationType,
|
|
mapName: typeof map === "object" && map !== null ? map.name : null,
|
|
hostName:
|
|
creator != null
|
|
? (creator.displayName ?? creator.username ?? null)
|
|
: null,
|
|
isOwn:
|
|
user != null &&
|
|
(typeof createdBy === "object" ? createdBy?.id : createdBy) === user.id,
|
|
};
|
|
});
|
|
const k = parseDateKey(fridayKey);
|
|
return {
|
|
fridayIso: fridayKey,
|
|
startIso: wallTimeToInstant(k.year, k.month, k.day, 20, 0).toISOString(),
|
|
missions,
|
|
};
|
|
});
|
|
|
|
let remainingSlots = 0;
|
|
let maps: SideOpDialogOption[] = [];
|
|
let campaigns: SideOpDialogOption[] = [];
|
|
let modLists: SideOpDialogOption[] = [];
|
|
|
|
if (user) {
|
|
const userId = user.id as number;
|
|
const [scheduledCount, mapsRes, campaignsRes, modListsRes] = await Promise.all([
|
|
payload.count({
|
|
collection: "missions",
|
|
where: {
|
|
and: [
|
|
{ createdBy: { equals: userId } },
|
|
{ "classification.startDateTime": { greater_than: new Date().toISOString() } },
|
|
{ "ownershipAndStatus.status": { not_in: ["Completed", "Cancelled"] } },
|
|
],
|
|
},
|
|
}),
|
|
payload.find({
|
|
collection: "maps",
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
limit: 200,
|
|
sort: "name",
|
|
select: { name: true },
|
|
}),
|
|
payload.find({
|
|
collection: "campaigns",
|
|
where: { owner: { equals: userId } },
|
|
depth: 0,
|
|
limit: 100,
|
|
sort: "name",
|
|
select: { name: true },
|
|
}),
|
|
payload.find({
|
|
collection: "mod-lists",
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
limit: 200,
|
|
sort: "modlistName",
|
|
select: { modlistName: true },
|
|
}),
|
|
]);
|
|
|
|
remainingSlots = Math.max(0, MAX_SCHEDULED_AHEAD_MISSIONS - scheduledCount.totalDocs);
|
|
maps = mapsRes.docs.map((m) => ({ id: m.id, name: m.name }));
|
|
campaigns = campaignsRes.docs.map((c) => ({ id: c.id, name: c.name }));
|
|
modLists = modListsRes.docs.map((l) => ({ id: l.id, name: l.modlistName }));
|
|
}
|
|
|
|
return (
|
|
<div className="p-5">
|
|
<div className="mx-auto w-full max-w-5xl">
|
|
<FridayCalendar
|
|
entries={entries}
|
|
remainingSlots={remainingSlots}
|
|
maxSlots={MAX_SCHEDULED_AHEAD_MISSIONS}
|
|
maps={maps}
|
|
campaigns={campaigns}
|
|
modLists={modLists}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|