Compare commits
2 commits
0e72eb2a62
...
37e4cdda0b
Author | SHA1 | Date | |
---|---|---|---|
37e4cdda0b | |||
eb2187738b |
39 changed files with 1049 additions and 980 deletions
50
package.json
50
package.json
|
@ -17,42 +17,44 @@
|
|||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "^0.18.4",
|
||||
"@auth/sveltekit": "^0.5.0",
|
||||
"@mdi/js": "^7.3.67",
|
||||
"@prisma/client": "^5.7.0",
|
||||
"@auth/core": "^0.18.6",
|
||||
"@auth/sveltekit": "^0.5.3",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@prisma/client": "^5.8.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.3.1",
|
||||
"@playwright/test": "^1.40.1",
|
||||
"@sveltejs/adapter-node": "^2.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@faker-js/faker": "^8.4.0",
|
||||
"@playwright/test": "^1.41.1",
|
||||
"@sveltejs/adapter-node": "^2.1.2",
|
||||
"@sveltejs/kit": "^2.5.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/node": "^20.10.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"daisyui": "^4.4.19",
|
||||
"eslint": "^8.55.0",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/server": "^10.45.0",
|
||||
"@types/node": "^20.11.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"daisyui": "^4.6.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-no-relative-import-paths": "^1.5.3",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-nesting": "^12.0.1",
|
||||
"prettier": "^3.1.0",
|
||||
"postcss-nesting": "^12.0.2",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"prisma": "^5.7.0",
|
||||
"svelte": "^4.2.8",
|
||||
"svelte-check": "^3.6.2",
|
||||
"sveltekit-zero-api": "^0.15.7",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"prisma": "^5.8.1",
|
||||
"svelte": "^4.2.9",
|
||||
"svelte-check": "^3.6.3",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"trpc-sveltekit": "^3.5.22",
|
||||
"tslib": "^2.6.2",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.0",
|
||||
"vitest": "^1.0.0"
|
||||
"vite": "^5.0.12",
|
||||
"vitest": "^1.2.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
1248
pnpm-lock.yaml
1248
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,3 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "patients"
|
||||
ADD COLUMN "hidden" BOOLEAN NOT NULL DEFAULT FALSE;
|
|
@ -14,7 +14,7 @@ datasource db {
|
|||
|
||||
model Account {
|
||||
id Int @id @default(autoincrement())
|
||||
user_id Int @map("userId")
|
||||
user_id Int @map("userId")
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
|
@ -74,9 +74,13 @@ model Patient {
|
|||
room Room? @relation(fields: [room_id], references: [id], onDelete: SetNull)
|
||||
room_id Int?
|
||||
Entry Entry[]
|
||||
hidden Boolean @default(false)
|
||||
|
||||
created_at DateTime @default(now())
|
||||
|
||||
full_name String? @default(dbgenerated("((first_name || ' '::text) || last_name)")) @ignore
|
||||
|
||||
@@index([full_name(ops: raw("gin_trgm_ops"))], map: "patients_full_name", type: Gin)
|
||||
@@map("patients")
|
||||
}
|
||||
|
||||
|
@ -101,6 +105,8 @@ model Entry {
|
|||
|
||||
created_at DateTime @default(now())
|
||||
|
||||
tsvec Unsupported("tsvector")?
|
||||
|
||||
@@map("entries")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import { createZeroApi } from "sveltekit-zero-api/api";
|
||||
import type { GeneratedAPI } from "./sveltekit-zero-api";
|
||||
|
||||
const routes = createZeroApi({
|
||||
// eslint-disable-next-line no-console
|
||||
onError: async (err) => console.error("[API]", err),
|
||||
}) as GeneratedAPI;
|
||||
|
||||
export default routes.api;
|
|
@ -1,5 +1,3 @@
|
|||
import { prisma } from "$lib/server/prisma";
|
||||
import { PrismaAdapter } from "$lib/server/authAdapter";
|
||||
import { SvelteKitAuth } from "@auth/sveltekit";
|
||||
import Keycloak from "@auth/core/providers/keycloak";
|
||||
import {
|
||||
|
@ -9,6 +7,12 @@ import {
|
|||
} from "$env/static/private";
|
||||
import { redirect, type Handle } from "@sveltejs/kit";
|
||||
import { sequence } from "@sveltejs/kit/hooks";
|
||||
import { createTRPCHandle } from "trpc-sveltekit";
|
||||
|
||||
import { prisma } from "$lib/server/prisma";
|
||||
import { PrismaAdapter } from "$lib/server/authAdapter";
|
||||
import { createContext } from "$lib/server/trpc/context";
|
||||
import { router } from "$lib/server/trpc/router";
|
||||
|
||||
/**
|
||||
* Protect the application against unauthorized access.
|
||||
|
@ -41,5 +45,6 @@ export const handle = sequence(
|
|||
strategy: "jwt",
|
||||
},
|
||||
}),
|
||||
authorization
|
||||
authorization,
|
||||
createTRPCHandle({ router, createContext })
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ function mapAccount(account: Account): AdapterAccount {
|
|||
refresh_token: account.refresh_token ?? undefined,
|
||||
access_token: account.access_token ?? undefined,
|
||||
expires_at: account.expires_at ?? undefined,
|
||||
token_type: account.token_type ?? undefined,
|
||||
token_type: (account.token_type as Lowercase<string>) ?? undefined,
|
||||
scope: account.scope ?? undefined,
|
||||
id_token: account.id_token ?? undefined,
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
import { ErrorConflict, ErrorNotFound } from "$lib/shared/util/error";
|
||||
import { ZodError } from "zod";
|
||||
import {
|
||||
|
@ -67,3 +68,4 @@ export async function apiWrapUser<T>(
|
|||
return handleError(error);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@ import { prisma } from "$lib/server/prisma";
|
|||
import type {
|
||||
EntriesFilter,
|
||||
Entry,
|
||||
EntryExecution,
|
||||
EntryExecutionNew,
|
||||
EntryNew,
|
||||
EntryVersion,
|
||||
|
@ -10,7 +11,7 @@ import type {
|
|||
PaginationRequest,
|
||||
} from "$lib/shared/model";
|
||||
import { ErrorConflict } from "$lib/shared/util/error";
|
||||
import { mapEntry, mapVersion } from "./mapping";
|
||||
import { mapEntry, mapVersion, mapExecution } from "./mapping";
|
||||
import { QueryBuilder, parseSearchQuery } from "./util";
|
||||
|
||||
const USER_SELECT = { select: { id: true, name: true } };
|
||||
|
@ -36,14 +37,22 @@ export async function getEntry(id: number): Promise<Entry> {
|
|||
return mapEntry(entry);
|
||||
}
|
||||
|
||||
export async function getEntryHistory(id: number): Promise<EntryVersion[]> {
|
||||
const entries = await prisma.entryVersion.findMany({
|
||||
export async function getEntryVersions(id: number): Promise<EntryVersion[]> {
|
||||
const versions = await prisma.entryVersion.findMany({
|
||||
where: { entry_id: id },
|
||||
include: { author: USER_SELECT, category: true },
|
||||
orderBy: { created_at: "desc" },
|
||||
});
|
||||
return versions.map(mapVersion);
|
||||
}
|
||||
|
||||
return entries.map(mapVersion);
|
||||
export async function getEntryExecutions(id: number): Promise<EntryExecution[]> {
|
||||
const executions = await prisma.entryExecution.findMany({
|
||||
where: { entry_id: id },
|
||||
include: { author: USER_SELECT },
|
||||
orderBy: { created_at: "desc" },
|
||||
});
|
||||
return executions.map(mapExecution);
|
||||
}
|
||||
|
||||
export async function newEntry(author_id: number, entry: EntryNew): Promise<number> {
|
||||
|
@ -65,7 +74,8 @@ export async function newEntry(author_id: number, entry: EntryNew): Promise<numb
|
|||
export async function newEntryVersion(
|
||||
author_id: number,
|
||||
entry_id: number,
|
||||
version: EntryVersionNew
|
||||
version: EntryVersionNew,
|
||||
old_version_id: number | undefined = undefined
|
||||
): Promise<number> {
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const entry = await tx.entry.findUniqueOrThrow({
|
||||
|
@ -80,7 +90,7 @@ export async function newEntryVersion(
|
|||
const cver = entry.EntryVersion[0];
|
||||
|
||||
// Check if the entry has been updated by someone else
|
||||
if (version.old_version && (!cver || cver.id !== version.old_version)) {
|
||||
if (old_version_id && (!cver || cver.id !== old_version_id)) {
|
||||
throw new ErrorConflict("old version id does not match");
|
||||
}
|
||||
|
||||
|
@ -103,7 +113,8 @@ export async function newEntryVersion(
|
|||
export async function newEntryExecution(
|
||||
author_id: number,
|
||||
entry_id: number,
|
||||
execution: EntryExecutionNew
|
||||
execution: EntryExecutionNew,
|
||||
old_execution_id: number | null | undefined = undefined
|
||||
): Promise<number> {
|
||||
return prisma.$transaction(async (tx) => {
|
||||
const entry = await tx.entry.findUniqueOrThrow({
|
||||
|
@ -119,8 +130,8 @@ export async function newEntryExecution(
|
|||
|
||||
// Check if the execution has been updated by someone else
|
||||
if (
|
||||
(execution.old_execution && (!cex || cex.id !== execution.old_execution)) ||
|
||||
(execution.old_execution === null && cex)
|
||||
(old_execution_id && (!cex || cex.id !== old_execution_id)) ||
|
||||
(old_execution_id === null && cex)
|
||||
) {
|
||||
throw new ErrorConflict("old execution id does not match");
|
||||
}
|
||||
|
@ -164,6 +175,7 @@ export async function getEntries(
|
|||
p.first_name as patient_first_name,
|
||||
p.last_name as patient_last_name,
|
||||
p.age as patient_age,
|
||||
p.hidden as patient_hidden,
|
||||
p.created_at as patient_created_at,
|
||||
r.id as room_id,
|
||||
r.name as room_name,
|
||||
|
@ -259,6 +271,7 @@ join stations s on s.id = r.station_id`
|
|||
patient_first_name: string;
|
||||
patient_last_name: string;
|
||||
patient_age: number;
|
||||
patient_hidden: boolean;
|
||||
patient_created_at: Date;
|
||||
room_id: number;
|
||||
room_name: string;
|
||||
|
@ -281,6 +294,7 @@ join stations s on s.id = r.station_id`
|
|||
last_name: item.patient_last_name,
|
||||
created_at: item.patient_created_at,
|
||||
age: item.patient_age,
|
||||
hidden: item.patient_hidden,
|
||||
room: {
|
||||
id: item.room_id,
|
||||
name: item.room_name,
|
||||
|
|
|
@ -38,6 +38,7 @@ export function mapPatient(patient: DbPatientLn): Patient {
|
|||
first_name: patient.first_name,
|
||||
last_name: patient.last_name,
|
||||
created_at: patient.created_at,
|
||||
hidden: patient.hidden,
|
||||
age: patient.age,
|
||||
room: patient.room
|
||||
? {
|
||||
|
@ -73,7 +74,7 @@ export function mapVersion(version: DbEntryVersionLn): EntryVersion {
|
|||
};
|
||||
}
|
||||
|
||||
function mapExecution(execution: DbEntryExecutionLn): EntryExecution {
|
||||
export function mapExecution(execution: DbEntryExecutionLn): EntryExecution {
|
||||
return {
|
||||
id: execution.id,
|
||||
author: execution.author,
|
||||
|
|
|
@ -36,7 +36,10 @@ export async function deletePatient(id: number) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Hide a patient
|
||||
/** Hide/show a patient */
|
||||
export async function hidePatient(id: number, hidden: boolean) {
|
||||
await prisma.patient.update({ where: { id }, data: { hidden } });
|
||||
}
|
||||
|
||||
export async function getPatient(id: number): Promise<Patient> {
|
||||
const patient = await prisma.patient.findUniqueOrThrow({
|
||||
|
@ -53,7 +56,7 @@ export async function getPatients(
|
|||
pagination: PaginationRequest
|
||||
): Promise<Pagination<Patient>> {
|
||||
const qb = new QueryBuilder(
|
||||
`select p.id, p.first_name, p.last_name, p.created_at, p.age,
|
||||
`select p.id, p.first_name, p.last_name, p.created_at, p.age, p.hidden,
|
||||
r.id as room_id, r.name as room_name, s.id as station_id, s.name as station_name`,
|
||||
`from patients p
|
||||
join rooms r on r.id = p.room_id
|
||||
|
@ -66,6 +69,10 @@ export async function getPatients(
|
|||
qb.addOrderClause(`similarity(p.full_name, ${qvar}) desc`);
|
||||
}
|
||||
|
||||
if (filter.hidden !== undefined) {
|
||||
qb.addFilter("p.hidden", filter.hidden);
|
||||
}
|
||||
|
||||
qb.addFilterList("r.id", filter.room);
|
||||
qb.addFilterList("s.id", filter.station);
|
||||
qb.addOrderClause("p.created_at desc");
|
||||
|
@ -77,6 +84,7 @@ export async function getPatients(
|
|||
last_name: string;
|
||||
created_at: Date;
|
||||
age: number;
|
||||
hidden: boolean;
|
||||
room_id: number;
|
||||
room_name: string;
|
||||
station_id: number;
|
||||
|
@ -96,6 +104,7 @@ export async function getPatients(
|
|||
last_name: patient.last_name,
|
||||
age: patient.age,
|
||||
created_at: patient.created_at,
|
||||
hidden: patient.hidden,
|
||||
room: {
|
||||
id: patient.room_id,
|
||||
name: patient.room_name,
|
||||
|
|
21
src/lib/server/trpc/context.ts
Normal file
21
src/lib/server/trpc/context.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { ZUser } from "$lib/shared/model/validation";
|
||||
import type { RequestEvent } from "@sveltejs/kit";
|
||||
import { type inferAsyncReturnType, TRPCError } from "@trpc/server";
|
||||
|
||||
// we're not using the event parameter is this example,
|
||||
// hence the eslint-disable rule
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export async function createContext(event: RequestEvent) {
|
||||
const session = await event.locals.getSession();
|
||||
|
||||
if (!session?.user) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "no session" });
|
||||
}
|
||||
|
||||
const user = ZUser.parse(session?.user);
|
||||
return {
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
export type Context = inferAsyncReturnType<typeof createContext>;
|
4
src/lib/server/trpc/index.ts
Normal file
4
src/lib/server/trpc/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { initTRPC } from "@trpc/server";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export const t = initTRPC.context<Context>().create();
|
14
src/lib/server/trpc/router.ts
Normal file
14
src/lib/server/trpc/router.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { t } from ".";
|
||||
|
||||
import { categoryRouter } from "./routes/category";
|
||||
import { entryRouter } from "./routes/entry";
|
||||
|
||||
export const router = t.router({
|
||||
greeting: t.procedure.query(async () => {
|
||||
return `Hello tRPC v10 @ ${new Date().toLocaleTimeString()}`;
|
||||
}),
|
||||
category: categoryRouter,
|
||||
entry: entryRouter,
|
||||
});
|
||||
|
||||
export type Router = typeof router;
|
26
src/lib/server/trpc/routes/category.ts
Normal file
26
src/lib/server/trpc/routes/category.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { t } from "..";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
deleteCategory,
|
||||
getCategories,
|
||||
newCategory,
|
||||
updateCategory,
|
||||
} from "$lib/server/query";
|
||||
import { ZCategoryNew, ZEntityId } from "$lib/shared/model/validation";
|
||||
|
||||
export const categoryRouter = t.router({
|
||||
list: t.procedure.query(getCategories),
|
||||
create: t.procedure.input(ZCategoryNew).mutation(async (opts) => {
|
||||
const id = await newCategory(opts.input);
|
||||
return { id };
|
||||
}),
|
||||
update: t.procedure
|
||||
.input(z.object({ id: ZEntityId, category: ZCategoryNew.partial() }))
|
||||
.mutation(async (opts) => {
|
||||
await updateCategory(opts.input.id, opts.input.category);
|
||||
}),
|
||||
delete: t.procedure.input(ZEntityId).mutation(async (opts) => {
|
||||
await deleteCategory(opts.input);
|
||||
}),
|
||||
});
|
74
src/lib/server/trpc/routes/entry.ts
Normal file
74
src/lib/server/trpc/routes/entry.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { t } from "..";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
ZEntityId,
|
||||
ZEntriesFilter,
|
||||
ZEntryExecutionNew,
|
||||
ZEntryVersionNew,
|
||||
ZPagination,
|
||||
} from "$lib/shared/model/validation";
|
||||
import {
|
||||
getEntries,
|
||||
getEntry,
|
||||
getEntryExecutions,
|
||||
getEntryVersions,
|
||||
newEntryExecution,
|
||||
newEntryVersion,
|
||||
} from "$lib/server/query";
|
||||
|
||||
export const entryRouter = t.router({
|
||||
get: t.procedure.input(ZEntityId).query(async (opts) => {
|
||||
return getEntry(opts.input);
|
||||
}),
|
||||
list: t.procedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
filter: ZEntriesFilter,
|
||||
pagination: ZPagination,
|
||||
})
|
||||
.partial()
|
||||
)
|
||||
.query(async (opts) => {
|
||||
return getEntries(opts.input.filter || {}, opts.input.pagination || {});
|
||||
}),
|
||||
versions: t.procedure.input(ZEntityId).query(async (opts) => {
|
||||
return getEntryVersions(opts.input);
|
||||
}),
|
||||
executions: t.procedure.input(ZEntityId).query(async (opts) => {
|
||||
return getEntryExecutions(opts.input);
|
||||
}),
|
||||
newVersion: t.procedure
|
||||
.input(
|
||||
z.object({
|
||||
id: ZEntityId,
|
||||
version: ZEntryVersionNew,
|
||||
old_version_id: ZEntityId.optional(),
|
||||
})
|
||||
)
|
||||
.query(async (opts) => {
|
||||
return newEntryVersion(
|
||||
opts.ctx.user.id,
|
||||
opts.input.id,
|
||||
opts.input.version,
|
||||
opts.input.old_version_id
|
||||
);
|
||||
}),
|
||||
newExecution: t.procedure
|
||||
.input(
|
||||
z.object({
|
||||
id: ZEntityId,
|
||||
execution: ZEntryExecutionNew,
|
||||
old_execution_id: ZEntityId.nullable().optional(),
|
||||
})
|
||||
)
|
||||
.query(async (opts) => {
|
||||
return newEntryExecution(
|
||||
opts.ctx.user.id,
|
||||
opts.input.id,
|
||||
opts.input.execution,
|
||||
opts.input.old_execution_id
|
||||
);
|
||||
}),
|
||||
});
|
|
@ -63,6 +63,7 @@ export type Patient = {
|
|||
last_name: string;
|
||||
age: Option<number>;
|
||||
room: Option<Room>;
|
||||
hidden: boolean;
|
||||
created_at: Date;
|
||||
};
|
||||
|
||||
|
@ -103,10 +104,7 @@ export type EntryVersionNdata = {
|
|||
priority: boolean;
|
||||
};
|
||||
|
||||
export type EntryVersionNew = Partial<{
|
||||
old_version: number;
|
||||
}> &
|
||||
Partial<EntryVersionNdata>;
|
||||
export type EntryVersionNew = Partial<EntryVersionNdata>;
|
||||
|
||||
export type EntryExecution = {
|
||||
id: number;
|
||||
|
@ -115,8 +113,6 @@ export type EntryExecution = {
|
|||
created_at: Date;
|
||||
};
|
||||
|
||||
export type EntryExecutionNew = Partial<{
|
||||
old_execution: number | null;
|
||||
}> & {
|
||||
export type EntryExecutionNew = {
|
||||
text: string;
|
||||
};
|
||||
|
|
|
@ -21,4 +21,5 @@ export type PatientsFilter = Partial<{
|
|||
search: string;
|
||||
station: FilterList<number>;
|
||||
room: FilterList<number>;
|
||||
hidden: boolean;
|
||||
}>;
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { expect, test } from "vitest";
|
||||
import { ZEntriesFilterUrl, ZPatientsFilterUrl } from "./validation";
|
||||
import type { EntriesFilter, PatientsFilter } from ".";
|
||||
|
||||
test("valid EntriesFilterUrl", () => {
|
||||
const data = ZEntriesFilterUrl.parse({
|
||||
done: "0",
|
||||
});
|
||||
|
||||
expect(data).toStrictEqual({
|
||||
done: false,
|
||||
} satisfies EntriesFilter);
|
||||
});
|
||||
|
||||
test("valid PatientsFilterUrl", () => {
|
||||
const data = ZPatientsFilterUrl.parse({
|
||||
room: "1;2;3;4",
|
||||
station: "5",
|
||||
});
|
||||
|
||||
expect(data).toStrictEqual({
|
||||
room: [1, 2, 3, 4],
|
||||
station: [5],
|
||||
} satisfies PatientsFilter);
|
||||
});
|
|
@ -10,12 +10,19 @@ import type {
|
|||
PatientNew,
|
||||
RoomNew,
|
||||
StationNew,
|
||||
User,
|
||||
} from ".";
|
||||
|
||||
const ZEntityId = z.number().int().nonnegative();
|
||||
export const ZEntityId = z.number().int().nonnegative();
|
||||
const ZNameString = z.string().min(1).max(200).trim();
|
||||
const ZTextString = z.string().trim();
|
||||
|
||||
export const ZUser = implement<User>().with({
|
||||
id: ZEntityId,
|
||||
name: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
});
|
||||
|
||||
export const ZStationNew = implement<StationNew>().with({ name: ZNameString });
|
||||
|
||||
export const ZRoomNew = implement<RoomNew>().with({
|
||||
|
@ -41,7 +48,6 @@ export const ZPatientNew = implement<PatientNew>().with({
|
|||
});
|
||||
|
||||
export const ZEntryVersionNew = implement<EntryVersionNew>().with({
|
||||
old_version: ZEntityId.optional(),
|
||||
text: ZTextString.optional(),
|
||||
date: z.date().optional(),
|
||||
category_id: ZEntityId.optional().nullable(),
|
||||
|
@ -59,41 +65,40 @@ export const ZEntryNew = implement<EntryNew>().with({
|
|||
});
|
||||
|
||||
export const ZEntryExecutionNew = implement<EntryExecutionNew>().with({
|
||||
old_execution: ZEntityId.optional().nullable(),
|
||||
text: ZTextString,
|
||||
});
|
||||
|
||||
// From URL
|
||||
const ZFilterListUrl = z
|
||||
.string()
|
||||
.regex(/^\d+(;\d+)*$/)
|
||||
.transform((s) => s.split(";").map(Number))
|
||||
.optional();
|
||||
|
||||
export const ZEntityIdUrl = z.coerce.number().int().nonnegative();
|
||||
|
||||
export const BooleanUrl = z
|
||||
.string()
|
||||
.transform((s) => s !== "0" && s.toLowerCase() !== "false");
|
||||
|
||||
export const ZPaginationUrl = implement<PaginationRequest>().with({
|
||||
limit: ZEntityIdUrl.optional(),
|
||||
offset: ZEntityIdUrl.optional(),
|
||||
export const ZPagination = implement<PaginationRequest>().with({
|
||||
limit: ZEntityId.optional(),
|
||||
offset: ZEntityId.optional(),
|
||||
});
|
||||
|
||||
export const ZEntriesFilterUrl = z.object({
|
||||
author: ZFilterListUrl,
|
||||
category: ZFilterListUrl,
|
||||
done: BooleanUrl.optional(),
|
||||
executor: ZFilterListUrl,
|
||||
patient: ZFilterListUrl,
|
||||
priority: BooleanUrl.optional(),
|
||||
room: ZFilterListUrl,
|
||||
search: z.string().optional(),
|
||||
station: ZFilterListUrl,
|
||||
});
|
||||
// const ZFilterList = z
|
||||
// .string()
|
||||
// .regex(/^\d+(;\d+)*$/)
|
||||
// .transform((s) => s.split(";").map(Number))
|
||||
// .optional();
|
||||
const ZFilterList = z.array(ZEntityId).or(ZEntityId);
|
||||
|
||||
export const ZPatientsFilterUrl = z.object({
|
||||
room: ZFilterListUrl,
|
||||
station: ZFilterListUrl,
|
||||
});
|
||||
export const ZEntriesFilter = z
|
||||
.object({
|
||||
author: ZFilterList,
|
||||
category: ZFilterList,
|
||||
done: z.boolean(),
|
||||
executor: ZFilterList,
|
||||
patient: ZFilterList,
|
||||
priority: z.boolean(),
|
||||
room: ZFilterList,
|
||||
search: z.string(),
|
||||
station: ZFilterList,
|
||||
})
|
||||
.partial();
|
||||
|
||||
export const ZPatientsFilterUrl = z
|
||||
.object({
|
||||
search: z.string(),
|
||||
room: ZFilterList,
|
||||
station: ZFilterList,
|
||||
hidden: z.boolean(),
|
||||
})
|
||||
.partial();
|
||||
|
|
16
src/lib/shared/trpc.ts
Normal file
16
src/lib/shared/trpc.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import type { Router } from "$lib/server/trpc/router";
|
||||
import { createTRPCClient, type TRPCClientInit } from "trpc-sveltekit";
|
||||
|
||||
let browserClient: ReturnType<typeof createTRPCClient<Router>>;
|
||||
|
||||
/** Get a new tRPC client
|
||||
*
|
||||
* This function switches between calling the tRPC server directly and using the TRPC
|
||||
* HTTP client depending on the side it is called on. */
|
||||
export function trpc(init?: TRPCClientInit) {
|
||||
const isBrowser = typeof window !== "undefined";
|
||||
if (isBrowser && browserClient) return browserClient;
|
||||
const client = createTRPCClient<Router>({ init });
|
||||
if (isBrowser) browserClient = client;
|
||||
return client;
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import api from "$api";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
api.category
|
||||
.id$(1)
|
||||
.GET()
|
||||
.Ok((resp) => console.log(resp.body));
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<h1 class="text-4xl">Planung</h1>
|
||||
|
||||
<p>{data.greeting}</p>
|
||||
<p>{JSON.stringify(data.categories)}</p>
|
||||
|
|
11
src/routes/(app)/plan/+page.ts
Normal file
11
src/routes/(app)/plan/+page.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { trpc } from "$lib/shared/trpc";
|
||||
import type { PageLoad } from "./$types";
|
||||
|
||||
export const load: PageLoad = async (event) => {
|
||||
const [greeting, categories] = await Promise.all([
|
||||
trpc(event).greeting.query(),
|
||||
trpc(event).category.list.query(),
|
||||
]);
|
||||
|
||||
return { greeting, categories };
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
import { getCategories, newCategory } from "$lib/server/query";
|
||||
import type { ApiBody, CategoryNew } from "$lib/shared/model";
|
||||
import { ZCategoryNew } from "$lib/shared/model/validation";
|
||||
import { apiWrap } from "$lib/server/handleError";
|
||||
|
||||
/** Get all categories */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(getCategories);
|
||||
};
|
||||
|
||||
/** Create a new category */
|
||||
export const POST = async (event: KitEvent<ApiBody<CategoryNew>, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const category = ZCategoryNew.parse(await event.request.json());
|
||||
const id = await newCategory(category);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { deleteCategory, getCategory, updateCategory } from "$lib/server/query";
|
||||
import { ZCategoryNew, ZEntityIdUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { CategoryNew, ApiBody } from "$lib/shared/model";
|
||||
|
||||
/** Get a category */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return await getCategory(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** Update a category */
|
||||
export const PATCH = async (
|
||||
event: KitEvent<ApiBody<Partial<CategoryNew>>, RequestEvent>
|
||||
) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const category = ZCategoryNew.partial().parse(await event.request.json());
|
||||
await updateCategory(id, category);
|
||||
return { id };
|
||||
});
|
||||
};
|
||||
|
||||
/** Delete a category */
|
||||
export const DELETE = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
await deleteCategory(id);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
import { getEntries } from "$lib/server/query";
|
||||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { ZEntriesFilterUrl, ZPaginationUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
/** Get entries */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const params = Object.fromEntries(event.url.searchParams);
|
||||
const filter = ZEntriesFilterUrl.parse(params);
|
||||
const pagination = ZPaginationUrl.parse(params);
|
||||
return await getEntries(filter, pagination);
|
||||
});
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { getEntry } from "$lib/server/query";
|
||||
import { ZEntityIdUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
/** Get a entry */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return getEntry(id);
|
||||
});
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, EntryExecutionNew } from "$lib/shared/model";
|
||||
import { apiWrapUser } from "$lib/server/handleError";
|
||||
import { ZEntityIdUrl, ZEntryExecutionNew } from "$lib/shared/model/validation";
|
||||
import { newEntryExecution } from "$lib/server/query";
|
||||
|
||||
/** Create an entry execution */
|
||||
export const POST = async (
|
||||
event: KitEvent<ApiBody<EntryExecutionNew>, RequestEvent>
|
||||
) => {
|
||||
return apiWrapUser(event, async (uId) => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const execution = ZEntryExecutionNew.parse(await event.request.json());
|
||||
const newExecutionId = await newEntryExecution(uId, id, execution);
|
||||
return { id, newExecutionId };
|
||||
});
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
import { apiWrap, apiWrapUser } from "$lib/server/handleError";
|
||||
import { getEntryHistory, newEntryVersion } from "$lib/server/query";
|
||||
import { ZEntityIdUrl, ZEntryVersionNew } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, EntryVersionNew } from "$lib/shared/model";
|
||||
|
||||
/** Get all versions of an entry */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return getEntryHistory(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** Create an entry version */
|
||||
export const PATCH = async (
|
||||
event: KitEvent<ApiBody<Partial<EntryVersionNew>>, RequestEvent>
|
||||
) => {
|
||||
return apiWrapUser(event, async (uId) => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const version = ZEntryVersionNew.parse(await event.request.json());
|
||||
const newVersionId = await newEntryVersion(uId, id, version);
|
||||
return { id, newVersionId };
|
||||
});
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { getPatients } from "$lib/server/query";
|
||||
import { ZPaginationUrl, ZPatientsFilterUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
/** Get patients */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const params = Object.fromEntries(event.url.searchParams);
|
||||
const filter = ZPatientsFilterUrl.parse(params);
|
||||
const pagination = ZPaginationUrl.parse(params);
|
||||
return await getPatients(filter, pagination);
|
||||
});
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { deletePatient, getPatient, updatePatient } from "$lib/server/query";
|
||||
import { ZEntityIdUrl, ZPatientNew } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, PatientNew } from "$lib/shared/model";
|
||||
|
||||
/** Get a patient */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return await getPatient(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** Update a patient */
|
||||
export const PATCH = async (
|
||||
event: KitEvent<ApiBody<Partial<PatientNew>>, RequestEvent>
|
||||
) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const patient = ZPatientNew.partial().parse(await event.request.json());
|
||||
await updatePatient(id, patient);
|
||||
return { id };
|
||||
});
|
||||
};
|
||||
|
||||
/** Delete a patient */
|
||||
export const DELETE = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
await deletePatient(id);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { getRooms, newRoom } from "$lib/server/query";
|
||||
import { apiWrap } from "$lib/server/handleError";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, RoomNew } from "$lib/shared/model";
|
||||
import { ZRoomNew } from "$lib/shared/model/validation";
|
||||
|
||||
/** Get all rooms */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(getRooms);
|
||||
};
|
||||
|
||||
/** Create a new room */
|
||||
export const POST = async (event: KitEvent<ApiBody<RoomNew>, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const room = ZRoomNew.parse(await event.request.json());
|
||||
const id = await newRoom(room);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { getRoom, updateRoom, deleteRoom } from "$lib/server/query";
|
||||
import { ZRoomNew, ZEntityIdUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, RoomNew } from "$lib/shared/model";
|
||||
|
||||
/** Get a room */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return await getRoom(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** Update a room */
|
||||
export const PATCH = async (
|
||||
event: KitEvent<ApiBody<Partial<RoomNew>>, RequestEvent>
|
||||
) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const room = ZRoomNew.partial().parse(await event.request.json());
|
||||
await updateRoom(id, room);
|
||||
return { id };
|
||||
});
|
||||
};
|
||||
|
||||
/** Delete a room */
|
||||
export const DELETE = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
await deleteRoom(id);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { getRooms, newRoom } from "$lib/server/query";
|
||||
import { apiWrap } from "$lib/server/handleError";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, RoomNew } from "$lib/shared/model";
|
||||
import { ZRoomNew } from "$lib/shared/model/validation";
|
||||
|
||||
/** Get all rooms */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(getRooms);
|
||||
};
|
||||
|
||||
/** Create a new room */
|
||||
export const POST = async (event: KitEvent<ApiBody<RoomNew>, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const room = ZRoomNew.parse(await event.request.json());
|
||||
const id = await newRoom(room);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { deleteStation, getStation, updateStation } from "$lib/server/query";
|
||||
import { ZEntityIdUrl, ZStationNew } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
import type { ApiBody, StationNew } from "$lib/shared/model";
|
||||
|
||||
/** Get a station */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
return await getStation(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** Update a station */
|
||||
export const PATCH = async (
|
||||
event: KitEvent<ApiBody<Partial<StationNew>>, RequestEvent>
|
||||
) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
const station = ZStationNew.partial().parse(await event.request.json());
|
||||
await updateStation(id, station);
|
||||
return { id };
|
||||
});
|
||||
};
|
||||
|
||||
/** Delete a station */
|
||||
export const DELETE = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const id = ZEntityIdUrl.parse(event.params.id);
|
||||
await deleteStation(id);
|
||||
return { id };
|
||||
});
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
import { apiWrap } from "$lib/server/handleError";
|
||||
import { getUsers } from "$lib/server/query";
|
||||
import { ZPaginationUrl } from "$lib/shared/model/validation";
|
||||
import type { KitEvent } from "sveltekit-zero-api";
|
||||
import type { RequestEvent } from "./$types";
|
||||
|
||||
/** Get users */
|
||||
export const GET = async (event: KitEvent<object, RequestEvent>) => {
|
||||
return apiWrap(async () => {
|
||||
const params = Object.fromEntries(event.url.searchParams);
|
||||
const pagination = ZPaginationUrl.parse(params);
|
||||
return await getUsers(pagination);
|
||||
});
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
getEntries,
|
||||
getEntry,
|
||||
getEntryHistory,
|
||||
getEntryVersions,
|
||||
newEntry,
|
||||
newEntryExecution,
|
||||
newEntryVersion,
|
||||
|
@ -59,7 +59,7 @@ test("create entry version", async () => {
|
|||
};
|
||||
expect(entry.current_version).toMatchObject(expectedVersion);
|
||||
|
||||
const history = await getEntryHistory(eId);
|
||||
const history = await getEntryVersions(eId);
|
||||
expect(history).length(2);
|
||||
expect(history[0]).toMatchObject(expectedVersion);
|
||||
expect(history[1].text).toBe(TEST_VERSION.text);
|
||||
|
@ -74,10 +74,7 @@ test("create entry version (partial)", async () => {
|
|||
const oldEntry = await getEntry(eId);
|
||||
const text = "10ml Blut abnehmen\n\nPS: Nadel nicht vergessen";
|
||||
|
||||
await newEntryVersion(2, eId, {
|
||||
old_version: oldEntry.current_version.id,
|
||||
text,
|
||||
});
|
||||
await newEntryVersion(2, eId, { text }, oldEntry.current_version.id);
|
||||
|
||||
const entry = await getEntry(eId);
|
||||
expect(entry.current_version).toMatchObject({
|
||||
|
@ -97,10 +94,12 @@ test("create entry version (wrong old vid)", async () => {
|
|||
const entry = await getEntry(eId);
|
||||
|
||||
expect(async () => {
|
||||
await newEntryVersion(1, eId, {
|
||||
old_version: entry.current_version.id + 1,
|
||||
text: "Hello World",
|
||||
});
|
||||
await newEntryVersion(
|
||||
1,
|
||||
eId,
|
||||
{ text: "Hello World" },
|
||||
entry.current_version.id + 1
|
||||
);
|
||||
}).rejects.toThrowError(new ErrorConflict("old version id does not match"));
|
||||
});
|
||||
|
||||
|
@ -111,7 +110,7 @@ test("create entry execution", async () => {
|
|||
});
|
||||
const text = "Blutabnahme erledigt.";
|
||||
|
||||
const xId = await newEntryExecution(1, eId, { old_execution: null, text });
|
||||
const xId = await newEntryExecution(1, eId, { text }, null);
|
||||
|
||||
const entry = await getEntry(eId);
|
||||
expect(entry.execution?.id).toBe(xId);
|
||||
|
@ -125,8 +124,8 @@ test("create entry execution (update)", async () => {
|
|||
version: TEST_VERSION,
|
||||
});
|
||||
|
||||
const x1 = await newEntryExecution(1, eId, { old_execution: null, text: "x1" });
|
||||
const x2 = await newEntryExecution(2, eId, { old_execution: x1, text: "x2" });
|
||||
const x1 = await newEntryExecution(1, eId, { text: "x1" }, null);
|
||||
const x2 = await newEntryExecution(2, eId, { text: "x2" }, x1);
|
||||
|
||||
const entry = await getEntry(eId);
|
||||
expect(entry.execution?.id).toBe(x2);
|
||||
|
@ -139,10 +138,10 @@ test("create entry execution (wrong old xid)", async () => {
|
|||
patient_id: 1,
|
||||
version: TEST_VERSION,
|
||||
});
|
||||
const x1 = await newEntryExecution(1, eId, { old_execution: null, text: "x1" });
|
||||
const x1 = await newEntryExecution(1, eId, { text: "x1" }, null);
|
||||
|
||||
expect(
|
||||
async () => await newEntryExecution(1, eId, { old_execution: x1 + 1, text: "x2" })
|
||||
async () => await newEntryExecution(1, eId, { text: "x2" }, x1 + 1)
|
||||
).rejects.toThrowError(new ErrorConflict("old execution id does not match"));
|
||||
});
|
||||
|
||||
|
@ -239,6 +238,12 @@ test("get entries", async () => {
|
|||
expect(entriesDone.items[0].id).toBe(eId3);
|
||||
expect(entriesDone.items[1].id).toBe(eId2);
|
||||
|
||||
// Filter not done
|
||||
const entriesNotDone = await getEntries({ done: false }, {});
|
||||
expect(entriesNotDone.items).length(1);
|
||||
expect(entriesNotDone.total).toBe(1);
|
||||
expect(entriesNotDone.items[0].id).toBe(eId1);
|
||||
|
||||
// Filter by priority
|
||||
const entriesPrio = await getEntries({ priority: true }, {});
|
||||
expect(entriesPrio.items).length(1);
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
updatePatient,
|
||||
deletePatient,
|
||||
newEntry,
|
||||
hidePatient,
|
||||
} from "$lib/server/query";
|
||||
import { expect, test } from "vitest";
|
||||
import { S1, S2 } from "$tests/helpers/testdata";
|
||||
|
@ -65,6 +66,16 @@ test("delete patient (restricted)", async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("hide patient", async () => {
|
||||
await hidePatient(1, true);
|
||||
let patient = await getPatient(1);
|
||||
expect(patient.hidden).toBe(true);
|
||||
|
||||
await hidePatient(1, false);
|
||||
patient = await getPatient(1);
|
||||
expect(patient.hidden).toBe(false);
|
||||
});
|
||||
|
||||
test("get patients", async () => {
|
||||
const patients = await getPatients({}, {});
|
||||
expect(patients).toMatchObject({
|
||||
|
@ -118,6 +129,18 @@ test("get patients (by station)", async () => {
|
|||
expect(patients.items[1].id).toBe(2);
|
||||
});
|
||||
|
||||
test("get patients (hidden)", async () => {
|
||||
await hidePatient(2, true);
|
||||
|
||||
let patients = await getPatients({ hidden: false }, {});
|
||||
expect(patients.items).length(2);
|
||||
expect(patients.items[0].id).toBe(1);
|
||||
|
||||
patients = await getPatients({ hidden: true }, {});
|
||||
expect(patients.items).length(1);
|
||||
expect(patients.items[0].id).toBe(2);
|
||||
});
|
||||
|
||||
test("search patients", async () => {
|
||||
const patients = await getPatients({ search: "Schustr" }, {});
|
||||
expect(patients.items).length(1);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { sveltekit } from "@sveltejs/kit/vite";
|
||||
import { defineConfig } from "vitest/config";
|
||||
import { zeroAPI } from "sveltekit-zero-api";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), zeroAPI()],
|
||||
plugins: [sveltekit()],
|
||||
test: {
|
||||
include: ["src/**/*.{test,spec}.{js,ts}"],
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue