Karolis' playground schedule / master src / App.svelte
master

Tree @master (Download .tar.gz)

App.svelte @masterraw · history · blame

<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>