mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-28 13:26:16 +03:00
🔨 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:
parent
d9dc79a4b4
commit
a9d74fdc48
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user