Compare commits
No commits in common. "9e7aa15cedc99fbc25b7b521f70aaef5e57988d6" and "22b74987e53038dd1a9623584545b49e6a0da787" have entirely different histories.
9e7aa15ced
...
22b74987e5
14 changed files with 69 additions and 317 deletions
|
@ -33,6 +33,7 @@ button {
|
||||||
.card2 {
|
.card2 {
|
||||||
@apply bg-base-200;
|
@apply bg-base-200;
|
||||||
@apply rounded-xl;
|
@apply rounded-xl;
|
||||||
|
@apply mb-8;
|
||||||
@apply flex flex-col;
|
@apply flex flex-col;
|
||||||
@apply border-solid border-base-content/30 border-[1px];
|
@apply border-solid border-base-content/30 border-[1px];
|
||||||
|
|
||||||
|
@ -50,14 +51,10 @@ button {
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-light {
|
.c-light {
|
||||||
@apply bg-base-content/20 py-1;
|
@apply bg-base-content/20;
|
||||||
}
|
|
||||||
|
|
||||||
.c-vlight {
|
|
||||||
@apply bg-base-content/10 py-1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-primary {
|
.c-primary {
|
||||||
@apply bg-primary text-primary-content py-1;
|
@apply bg-primary text-primary-content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
export let backHref: string | undefined = undefined;
|
export let backHref: string | undefined = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-row">
|
<div class="mb-4 flex flex-row">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
{#if backHref}
|
{#if backHref}
|
||||||
<a href={backHref} class="btn btn-sm btn-circle btn-ghost">
|
<a href={backHref} class="btn btn-sm btn-circle btn-ghost">
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
<!-- Button showing the amount of entry versions -->
|
|
||||||
<script lang="ts">
|
|
||||||
import { mdiHistory } from "@mdi/js";
|
|
||||||
import Icon from "./Icon.svelte";
|
|
||||||
|
|
||||||
export let n: number;
|
|
||||||
export let href: string;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if n > 1}
|
|
||||||
<a {href} class="btn btn-xs btn-primary rounded-full">
|
|
||||||
<Icon path={mdiHistory} size={1.2} />
|
|
||||||
<span>{n}</span>
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
|
@ -39,14 +39,6 @@ export async function getEntry(id: number): Promise<Entry> {
|
||||||
return mapEntry(entry);
|
return mapEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEntryNVersions(id: number): Promise<number> {
|
|
||||||
return prisma.entryVersion.count({ where: { entry_id: id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getEntryNExecutions(id: number): Promise<number> {
|
|
||||||
return prisma.entryExecution.count({ where: { entry_id: id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getEntryVersions(id: number): Promise<EntryVersion[]> {
|
export async function getEntryVersions(id: number): Promise<EntryVersion[]> {
|
||||||
const versions = await prisma.entryVersion.findMany({
|
const versions = await prisma.entryVersion.findMany({
|
||||||
where: { entry_id: id },
|
where: { entry_id: id },
|
||||||
|
|
|
@ -12,28 +12,19 @@ import {
|
||||||
getEntries,
|
getEntries,
|
||||||
getEntry,
|
getEntry,
|
||||||
getEntryExecutions,
|
getEntryExecutions,
|
||||||
getEntryNExecutions,
|
|
||||||
getEntryNVersions,
|
|
||||||
getEntryVersions,
|
getEntryVersions,
|
||||||
newEntry,
|
newEntry,
|
||||||
newEntryExecution,
|
newEntryExecution,
|
||||||
newEntryVersion,
|
newEntryVersion,
|
||||||
} from "$lib/server/query";
|
} from "$lib/server/query";
|
||||||
import { executionsDiff, versionsDiff } from "$lib/shared/util/diff";
|
import { versionsDiff } from "$lib/shared/util/diff";
|
||||||
|
|
||||||
const ZEntityId = fields.EntityId();
|
const ZEntityId = fields.EntityId();
|
||||||
|
|
||||||
export const entryRouter = t.router({
|
export const entryRouter = t.router({
|
||||||
get: t.procedure.input(ZEntityId).query(async (opts) =>
|
get: t.procedure
|
||||||
trpcWrap(async () => {
|
.input(ZEntityId)
|
||||||
const [entry, n_versions, n_executions] = await Promise.all([
|
.query(async (opts) => trpcWrap(async () => getEntry(opts.input))),
|
||||||
getEntry(opts.input),
|
|
||||||
getEntryNVersions(opts.input),
|
|
||||||
getEntryNExecutions(opts.input),
|
|
||||||
]);
|
|
||||||
return { ...entry, n_versions, n_executions };
|
|
||||||
})
|
|
||||||
),
|
|
||||||
list: t.procedure
|
list: t.procedure
|
||||||
.input(ZEntriesQuery)
|
.input(ZEntriesQuery)
|
||||||
.query(async (opts) =>
|
.query(async (opts) =>
|
||||||
|
@ -57,12 +48,6 @@ export const entryRouter = t.router({
|
||||||
executions: t.procedure
|
executions: t.procedure
|
||||||
.input(ZEntityId)
|
.input(ZEntityId)
|
||||||
.query(async (opts) => trpcWrap(async () => getEntryExecutions(opts.input))),
|
.query(async (opts) => trpcWrap(async () => getEntryExecutions(opts.input))),
|
||||||
executionsDiff: t.procedure.input(ZEntityId).query(async (opts) =>
|
|
||||||
trpcWrap(async () => {
|
|
||||||
const executions = await getEntryExecutions(opts.input);
|
|
||||||
return executionsDiff(executions);
|
|
||||||
})
|
|
||||||
),
|
|
||||||
create: t.procedure
|
create: t.procedure
|
||||||
.input(ZEntryNew)
|
.input(ZEntryNew)
|
||||||
.mutation(async (opts) =>
|
.mutation(async (opts) =>
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import { diffWords } from "diff";
|
import { diffWords } from "diff";
|
||||||
import type {
|
import type { EntryVersion, Category, Option, UserTag } from "$lib/shared/model";
|
||||||
EntryVersion,
|
|
||||||
EntryExecution,
|
|
||||||
Category,
|
|
||||||
Option,
|
|
||||||
UserTag,
|
|
||||||
} from "$lib/shared/model";
|
|
||||||
|
|
||||||
export type EntryVersionChange = {
|
export type EntryVersionChange = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -18,14 +12,6 @@ export type EntryVersionChange = {
|
||||||
priority?: boolean;
|
priority?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EntryExecutionChange = {
|
|
||||||
id: number;
|
|
||||||
author: UserTag;
|
|
||||||
created_at: Date;
|
|
||||||
|
|
||||||
text: Diff.Change[];
|
|
||||||
};
|
|
||||||
|
|
||||||
function newOrUndef<T>(o: T, n: T): T | undefined {
|
function newOrUndef<T>(o: T, n: T): T | undefined {
|
||||||
return o === n ? undefined : n;
|
return o === n ? undefined : n;
|
||||||
}
|
}
|
||||||
|
@ -58,29 +44,3 @@ export function versionsDiff(versions: EntryVersion[]): EntryVersionChange[] {
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function executionsDiff(executions: EntryExecution[]): EntryExecutionChange[] {
|
|
||||||
let prev = executions[executions.length - 1];
|
|
||||||
const changes: EntryExecutionChange[] = [
|
|
||||||
{
|
|
||||||
...prev,
|
|
||||||
text: diffWords("", prev.text),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = executions.length - 2; i >= 0; i--) {
|
|
||||||
const v = executions[i];
|
|
||||||
const text = diffWords(prev.text, v.text);
|
|
||||||
|
|
||||||
changes.push({
|
|
||||||
id: v.id,
|
|
||||||
author: v.author,
|
|
||||||
created_at: v.created_at,
|
|
||||||
text,
|
|
||||||
});
|
|
||||||
|
|
||||||
prev = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { expect, test, vi } from "vitest";
|
|
||||||
import { humanDate } from ".";
|
|
||||||
|
|
||||||
const MINUTE = 60000;
|
|
||||||
const HOUR = 3_600_000;
|
|
||||||
const DAY = 24 * HOUR;
|
|
||||||
|
|
||||||
test.each([
|
|
||||||
{ s: 0, txt: "jetzt gerade" },
|
|
||||||
{ s: -30 * MINUTE, txt: "vor 30 Minuten" },
|
|
||||||
{ s: -11 * HOUR, txt: "vor 11 Stunden" },
|
|
||||||
{ s: -DAY, txt: "gestern" },
|
|
||||||
{ s: -2.5 * DAY, txt: "vor 2 Tagen" },
|
|
||||||
{ s: -2.6 * DAY, txt: "vor 3 Tagen" },
|
|
||||||
{ s: -3.5 * DAY, txt: "vor 3 Tagen" },
|
|
||||||
{ s: -4 * DAY, txt: "am 28.12.2023, 12:00" },
|
|
||||||
|
|
||||||
{ s: 28 * MINUTE, txt: "in 28 Minuten" },
|
|
||||||
{ s: 8 * HOUR, txt: "in 8 Stunden" },
|
|
||||||
{ s: DAY, txt: "morgen" },
|
|
||||||
{ s: 2.6 * DAY, txt: "in 3 Tagen" },
|
|
||||||
{ s: 4 * DAY, txt: "am 05.01.2024, 12:00" },
|
|
||||||
])("humanDate", ({ s, txt }) => {
|
|
||||||
const mockDate = new Date(2024, 0, 1, 12, 0);
|
|
||||||
vi.setSystemTime(mockDate);
|
|
||||||
|
|
||||||
const dt = new Date(Number(mockDate) + s);
|
|
||||||
const res = humanDate(dt, true);
|
|
||||||
expect(res).toBe(txt);
|
|
||||||
|
|
||||||
vi.useRealTimers();
|
|
||||||
});
|
|
|
@ -4,19 +4,13 @@ import { TRPCClientError } from "@trpc/client";
|
||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
const LOCALE = "de-DE";
|
|
||||||
|
|
||||||
function coerceDate(date: Date | string): Date {
|
|
||||||
if (!(date instanceof Date)) {
|
|
||||||
return new Date(date);
|
|
||||||
}
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatDate(date: Date | string, time = false): string {
|
export function formatDate(date: Date | string, time = false): string {
|
||||||
const dt = coerceDate(date);
|
let dt = date;
|
||||||
|
if (!(dt instanceof Date)) {
|
||||||
|
dt = new Date(dt);
|
||||||
|
}
|
||||||
if (time) {
|
if (time) {
|
||||||
return dt.toLocaleString(LOCALE, {
|
return dt.toLocaleString("de-DE", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
|
@ -24,7 +18,7 @@ export function formatDate(date: Date | string, time = false): string {
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return dt.toLocaleDateString(LOCALE, {
|
return dt.toLocaleDateString("de-DE", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
|
@ -32,42 +26,6 @@ export function formatDate(date: Date | string, time = false): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MS_PER_DAY = 86400000;
|
|
||||||
|
|
||||||
function dateDiffInDays(a: Date, b: Date): number {
|
|
||||||
const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
|
||||||
const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
|
||||||
|
|
||||||
return Math.round((utc2 - utc1) / MS_PER_DAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function humanDate(date: Date | string, time = false): string {
|
|
||||||
const now = new Date();
|
|
||||||
const dt = coerceDate(date);
|
|
||||||
const threshold = 302400000; // 3.5 * 24 * 3_600_000
|
|
||||||
const diff = Number(dt) - Number(now); // pos: Future, neg: Past
|
|
||||||
if (Math.abs(diff) > threshold) return "am " + formatDate(date, time);
|
|
||||||
|
|
||||||
const intl = new Intl.RelativeTimeFormat(LOCALE);
|
|
||||||
|
|
||||||
const diffDays = dateDiffInDays(now, dt);
|
|
||||||
if (diffDays !== 0) {
|
|
||||||
if (diffDays === 1) return "morgen";
|
|
||||||
if (diffDays === -1) return "gestern";
|
|
||||||
return intl.format(diffDays, "day");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (time) {
|
|
||||||
const diffHours = Math.round(diff / 3_600_000);
|
|
||||||
if (diffHours !== 0) return intl.format(diffHours, "hour");
|
|
||||||
|
|
||||||
const diffMinutes = Math.round(diff / 60_000);
|
|
||||||
if (diffMinutes !== 0) return intl.format(diffMinutes, "minute");
|
|
||||||
}
|
|
||||||
|
|
||||||
return time ? "jetzt gerade" : "heute";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatBool(val: boolean): string {
|
export function formatBool(val: boolean): string {
|
||||||
return val ? "Ja" : "Nein";
|
return val ? "Ja" : "Nein";
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,6 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="max-w-[100vw] p-4 pb-8 flex flex-col gap-4">
|
<div class="max-w-[100vw] p-4 pb-8">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
import { formatDate, humanDate } from "$lib/shared/util";
|
import { formatDate } from "$lib/shared/util";
|
||||||
import UserField from "$lib/components/table/UserField.svelte";
|
import UserField from "$lib/components/table/UserField.svelte";
|
||||||
import RoomField from "$lib/components/table/RoomField.svelte";
|
import RoomField from "$lib/components/table/RoomField.svelte";
|
||||||
import CategoryField from "$lib/components/table/CategoryField.svelte";
|
import CategoryField from "$lib/components/table/CategoryField.svelte";
|
||||||
import Markdown from "$lib/components/ui/Markdown.svelte";
|
import Markdown from "$lib/components/ui/Markdown.svelte";
|
||||||
import Header from "$lib/components/ui/Header.svelte";
|
import Header from "$lib/components/ui/Header.svelte";
|
||||||
import Icon from "$lib/components/ui/Icon.svelte";
|
import Icon from "$lib/components/ui/Icon.svelte";
|
||||||
import { mdiPencil } from "@mdi/js";
|
import { mdiHistory } from "@mdi/js";
|
||||||
import VersionsButton from "$lib/components/ui/VersionsButton.svelte";
|
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
$: basePath = `/entry/${data.entry.id}`;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -26,13 +23,16 @@
|
||||||
{#if data.entry.current_version.priority}
|
{#if data.entry.current_version.priority}
|
||||||
<div class="badge ellipsis badge-warning">Priorität</div>
|
<div class="badge ellipsis badge-warning">Priorität</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Header>
|
|
||||||
|
|
||||||
<p class="text-sm flex flex-row gap-2">
|
<a
|
||||||
<span>Erstellt {humanDate(data.entry.created_at, true)}</span>
|
href="/entry/{data.entry.id}/versions"
|
||||||
<span>·</span>
|
class="btn btn-sm btn-primary ml-auto"
|
||||||
<span>Zu erledigen {humanDate(data.entry.current_version.date)}</span>
|
slot="rightBtn"
|
||||||
</p>
|
>
|
||||||
|
<Icon path={mdiHistory} />
|
||||||
|
<span class="hidden sm:inline">Versionen</span>
|
||||||
|
</a>
|
||||||
|
</Header>
|
||||||
|
|
||||||
<div class="card2">
|
<div class="card2">
|
||||||
<div class="row c-light text-sm">Patient</div>
|
<div class="row c-light text-sm">Patient</div>
|
||||||
|
@ -40,48 +40,28 @@
|
||||||
{#if data.entry.patient.room}
|
{#if data.entry.patient.room}
|
||||||
<RoomField room={data.entry.patient.room} />
|
<RoomField room={data.entry.patient.room} />
|
||||||
{/if}
|
{/if}
|
||||||
<a href="/patient/{data.entry.patient.id}">
|
|
||||||
{data.entry.patient.first_name}
|
{data.entry.patient.first_name}
|
||||||
{data.entry.patient.last_name}
|
{data.entry.patient.last_name}
|
||||||
({data.entry.patient.age})
|
({data.entry.patient.age})
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card2">
|
<div class="card2">
|
||||||
<div class="row c-light text-sm items-center justify-between">
|
<div class="row c-light text-sm">Beschreibung</div>
|
||||||
Beschreibung
|
|
||||||
<div>
|
|
||||||
<VersionsButton href="{basePath}/versions" n={data.entry.n_versions} />
|
|
||||||
<button class="btn btn-circle btn-sm btn-ghost">
|
|
||||||
<Icon path={mdiPencil} size={1.2} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p class="prose">
|
<p class="prose">
|
||||||
<Markdown src={data.entry.current_version.text} />
|
<Markdown src={data.entry.current_version.text} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row c-vlight text-sm">
|
|
||||||
Zuletzt bearbeitet am {formatDate(data.entry.current_version.created_at, true)} von
|
|
||||||
<UserField user={data.entry.current_version.author} filterName="author" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if data.entry.execution}
|
{#if data.entry.execution}
|
||||||
<div class="card2">
|
<div class="card2">
|
||||||
<div class="row c-light text-sm items-center justify-between">
|
<div class="row c-light text-sm">
|
||||||
<p>
|
<p>
|
||||||
Erledigt am {formatDate(data.entry.execution.created_at, true)} von
|
Erledigt am {formatDate(data.entry.execution.created_at, true)} von
|
||||||
<UserField user={data.entry.execution.author} filterName="executor" />:
|
<UserField user={data.entry.execution.author} filterName="executor" />:
|
||||||
</p>
|
</p>
|
||||||
<div>
|
|
||||||
<VersionsButton href="{basePath}/executions" n={data.entry.n_executions} />
|
|
||||||
<button class="btn btn-circle btn-xs btn-ghost">
|
|
||||||
<Icon path={mdiPencil} size={1.2} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p class="prose">
|
<p class="prose">
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import CategoryField from "$lib/components/table/CategoryField.svelte";
|
|
||||||
import UserField from "$lib/components/table/UserField.svelte";
|
|
||||||
import { formatBool, formatDate } from "$lib/shared/util";
|
|
||||||
import type { PageData } from "./$types";
|
|
||||||
import Header from "$lib/components/ui/Header.svelte";
|
|
||||||
import { page } from "$app/stores";
|
|
||||||
|
|
||||||
export let data: PageData;
|
|
||||||
$: entryId = $page.params.id;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Eintrag #{entryId} - Erledigt</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<Header title="Eintrag #{entryId} - Erledigt" backHref="/entry/{entryId}" />
|
|
||||||
|
|
||||||
{#each data.versions as version, i}
|
|
||||||
<div class="card2">
|
|
||||||
<div class="row c-light text-sm">
|
|
||||||
#{i + 1}
|
|
||||||
<UserField user={version.author} />, {formatDate(version.created_at, true)}
|
|
||||||
</div>
|
|
||||||
{#if version.text.length > 0}
|
|
||||||
<div class="row whitespace-pre-wrap">
|
|
||||||
{#each version.text as change}
|
|
||||||
<span class:added={change.added} class:removed={change.removed}>
|
|
||||||
{change.value}
|
|
||||||
</span>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
<style lang="postcss">
|
|
||||||
.added {
|
|
||||||
@apply bg-success/20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.removed {
|
|
||||||
@apply bg-error/20;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { ZUrlEntityId } from "$lib/shared/model/validation";
|
|
||||||
import { trpc } from "$lib/shared/trpc";
|
|
||||||
import { loadWrap } from "$lib/shared/util";
|
|
||||||
import type { PageLoad } from "./$types";
|
|
||||||
|
|
||||||
export const load: PageLoad = async (event) => {
|
|
||||||
const id = ZUrlEntityId.parse(event.params.id);
|
|
||||||
const versions = await loadWrap(async () =>
|
|
||||||
trpc(event).entry.executionsDiff.query(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return { versions };
|
|
||||||
};
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
<Header title="Eintrag #{entryId} - Versionen" backHref="/entry/{entryId}" />
|
<Header title="Eintrag #{entryId} - Versionen" backHref="/entry/{entryId}" />
|
||||||
|
|
||||||
{#each data.versions as version, i}
|
<div class="overflow-x-auto">
|
||||||
|
{#each data.versions as version}
|
||||||
<div class="card2">
|
<div class="card2">
|
||||||
<div class="row c-light text-sm">
|
<div class="row c-light text-sm">
|
||||||
#{i + 1}
|
|
||||||
<UserField user={version.author} />, {formatDate(version.created_at, true)}
|
<UserField user={version.author} />, {formatDate(version.created_at, true)}
|
||||||
</div>
|
</div>
|
||||||
{#if version.category}
|
{#if version.category}
|
||||||
|
@ -54,6 +54,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.row > div:first-child {
|
.row > div:first-child {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import {
|
import {
|
||||||
getEntries,
|
getEntries,
|
||||||
getEntry,
|
getEntry,
|
||||||
getEntryNExecutions,
|
|
||||||
getEntryNVersions,
|
|
||||||
getEntryVersions,
|
getEntryVersions,
|
||||||
newEntry,
|
newEntry,
|
||||||
newEntryExecution,
|
newEntryExecution,
|
||||||
|
@ -34,11 +32,6 @@ test("create entry", async () => {
|
||||||
expect(entry.current_version.text).toBe(TEST_VERSION.text);
|
expect(entry.current_version.text).toBe(TEST_VERSION.text);
|
||||||
expect(entry.current_version.date).toStrictEqual(TEST_VERSION.date);
|
expect(entry.current_version.date).toStrictEqual(TEST_VERSION.date);
|
||||||
expect(entry.current_version.priority).toBe(TEST_VERSION.priority);
|
expect(entry.current_version.priority).toBe(TEST_VERSION.priority);
|
||||||
|
|
||||||
const nVersions = await getEntryNVersions(eId);
|
|
||||||
expect(nVersions).toBe(1);
|
|
||||||
const nExecutions = await getEntryNExecutions(eId);
|
|
||||||
expect(nExecutions).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create entry version", async () => {
|
test("create entry version", async () => {
|
||||||
|
@ -72,9 +65,6 @@ test("create entry version", async () => {
|
||||||
expect(history[0]).toMatchObject(expectedVersion);
|
expect(history[0]).toMatchObject(expectedVersion);
|
||||||
expect(history[1].text).toBe(TEST_VERSION.text);
|
expect(history[1].text).toBe(TEST_VERSION.text);
|
||||||
expect(history[0].created_at).greaterThan(history[1].created_at);
|
expect(history[0].created_at).greaterThan(history[1].created_at);
|
||||||
|
|
||||||
const nVersions = await getEntryNVersions(eId);
|
|
||||||
expect(nVersions).toBe(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create entry version (partial)", async () => {
|
test("create entry version (partial)", async () => {
|
||||||
|
@ -127,9 +117,6 @@ test("create entry execution", async () => {
|
||||||
expect(entry.execution?.id).toBe(xId);
|
expect(entry.execution?.id).toBe(xId);
|
||||||
expect(entry.execution?.author.id).toBe(1);
|
expect(entry.execution?.author.id).toBe(1);
|
||||||
expect(entry.execution?.text).toBe(text);
|
expect(entry.execution?.text).toBe(text);
|
||||||
|
|
||||||
const nExecutions = await getEntryNExecutions(eId);
|
|
||||||
expect(nExecutions).toBe(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create entry execution (update)", async () => {
|
test("create entry execution (update)", async () => {
|
||||||
|
@ -145,9 +132,6 @@ test("create entry execution (update)", async () => {
|
||||||
expect(entry.execution?.id).toBe(x2);
|
expect(entry.execution?.id).toBe(x2);
|
||||||
expect(entry.execution?.text).toBe("x2");
|
expect(entry.execution?.text).toBe("x2");
|
||||||
expect(entry.execution?.author.id).toBe(2);
|
expect(entry.execution?.author.id).toBe(2);
|
||||||
|
|
||||||
const nExecutions = await getEntryNExecutions(eId);
|
|
||||||
expect(nExecutions).toBe(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create entry execution (wrong old xid)", async () => {
|
test("create entry execution (wrong old xid)", async () => {
|
||||||
|
|
Loading…
Reference in a new issue