<script lang="ts">
import { Link, Route, Router } from "svelte-navigator";
import DayContainer from "./lib/DayContainer.svelte";
import {
SelectOperators,
type Day,
type timeperiod,
} from "./lib/models/models";
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat";
import isoWeek from "dayjs/plugin/isoWeek";
import {
parseType,
spliceDays,
type legacyDay,
parseRange,
} from "./lib/models/parser";
import { days, type weekDay } from "./lib/models/models";
import createHashSource from "./lib/navigatorHistory";
import SingleUserDayCnt from "./lib/SingleUserDayCnt.svelte";
import Navbar from "./lib/navbar.svelte";
dayjs.extend(customParseFormat);
dayjs.extend(isoWeek);
async function loadSchedule(cfg: config, id: string) {
const request = await fetch(`./scheduled/${id}.json`);
const t = (await request.json()) as { [k: string]: legacyDay };
return new Map(
days.map((x) => [x, insertPlaceholder(cfg, parseType(x, t[x]))]),
);
}
function calculatetimePeriods(tt: Map<weekDay, Day>) {
const result = {};
for (const t of tt.values()) {
for (const [name, opData] of t.operators) {
if (!result[name]) {
result[name] = {};
}
for (const tp of opData) {
const label = tp.label ? `${tp.type}:${tp.label}` : tp.type;
const tmp = result[name][label];
result[name][label] =
(tmp || 0) + tp.endTime - tp.startTime;
}
}
}
return result;
}
let cache = {};
const loadScheduleMemoized = (
cfg: config,
id: string,
): Promise<Map<weekDay, Day>> => {
if (id in cache) {
return cache[id];
}
cache[id] = cache[id] || loadSchedule(cfg, id);
return cache[id];
};
type config = {
periods: number;
dayOffset: number;
switchTime: number;
switchoverDay: weekDay;
oncallPlaceholders: { [d in weekDay]: { [key: string]: string } };
};
function generatePlaceholder(cfg: config, day: weekDay): timeperiod[] {
return Object.entries(cfg.oncallPlaceholders[day]).map(([l, v]) => {
return { type: "placeholder", label: l, ...parseRange(v) };
});
}
function insertPlaceholder(cfg: config, d: Day): Day {
if (!cfg.oncallPlaceholders) {
return d;
}
d.operators.set("placeholder", generatePlaceholder(cfg, d.Name));
console.log(d);
return d;
}
const configPromise = fetch(`./config.json`).then(
(x) => x.json() as Promise<config>,
);
function mod(n: number, m: number) {
return ((n % m) + m) % m;
}
function calculateWeekId(date: Date, cfg: config) {
return mod(dayjs(date).isoWeek() + cfg.dayOffset, cfg.periods);
}
async function createFixedSchedule(
cfg: config,
now: Date,
d: Date,
): Promise<Map<weekDay, Day>> {
const id = calculateWeekId(d, cfg);
let idStr = id.toString();
if (dayjs(d).isoWeek() === dayjs(now).isoWeek()) {
idStr = "current";
}
const previousId = mod(id - 1, cfg.periods);
console.log(idStr, previousId);
const a = await loadScheduleMemoized(cfg, idStr);
const p = await loadScheduleMemoized(cfg, previousId.toString());
return new Map(
days
.map<[weekDay, Day]>((x) => {
if (x === cfg.switchoverDay) {
return [
x,
spliceDays(p.get(x), a.get(x), cfg.switchTime),
];
}
return [x, a.get(x)];
})
.map(([x, data], i) => {
return [
x,
{
...data,
date: dayjs(d)
.isoWeekday(i + 1)
.toDate(),
},
];
}),
);
}
const Now = new Date();
let val: string = "";
$: t = val == "" ? Now : new Date(val);
</script>
<Router history={createHashSource()}>
{#await configPromise then cfg}
<Route path="/*">
<input type="date" bind:value={val} />
{#await createFixedSchedule(cfg, Now, t)}
"loading"
{:then dd}
<Route path="">
<Navbar {cfg} suffix="/" />
{#each days as day}
<DayContainer
linkTemplate={(name) => `${name}`}
dayData={dd.get(day)}
/>
{/each}
<textarea disabled style="width: 100%; height: 50vh"
>{JSON.stringify(
calculatetimePeriods(dd),
null,
4,
)}</textarea
>
</Route>
<Route path=":name" let:params>
<Link to="/">Back</Link>
<Navbar {cfg} suffix="/{params.name}" />
{#each days as day}
<SingleUserDayCnt
dayData={SelectOperators(dd.get(day), params.name)}
/>
{/each}
<textarea disabled style="width: 100%; height: 50vh"
>{JSON.stringify(
calculatetimePeriods(dd)[params.name],
null,
4,
)}</textarea
>
</Route>
{:catch e}
we done goofed {e}
{/await}
</Route>
<Route path="/raw/:id/*" let:params>
{#await loadScheduleMemoized(cfg, params.id)}
"loading"
{:then dd}
<Route path="/">
<Navbar {cfg} suffix="/" />
{#each days as day}
<DayContainer
linkTemplate={(name) => `${name}`}
dayData={dd.get(day)}
/>
{/each}
<textarea disabled style="width: 100%; height: 50vh"
>{JSON.stringify(
calculatetimePeriods(dd),
null,
4,
)}</textarea
>
</Route>
<Route path=":name" let:params={pparams}>
<Link to="/raw/{params.id}">Back</Link>
<Navbar {cfg} suffix="/{pparams.name}" />
{#each days as day}
<SingleUserDayCnt
dayData={SelectOperators(dd.get(day), pparams.name)}
/>
{/each}
<textarea disabled style="width: 100%; height: 50vh"
>{JSON.stringify(
calculatetimePeriods(dd)[pparams.name],
null,
4,
)}</textarea
>
</Route>
{:catch e}
we done goofed {e}
{/await}
</Route>
{:catch e}
we done goofed {e}
{/await}
</Router>
<style>
</style>