mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-01 04:14:45 +03:00
replace week-day-session pages with the unified timeline
This commit is contained in:
parent
c0a85d91b8
commit
87e9eb85aa
@ -35,29 +35,7 @@
|
||||
<nav
|
||||
class="flex items-center flex-none justify-between py-2 px-8 space-x-3 border-b select-none text-zinc-300 border-zinc-700"
|
||||
>
|
||||
<div
|
||||
class="text-zinc-400 w-64 font-medium grid grid-cols-3 items-center bg-zinc-700/50 rounded-lg h-7 px-4 gap-1"
|
||||
>
|
||||
<a
|
||||
class="
|
||||
{selection === 'week' ? 'bg-zinc-600/70 text-zinc-100' : ''}
|
||||
rounded-lg h-7 flex items-center justify-center p-3 text-center hover:text-zinc-100"
|
||||
href="/projects/{$project?.id}/week">Week</a
|
||||
>
|
||||
<a
|
||||
href="/projects/{$project?.id}/day"
|
||||
class="
|
||||
{selection === 'day' ? 'bg-zinc-600/70 text-zinc-100' : ''}
|
||||
rounded-lg h-7 flex items-center justify-center p-3 text-center hover:text-zinc-100">Day</a
|
||||
>
|
||||
<a
|
||||
href="/projects/{$project?.id}/sessions/{lastSessionId}"
|
||||
class="
|
||||
{selection === 'sessions' ? 'bg-zinc-600/70 text-zinc-100' : ''}
|
||||
rounded-lg h-7 flex items-center justify-center p-3 text-center hover:text-zinc-100"
|
||||
title="go to current session">Session</a
|
||||
>
|
||||
</div>
|
||||
<div />
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<h1 class="text-zinc-200 text-xl flex justify-center">
|
||||
Overview of {$project?.title}
|
||||
</h1>
|
||||
<div class="flex justify-center space-x-2">
|
||||
<a class="hover:text-zinc-200" href="/projects/{$project?.id}/week">Week</a>
|
||||
<a href="/projects/{$project?.id}/day" class="hover:text-zinc-200">Day</a>
|
||||
<a href="/projects/{$project?.id}/timeline" class="hover:text-zinc-200">New timeline</a>
|
||||
<div class="flex justify-center space-x-2 text-lg">
|
||||
<a href="/projects/{$project?.id}/timeline" class="hover:text-zinc-200 text-orange-400"
|
||||
>Timeline</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,84 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { IconChevronRight, IconChevronLeft } from '@tabler/icons-svelte';
|
||||
import { TimelineDaySession } from '$lib/components/timeline';
|
||||
import type { PageData } from './$types';
|
||||
import type { Session } from '$lib/sessions';
|
||||
import { derived } from 'svelte/store';
|
||||
export let data: PageData;
|
||||
const { project, sessions } = data;
|
||||
|
||||
let date = new Date();
|
||||
$: canNavigateForwad = new Date(date.getTime() + 24 * 60 * 60 * 1000) < new Date();
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return new Intl.DateTimeFormat('default', {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short'
|
||||
}).format(date);
|
||||
};
|
||||
|
||||
const sessionDisplayWidth = (session: Session) => {
|
||||
let sessionDurationMinutes =
|
||||
(session.meta.lastTimestampMs - session.meta.startTimestampMs) / 60;
|
||||
if (sessionDurationMinutes <= 10) {
|
||||
return 'w-40 min-w-40';
|
||||
} else {
|
||||
return 'w-60 min-w-60';
|
||||
}
|
||||
};
|
||||
|
||||
$: sessionsInDay = derived([sessions], ([sessions]) => {
|
||||
const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
const end = new Date(start.getTime() + 24 * 60 * 60 * 1000);
|
||||
return sessions.filter((session) => {
|
||||
return (
|
||||
start <= new Date(session.meta.startTimestampMs) &&
|
||||
new Date(session.meta.startTimestampMs) <= end
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full select-none text-zinc-400">
|
||||
<header class="flex items-center justify-between flex-none px-8 py-1.5 border-b border-zinc-700">
|
||||
<div class="flex items-center justify-start w-64">
|
||||
<button
|
||||
class="-ml-2 hover:text-zinc-100"
|
||||
on:click={() => (date = new Date(date.getTime() - 24 * 60 * 60 * 1000))}
|
||||
>
|
||||
<IconChevronLeft class="w-8 h-8" />
|
||||
</button>
|
||||
<div class="flex-grow text-center">
|
||||
{formatDate(date)}
|
||||
</div>
|
||||
<button
|
||||
class="-mr-2 hover:text-zinc-100 disabled:text-zinc-700"
|
||||
disabled={!canNavigateForwad}
|
||||
on:click={() => {
|
||||
if (canNavigateForwad) {
|
||||
date = new Date(date.getTime() + 24 * 60 * 60 * 1000);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconChevronRight class="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="w-full h-full overflow-scroll mx-2 flex">
|
||||
{#if $project}
|
||||
<div class="flex-grow items-center justify-center mt-4">
|
||||
<div class="justify-center flex flex-row space-x-2 pt-2">
|
||||
{#each $sessionsInDay as session}
|
||||
<div class={sessionDisplayWidth(session)}>
|
||||
<TimelineDaySession projectId={$project.id} {session} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p>Project not found</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -1,266 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-svelte';
|
||||
import { add, format, differenceInSeconds, addSeconds } from 'date-fns';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { derived } from 'svelte/store';
|
||||
import { Operation } from '$lib/deltas';
|
||||
import { Slider } from 'fluent-svelte';
|
||||
import { CodeViewer } from '$lib/components';
|
||||
import 'fluent-svelte/theme.css';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
$: session = data.session;
|
||||
$: previousSesssion = data.previousSesssion;
|
||||
$: nextSession = data.nextSession;
|
||||
$: deltas = data.deltas;
|
||||
|
||||
let time = new Date();
|
||||
$: start = new Date($session.meta.startTimestampMs);
|
||||
$: end = $session.hash ? addSeconds(new Date($session.meta.lastTimestampMs), 10) : time; // For some reason, some deltas are stamped a few seconds after the session end.
|
||||
// Also, if the session is current, the end time moves.
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
time = new Date();
|
||||
}, 10000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
});
|
||||
$: midpoint = add(start, {
|
||||
seconds: differenceInSeconds(end, start) * 0.5
|
||||
});
|
||||
$: quarter = add(start, {
|
||||
seconds: differenceInSeconds(end, start) * 0.25
|
||||
});
|
||||
$: threequarters = add(start, {
|
||||
seconds: differenceInSeconds(end, start) * 0.75
|
||||
});
|
||||
const timeStampToCol = (deltaTimestamp: Date) => {
|
||||
if (deltaTimestamp < start || deltaTimestamp > end) {
|
||||
console.error(
|
||||
`Delta timestamp out of session range. Delta timestamp: ${deltaTimestamp}, Session start: ${start}, Session end: ${end}`
|
||||
);
|
||||
}
|
||||
// there are 88 columns
|
||||
// start is column 17
|
||||
const totalDiff = differenceInSeconds(end, start);
|
||||
const eventDiff = differenceInSeconds(deltaTimestamp, start);
|
||||
const rat = eventDiff / totalDiff;
|
||||
const col = Math.floor(rat * 63 + 17);
|
||||
return col;
|
||||
};
|
||||
|
||||
const colToTimestamp = (col: number) => {
|
||||
const totalDiff = differenceInSeconds(end, start);
|
||||
const colDiff = col - 17;
|
||||
const rat = colDiff / 63;
|
||||
const eventDiff = totalDiff * rat;
|
||||
const timestamp = addSeconds(start, eventDiff);
|
||||
return timestamp;
|
||||
};
|
||||
|
||||
$: tickSizeMs = Math.floor((end.getTime() - start.getTime()) / 63); // how many ms each column represents
|
||||
let selectedFileIdx = 0;
|
||||
let value = 0;
|
||||
|
||||
$: doc = derived([data.deltas], ([allDeltas]) => {
|
||||
const filePath = Object.keys(allDeltas)[selectedFileIdx];
|
||||
const deltas = allDeltas[filePath];
|
||||
|
||||
let text = data.files[filePath] || '';
|
||||
if (!deltas) return text;
|
||||
|
||||
const sliderValueTimestampMs = colToTimestamp(value).getTime() + tickSizeMs; // Include the tick size so that the slider value is always in the future
|
||||
// Filter operations based on the current slider value
|
||||
const operations = deltas
|
||||
.filter(
|
||||
(delta) =>
|
||||
delta.timestampMs >= start.getTime() && delta.timestampMs <= sliderValueTimestampMs
|
||||
)
|
||||
.sort((a, b) => a.timestampMs - b.timestampMs)
|
||||
.flatMap((delta) => delta.operations);
|
||||
|
||||
operations.forEach((operation) => {
|
||||
if (Operation.isInsert(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.insert[0]) +
|
||||
operation.insert[1] +
|
||||
text.slice(operation.insert[0]);
|
||||
} else if (Operation.isDelete(operation)) {
|
||||
text =
|
||||
text.slice(0, operation.delete[0]) +
|
||||
text.slice(operation.delete[0] + operation.delete[1]);
|
||||
}
|
||||
});
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
return new Intl.DateTimeFormat('default', {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
}).format(date);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full text-zinc-400 overflow-hidden">
|
||||
<header class="flex items-center justify-between flex-none px-8 py-1.5 border-b border-zinc-700">
|
||||
<div class="flex items-center justify-start w-64">
|
||||
<a
|
||||
href="/projects/{$page.params.projectId}/sessions/{$previousSesssion?.id}"
|
||||
class="-ml-2 hover:text-zinc-100 {$previousSesssion
|
||||
? ''
|
||||
: 'opacity-50 pointer-events-none cursor-not-allowed'}"
|
||||
>
|
||||
<IconChevronLeft class="w-8 h-8" />
|
||||
</a>
|
||||
<div class="flex-grow text-center cursor-default grid grid-cols-7">
|
||||
<span class="col-span-3">{formatDate(start)}</span>
|
||||
<span>—</span>
|
||||
<span class="col-span-3">{formatDate(end)}</span>
|
||||
</div>
|
||||
<a
|
||||
href="/projects/{$page.params.projectId}/sessions/{$nextSession?.id}"
|
||||
class="-mr-2 hover:text-zinc-100 {$nextSession
|
||||
? ''
|
||||
: 'text-zinc-700 pointer-events-none cursor-not-allowed'}"
|
||||
>
|
||||
<IconChevronRight class="w-8 h-8" />
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- main part -->
|
||||
<div
|
||||
class="flex flex-col flex-none max-w-full select-none border-b border-zinc-700 h-full overflow-auto"
|
||||
>
|
||||
<div class="flex flex-col flex-none max-w-full mb-40">
|
||||
<!-- sticky header -->
|
||||
<div
|
||||
class="overflow-hidden sticky top-0 z-30 bg-zinc-800 flex-none shadow shadow-zinc-700 ring-1 ring-zinc-700 ring-opacity-5 mb-1"
|
||||
>
|
||||
<div class="grid-cols-11 -mr-px border-zinc-700 grid">
|
||||
<div />
|
||||
<div class="col-span-2 flex items-center justify-center py-2">
|
||||
<span>{format(start, 'hh:mm')}</span>
|
||||
</div>
|
||||
<div class="col-span-2 flex items-center justify-center py-2">
|
||||
<span>{format(quarter, 'hh:mm')}</span>
|
||||
</div>
|
||||
<div class="col-span-2 flex items-center justify-center py-2">
|
||||
<span>{format(midpoint, 'hh:mm')}</span>
|
||||
</div>
|
||||
<div class="col-span-2 flex items-center justify-center py-2">
|
||||
<span>{format(threequarters, 'hh:mm')}</span>
|
||||
</div>
|
||||
<div class="col-span-2 flex items-center justify-center py-2">
|
||||
<span>{format(end, 'hh:mm')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- needle -->
|
||||
<div class="grid grid-cols-11">
|
||||
<div class="col-span-2 flex items-center justify-center" />
|
||||
<div class="-mx-1 col-span-8 flex items-center justify-center">
|
||||
<Slider min={17} max={80} step={1} bind:value>
|
||||
<svelte:fragment slot="tooltip" let:value>
|
||||
{format(colToTimestamp(value), 'hh:mm')}
|
||||
</svelte:fragment>
|
||||
</Slider>
|
||||
</div>
|
||||
<div class="col-span-1 flex items-center justify-center" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-auto mb-1">
|
||||
<div class="grid flex-auto grid-cols-1 grid-rows-1">
|
||||
<!-- file names list -->
|
||||
<div
|
||||
class="bg-col-start-1 col-end-2 row-start-1 grid divide-y divide-zinc-700/20"
|
||||
style="grid-template-rows: repeat({Object.keys($deltas).length}, minmax(2rem, 1fr));"
|
||||
>
|
||||
<!-- <div class="row-end-1 h-7" /> -->
|
||||
|
||||
{#each Object.keys($deltas) as filePath, i}
|
||||
<div class="flex {i == selectedFileIdx ? 'bg-zinc-500/70' : ''}">
|
||||
<button
|
||||
class="z-20 flex justify-end items-center overflow-hidden sticky left-0 w-1/6 leading-5
|
||||
{selectedFileIdx == i
|
||||
? 'text-zinc-200 cursor-default'
|
||||
: 'text-zinc-400 hover:text-zinc-200 cursor-pointer'}"
|
||||
on:click={() => (selectedFileIdx = i)}
|
||||
title={filePath}
|
||||
>
|
||||
{filePath.split('/').pop()}
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- col selection -->
|
||||
<div
|
||||
class="col-start-1 col-end-2 row-start-1 grid"
|
||||
style="grid-template-columns: repeat(88, minmax(0, 1fr));"
|
||||
>
|
||||
<div class="bg-sky-400/60 " style=" grid-column: {value};" />
|
||||
</div>
|
||||
<!-- time vertical lines -->
|
||||
<div
|
||||
class="col-start-1 col-end-2 row-start-1 grid-rows-1 divide-x divide-zinc-700/50 grid grid-cols-11"
|
||||
>
|
||||
<div class="col-span-2 row-span-full" />
|
||||
<div class="col-span-2 row-span-full" />
|
||||
<div class="col-span-2 row-span-full" />
|
||||
<div class="col-span-2 row-span-full" />
|
||||
<div class="col-span-2 row-span-full" />
|
||||
<div class="col-span-2 row-span-full" />
|
||||
</div>
|
||||
|
||||
<!-- actual entries -->
|
||||
<ol
|
||||
class="col-start-1 col-end-2 row-start-1 grid"
|
||||
style="
|
||||
grid-template-columns: repeat(88, minmax(0, 1fr));
|
||||
grid-template-rows: repeat({Object.keys($deltas)
|
||||
.length}, minmax(0px, 1fr)) auto;"
|
||||
>
|
||||
{#each Object.entries($deltas) as [filePath, fileDeltas], idx}
|
||||
{#each fileDeltas as delta}
|
||||
<li
|
||||
class="relative flex items-center bg-zinc-300 hover:bg-zinc-100 rounded m-0.5 cursor-pointer"
|
||||
style="
|
||||
grid-row: {idx + 1} / span 1;
|
||||
grid-column: {timeStampToCol(
|
||||
new Date(delta.timestampMs)
|
||||
)} / span 1;"
|
||||
>
|
||||
<button
|
||||
class="z-20 h-full flex flex-col w-full items-center justify-center"
|
||||
on:click={() => {
|
||||
value = timeStampToCol(new Date(delta.timestampMs));
|
||||
selectedFileIdx = idx;
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
{/each}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-11 mt-6">
|
||||
<div class="col-span-2" />
|
||||
<div class="col-span-8 p-1 bg-zinc-500/70 rounded select-text">
|
||||
{#if $doc}
|
||||
<CodeViewer value={$doc} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,45 +0,0 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { derived, readable } from 'svelte/store';
|
||||
import { building } from '$app/environment';
|
||||
import type { Delta } from '$lib/deltas';
|
||||
|
||||
export const prerender = false;
|
||||
export const load: PageLoad = async ({ parent, params }) => {
|
||||
const { sessions } = await parent();
|
||||
const deltas = building
|
||||
? readable({} as Record<string, Delta[]>)
|
||||
: (await import('$lib/deltas')).default({
|
||||
projectId: params.projectId,
|
||||
sessionId: params.sessionId
|
||||
});
|
||||
const files = building
|
||||
? ({} as Record<string, string>)
|
||||
: (await import('$lib/sessions')).listFiles({
|
||||
projectId: params.projectId,
|
||||
sessionId: params.sessionId
|
||||
});
|
||||
return {
|
||||
session: derived(sessions, (sessions) => {
|
||||
const result = sessions.find((session) => session.id === params.sessionId);
|
||||
return result ? result : sessions[0];
|
||||
}),
|
||||
previousSesssion: derived(sessions, (sessions) => {
|
||||
const currentSessionIndex = sessions.findIndex((session) => session.id === params.sessionId);
|
||||
if (currentSessionIndex - 1 < sessions.length) {
|
||||
return sessions[currentSessionIndex - 1];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}),
|
||||
nextSession: derived(sessions, (sessions) => {
|
||||
const currentSessionIndex = sessions.findIndex((session) => session.id === params.sessionId);
|
||||
if (currentSessionIndex + 1 < sessions.length) {
|
||||
return sessions[currentSessionIndex + 1];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}),
|
||||
files: files,
|
||||
deltas: deltas
|
||||
};
|
||||
};
|
@ -1,318 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Week } from '$lib/week';
|
||||
import type { PageData } from './$types';
|
||||
import { WeekBlockEntry } from '$lib/components/week';
|
||||
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-svelte';
|
||||
import { derived } from 'svelte/store';
|
||||
import { toHumanBranchName } from '$lib/branch';
|
||||
|
||||
export let data: PageData;
|
||||
const { project, sessions } = data;
|
||||
|
||||
let week = Week.from(new Date());
|
||||
|
||||
$: canNavigateForwad = week.end.getTime() < new Date().getTime();
|
||||
const formatDate = (date: Date) => {
|
||||
return new Intl.DateTimeFormat('default', {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short'
|
||||
}).format(date);
|
||||
};
|
||||
|
||||
$: sessionsInWeek = derived([sessions], ([sessions]) => {
|
||||
return sessions.filter((session) => {
|
||||
return (
|
||||
week.start <= new Date(session.meta.startTimestampMs) &&
|
||||
new Date(session.meta.startTimestampMs) <= week.end
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full select-none text-zinc-400">
|
||||
<header class="flex items-center justify-between flex-none px-8 py-1.5 border-b border-zinc-700">
|
||||
<div class="flex items-center justify-start w-64">
|
||||
<button class="-ml-2 hover:text-zinc-100" on:click={() => (week = Week.previous(week))}>
|
||||
<IconChevronLeft class="w-8 h-8" />
|
||||
</button>
|
||||
<div class="flex-grow text-center cursor-default grid grid-cols-7">
|
||||
<span class="col-span-3">{formatDate(Week.nThDay(week, 0))}</span>
|
||||
<span>—</span>
|
||||
<span class="col-span-3">{formatDate(Week.nThDay(week, 6))}</span>
|
||||
</div>
|
||||
<button
|
||||
class="-mr-2 hover:text-zinc-100 disabled:text-zinc-700"
|
||||
disabled={!canNavigateForwad}
|
||||
on:click={() => {
|
||||
if (canNavigateForwad) {
|
||||
week = Week.next(week);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconChevronRight class="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="isolate flex flex-col flex-auto overflow-auto">
|
||||
<div class="h-4/5 overflow-auto flex flex-col flex-none max-w-full border-b border-zinc-700">
|
||||
<!-- sticky top -->
|
||||
<div
|
||||
class="overflow-hidden sticky top-0 z-30 bg-zinc-800 border-b border-zinc-700 flex-none px-8"
|
||||
>
|
||||
<div class="grid-cols-8 divide-x divide-zinc-700 grid">
|
||||
<div class="py-4" />
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Mon <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 0).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Tue <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 1).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Wed <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 2).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Thu <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 3).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Fri <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 4).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Sat <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 5).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<span
|
||||
>Sun <span class="items-center justify-center font-semibold"
|
||||
>{Week.nThDay(week, 6).getDate()}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-auto ">
|
||||
<div class="grid flex-auto grid-cols-1 grid-rows-1">
|
||||
<!-- hours y lines-->
|
||||
|
||||
<div
|
||||
class="text-zinc-500 col-start-1 col-end-2 row-start-1 grid-rows-1 grid grid-cols-8 px-8"
|
||||
>
|
||||
<div
|
||||
class="col-start-1 col-end-2 row-start-1 grid justify-end"
|
||||
style="grid-template-rows: repeat(24, minmax(1.5rem, 1fr));"
|
||||
>
|
||||
<div class="row-end-1 h-7" />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
12 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
2 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
4 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
6 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
8 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
10 AM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
12 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
2 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
4 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
6 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
8 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div class="z-20 -mt-2.5 -ml-14 w-14 pr-4 text-right leading-5 text-zinc-500">
|
||||
10 PM
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="text-zinc-500 col-start-1 col-end-2 row-start-1 grid-rows-1 grid grid-cols-8 ml-8 "
|
||||
>
|
||||
<div
|
||||
class="col-start-2 col-end-9 row-start-1 grid divide-y divide-zinc-700/20"
|
||||
style="grid-template-rows: repeat(24, minmax(1.5rem, 1fr));"
|
||||
>
|
||||
<div class="row-end-1 h-7" />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="h-7 left-0 z-20 -mt-2.5 -ml-14 w-14 pr-2 text-right leading-5 text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
|
||||
<!-- day x lines -->
|
||||
<div
|
||||
class="col-start-1 col-end-2 row-start-1 grid-rows-1 divide-x divide-zinc-700/50 grid grid-cols-8 px-8"
|
||||
>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
|
||||
<!-- actual entries -->
|
||||
<ol
|
||||
class="col-start-1 col-end-2 row-start-1 grid grid-cols-8 px-8"
|
||||
style="grid-template-rows: 1.75rem repeat(96, minmax(0px, 1fr)) auto;"
|
||||
>
|
||||
{#each $sessionsInWeek as session}
|
||||
<WeekBlockEntry
|
||||
startTime={new Date(session.meta.startTimestampMs)}
|
||||
endTime={new Date(session.meta.startTimestampMs)}
|
||||
label={toHumanBranchName(session.meta.branch)}
|
||||
href="/projects/{$project?.id}/sessions/{session.id}/"
|
||||
/>
|
||||
{/each}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user