🔨 Refactor timeline page components

- Update project timeline page to display date and time in a different format
- Add functions for path name and icon SVG
- Calculate session duration and display list of session files with icons

[src/routes/projects/[projectId]/timeline/+page.svelte]
- Update import paths to use double quotes
- Change the format of the date and time displayed
- Add a path to name function
- Add a path to icon SVG function
- Add a session duration calculation
- Add a session files list with icons
This commit is contained in:
Kiril Videlov 2023-02-24 22:15:53 +01:00
parent d9dc79a4b4
commit a9d74fdc48

View File

@ -1,135 +1,157 @@
<script lang="ts"> <script lang="ts">
import { themeIcons } from 'seti-icons'; import { themeIcons } from "seti-icons";
import type { PageData } from './$types'; import type { PageData } from "./$types";
import { derived } from 'svelte/store'; import { derived } from "svelte/store";
import { asyncDerived } from '@square/svelte-store'; import { asyncDerived } from "@square/svelte-store";
import type { Session } from '$lib/sessions'; import type { Session } from "$lib/sessions";
import { startOfDay } from 'date-fns'; import { startOfDay } from "date-fns";
import { list as listDeltas } from '$lib/deltas'; import { list as listDeltas } from "$lib/deltas";
import type { Delta } from '$lib/deltas'; import type { Delta } from "$lib/deltas";
export let data: PageData; export let data: PageData;
const { project, sessions } = data; const { project, sessions } = data;
const formatDate = (date: Date) => { const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('default', { return new Intl.DateTimeFormat("default", {
weekday: 'short', weekday: "short",
day: 'numeric', day: "numeric",
month: 'short' month: "short",
}).format(date); }).format(date);
}; };
const formatTime = (date: Date) => { const formatTime = (date: Date) => {
return new Intl.DateTimeFormat('default', { return new Intl.DateTimeFormat("default", {
hour: 'numeric', hour: "numeric",
minute: 'numeric' minute: "numeric",
}).format(date); }).format(date);
}; };
function pathToName(path: string) { function pathToName(path: string) {
return path.split('/').slice(-1)[0]; return path.split("/").slice(-1)[0];
} }
const getIcon = themeIcons({ const getIcon = themeIcons({
blue: '#268bd2', blue: "#268bd2",
grey: '#657b83', grey: "#657b83",
'grey-light': '#839496', "grey-light": "#839496",
green: '#859900', green: "#859900",
orange: '#cb4b16', orange: "#cb4b16",
pink: '#d33682', pink: "#d33682",
purple: '#6c71c4', purple: "#6c71c4",
red: '#dc322f', red: "#dc322f",
white: '#fdf6e3', white: "#fdf6e3",
yellow: '#b58900', yellow: "#b58900",
ignore: '#586e75' ignore: "#586e75",
}); });
function pathToIconSvg(path: string) { function pathToIconSvg(path: string) {
let name: string = pathToName(path); let name: string = pathToName(path);
let { svg } = getIcon(name); let { svg } = getIcon(name);
return svg; return svg;
} }
type UISession = { session: Session; deltas: Record<string, Delta[]> }; type UISession = { session: Session; deltas: Record<string, Delta[]> };
$: dateSessions = asyncDerived([sessions], async ([sessions]) => { $: dateSessions = asyncDerived([sessions], async ([sessions]) => {
const deltas = await Promise.all( const deltas = await Promise.all(
sessions.map((session) => { sessions.map((session) => {
return listDeltas({ return listDeltas({
projectId: $project?.id, projectId: $project?.id,
sessionId: session.id sessionId: session.id,
}); });
}) })
); );
const uiSessions = sessions const uiSessions = sessions
.map((session, i) => { .map((session, i) => {
return { session, deltas: deltas[i] } as UISession; return { session, deltas: deltas[i] } as UISession;
}) })
.filter((uiSession) => { .filter((uiSession) => {
return Object.keys(uiSession.deltas).length > 0; return Object.keys(uiSession.deltas).length > 0;
}); });
const dateSessions: Record<number, UISession[]> = {}; const dateSessions: Record<number, UISession[]> = {};
uiSessions.forEach((uiSession) => { uiSessions.forEach((uiSession) => {
const date = startOfDay(new Date(uiSession.session.meta.startTimestampMs)); const date = startOfDay(
if (dateSessions[date.getTime()]) { new Date(uiSession.session.meta.startTimestampMs)
dateSessions[date.getTime()]?.push(uiSession); );
} else { if (dateSessions[date.getTime()]) {
dateSessions[date.getTime()] = [uiSession]; dateSessions[date.getTime()]?.push(uiSession);
} } else {
}); dateSessions[date.getTime()] = [uiSession];
}
});
return dateSessions; return dateSessions;
}); });
</script> </script>
<div class="flex"> <div class="flex">
<div class="m-6 overflow-x-hidden w-full"> <div class="m-6 overflow-x-hidden w-full">
All sessions All sessions
<div class="flex flex-row border overflow-x-auto space-x-12 px-4 py-12"> <div class="flex flex-row border overflow-x-auto space-x-12 px-4 py-12">
{#if $dateSessions === undefined} {#if $dateSessions === undefined}
<span>Loading...</span> <span>Loading...</span>
{:else} {:else}
{#each Object.entries($dateSessions) as [dateMilliseconds, uiSessions]} {#each Object.entries($dateSessions) as [dateMilliseconds, uiSessions]}
<!-- Day --> <!-- Day -->
<div class="bg-zinc-600 py-1 min-w-full overflow-hidden"> <div class="bg-zinc-600 py-1">
<div>{formatDate(new Date(+dateMilliseconds))}</div> <div>{formatDate(new Date(+dateMilliseconds))}</div>
<div class="flex space-x-2 "> <div class="flex space-x-2 ">
{#each uiSessions as uiSession} {#each uiSessions as uiSession}
<!-- Session --> <!-- Session -->
<div> <div>
<div class="text-sm rounded borded bg-orange-500 text-zinc-200"> <div
{formatTime(new Date(uiSession.session.meta.startTimestampMs))} class="text-sm rounded borded bg-orange-500 text-zinc-200"
- >
{formatTime(new Date(uiSession.session.meta.lastTimestampMs))} {formatTime(
</div> new Date(
<div title="Session duration"> uiSession.session.meta.startTimestampMs
{Math.round( )
(uiSession.session.meta.lastTimestampMs - )}
uiSession.session.meta.startTimestampMs) / -
1000 / {formatTime(
60 new Date(
)} min uiSession.session.meta.lastTimestampMs
</div> )
<div title="Session files"> )}
{#each Object.keys(uiSession.deltas) as filePath} </div>
<div class="flex flex-row w-32 items-center"> <div title="Session duration">
<div class="w-6 h-6 text-white fill-blue-400"> {Math.round(
{@html pathToIconSvg(filePath)} (uiSession.session.meta
</div> .lastTimestampMs -
<div class="text-white w-24 truncate"> uiSession.session.meta
{pathToName(filePath)} .startTimestampMs) /
</div> 1000 /
</div> 60
{/each} )} min
</div> </div>
</div> <div title="Session files">
{/each} {#each Object.keys(uiSession.deltas) as filePath}
</div> <div
</div> class="flex flex-row w-32 items-center"
{/each} >
{/if} <div
</div> class="w-6 h-6 text-white fill-blue-400"
</div> >
{@html pathToIconSvg(
filePath
)}
</div>
<div
class="text-white w-24 truncate"
>
{pathToName(filePath)}
</div>
</div>
{/each}
</div>
</div>
{/each}
</div>
</div>
{/each}
{/if}
</div>
</div>
</div> </div>