Compare commits
	
		
			No commits in common. "9a6b8094dae44b3c1b861d671d54fb6495e85c9f" and "02ffd4a0138030c5f2b8548fbd4dc2875adea87c" have entirely different histories.
		
	
	
		
			
				9a6b8094da
			
			...
			
				02ffd4a013
			
		
	
		
					 21 changed files with 649 additions and 690 deletions
				
			
		|  | @ -14,16 +14,10 @@ module.exports = { | ||||||
|       parser: "svelte-eslint-parser", |       parser: "svelte-eslint-parser", | ||||||
|       parserOptions: { |       parserOptions: { | ||||||
|         parser: "@typescript-eslint/parser", |         parser: "@typescript-eslint/parser", | ||||||
|         svelteFeatures: { |  | ||||||
|           experimentalGenerics: true, |  | ||||||
|         }, |  | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
|   settings: {}, |   settings: {}, | ||||||
|   globals: { |  | ||||||
|     $$Generic: "readonly", |  | ||||||
|   }, |  | ||||||
|   parserOptions: { |   parserOptions: { | ||||||
|     sourceType: "module", |     sourceType: "module", | ||||||
|     ecmaVersion: 2020, |     ecmaVersion: 2020, | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -8,3 +8,5 @@ node_modules | ||||||
| !.env.example | !.env.example | ||||||
| vite.config.js.timestamp-* | vite.config.js.timestamp-* | ||||||
| vite.config.ts.timestamp-* | vite.config.ts.timestamp-* | ||||||
|  | sveltekit-zero-api.d.ts | ||||||
|  | /.vscode | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +0,0 @@ | ||||||
| { |  | ||||||
|   "eslint.validate": ["javascript", "typescript", "svelte"] |  | ||||||
| } |  | ||||||
							
								
								
									
										52
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										52
									
								
								package.json
									
										
									
									
									
								
							|  | @ -17,13 +17,13 @@ | ||||||
|     "test:e2e": "playwright test" |     "test:e2e": "playwright test" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@auth/core": "^0.28.2", |     "@auth/core": "^0.27.0", | ||||||
|     "@floating-ui/core": "^1.6.0", |     "@floating-ui/core": "^1.6.0", | ||||||
|     "@mdi/js": "^7.4.47", |     "@mdi/js": "^7.4.47", | ||||||
|     "@prisma/client": "^5.12.1", |     "@prisma/client": "^5.10.2", | ||||||
|     "diff": "^5.2.0", |     "diff": "^5.2.0", | ||||||
|     "isomorphic-dompurify": "^2.6.0", |     "isomorphic-dompurify": "^2.4.0", | ||||||
|     "marked": "^12.0.1", |     "marked": "^12.0.0", | ||||||
|     "set-cookie-parser": "^2.6.0", |     "set-cookie-parser": "^2.6.0", | ||||||
|     "svelte-floating-ui": "^1.5.8", |     "svelte-floating-ui": "^1.5.8", | ||||||
|     "zod": "^3.22.4", |     "zod": "^3.22.4", | ||||||
|  | @ -31,40 +31,40 @@ | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@faker-js/faker": "^8.4.1", |     "@faker-js/faker": "^8.4.1", | ||||||
|     "@playwright/test": "^1.42.1", |     "@playwright/test": "^1.42.0", | ||||||
|     "@sveltejs/adapter-node": "^5.0.1", |     "@sveltejs/adapter-node": "^4.0.1", | ||||||
|     "@sveltejs/kit": "^2.5.5", |     "@sveltejs/kit": "^2.5.2", | ||||||
|     "@sveltejs/vite-plugin-svelte": "^3.0.2", |     "@sveltejs/vite-plugin-svelte": "^3.0.2", | ||||||
|     "@tailwindcss/typography": "^0.5.12", |     "@tailwindcss/typography": "^0.5.10", | ||||||
|     "@trpc/client": "^10.45.2", |     "@trpc/client": "^10.45.1", | ||||||
|     "@trpc/server": "^10.45.2", |     "@trpc/server": "^10.45.1", | ||||||
|     "@types/diff": "^5.0.9", |     "@types/diff": "^5.0.9", | ||||||
|     "@types/node": "^20.12.4", |     "@types/node": "^20.11.21", | ||||||
|     "@types/set-cookie-parser": "^2.4.7", |     "@types/set-cookie-parser": "^2.4.7", | ||||||
|     "@typescript-eslint/eslint-plugin": "^7.5.0", |     "@typescript-eslint/eslint-plugin": "^7.1.0", | ||||||
|     "@typescript-eslint/parser": "^7.5.0", |     "@typescript-eslint/parser": "^7.1.0", | ||||||
|     "autoprefixer": "^10.4.19", |     "autoprefixer": "^10.4.17", | ||||||
|     "daisyui": "^4.9.0", |     "daisyui": "^4.7.2", | ||||||
|     "dotenv": "^16.4.5", |     "dotenv": "^16.4.5", | ||||||
|     "eslint": "^8.57.0", |     "eslint": "^8.57.0", | ||||||
|     "eslint-config-prettier": "^9.1.0", |     "eslint-config-prettier": "^9.1.0", | ||||||
|     "eslint-plugin-no-relative-import-paths": "^1.5.3", |     "eslint-plugin-no-relative-import-paths": "^1.5.3", | ||||||
|     "eslint-plugin-svelte": "^2.35.1", |     "eslint-plugin-svelte": "^2.35.1", | ||||||
|     "postcss-import": "^16.1.0", |     "postcss-import": "^16.0.1", | ||||||
|     "postcss-nesting": "^12.1.1", |     "postcss-nesting": "^12.0.4", | ||||||
|     "prettier": "^3.2.5", |     "prettier": "^3.2.5", | ||||||
|     "prettier-plugin-svelte": "^3.2.2", |     "prettier-plugin-svelte": "^3.2.2", | ||||||
|     "prisma": "^5.12.1", |     "prisma": "^5.10.2", | ||||||
|     "svelte": "^4.2.12", |     "svelte": "^4.2.12", | ||||||
|     "svelte-check": "^3.6.9", |     "svelte-check": "^3.6.5", | ||||||
|     "sveltekit-superforms": "^2.12.2", |     "sveltekit-superforms": "^2.6.2", | ||||||
|     "tailwindcss": "^3.4.3", |     "tailwindcss": "^3.4.1", | ||||||
|     "trpc-sveltekit": "^3.6.1", |     "trpc-sveltekit": "^3.5.27", | ||||||
|     "tslib": "^2.6.2", |     "tslib": "^2.6.2", | ||||||
|     "tsx": "^4.7.2", |     "tsx": "^4.7.1", | ||||||
|     "typescript": "^5.4.3", |     "typescript": "^5.3.3", | ||||||
|     "vite": "^5.2.8", |     "vite": "^5.1.4", | ||||||
|     "vitest": "^1.4.0" |     "vitest": "^1.3.1" | ||||||
|   }, |   }, | ||||||
|   "type": "module" |   "type": "module" | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										1109
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1109
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -10,46 +10,33 @@ | ||||||
|   import { createFloatingActions } from "svelte-floating-ui"; |   import { createFloatingActions } from "svelte-floating-ui"; | ||||||
|   import { shift } from "svelte-floating-ui/dom"; |   import { shift } from "svelte-floating-ui/dom"; | ||||||
|   import outclick from "$lib/actions/outclick"; |   import outclick from "$lib/actions/outclick"; | ||||||
|   import type { BaseItem } from "./types"; |   import type { BaseItem, SelectionOrText } from "./types"; | ||||||
|   import Icon from "$lib/components/ui/Icon.svelte"; |   import Icon from "$lib/components/ui/Icon.svelte"; | ||||||
|   import { onMount } from "svelte"; |  | ||||||
| 
 | 
 | ||||||
|   type T = $$Generic<BaseItem>; |  | ||||||
|   type OnSelectResult = { newValue: string; close: boolean }; |   type OnSelectResult = { newValue: string; close: boolean }; | ||||||
| 
 | 
 | ||||||
|   /** List of items to choose from (or an async function fetching them) */ |   export let items: BaseItem[] | (() => Promise<BaseItem[]>); | ||||||
|   export let items: T[] | (() => Promise<T[]>); |   export let selection: SelectionOrText | null = null; | ||||||
|   /** Current selection of the autocomplete field */ |  | ||||||
|   export let selection: T | null = null; |  | ||||||
|   /** Set of item IDs that should be hidden from the list */ |  | ||||||
|   export let hiddenIds: Set<string | number> = new Set(); |   export let hiddenIds: Set<string | number> = new Set(); | ||||||
|   /** Object to cache fetched items in */ |   export let cache: { [key: string]: BaseItem[] } = {}; | ||||||
|   export let cache: { [key: string]: T[] } = {}; |  | ||||||
|   /** Key in cache object under which fetched items are stored */ |  | ||||||
|   export let cacheKey: string | undefined = undefined; |   export let cacheKey: string | undefined = undefined; | ||||||
|   /** Input field placeholder */ |  | ||||||
|   export let placeholder: string | undefined = undefined; |   export let placeholder: string | undefined = undefined; | ||||||
|   /** Add horizontal padding to the input element */ |  | ||||||
|   export let padding = true; |   export let padding = true; | ||||||
|   /** CSS classes of the container */ |  | ||||||
|   export let cls = ""; |   export let cls = ""; | ||||||
|   /** CSS classes of the input element */ |  | ||||||
|   export let inputCls = "w-full bg-transparent outline-none"; |   export let inputCls = "w-full bg-transparent outline-none"; | ||||||
|   /** |   export let asTextInput = false; | ||||||
|    * Enable filter bar optimizations: |  | ||||||
|    * - Clear input on mount |  | ||||||
|    */ |  | ||||||
|   export let partOfFilterbar = false; |  | ||||||
|   /** Dont select item automatically on close if only 1 item is shown */ |   /** Dont select item automatically on close if only 1 item is shown */ | ||||||
|   export let noAutoselect1 = false; |   export let noAutoselect1 = false; | ||||||
|   /** Name of the form input. This sets a hidden field to the ID of the selected element |  | ||||||
|    * (to allow Svelte Superforms to work) */ |  | ||||||
|   export let idInputName: string | undefined = undefined; |   export let idInputName: string | undefined = undefined; | ||||||
|   /** Function to filter shown items */ |   export let filterFn: (item: BaseItem) => boolean = () => true; | ||||||
|   export let filterFn: (item: T) => boolean = () => true; |  | ||||||
| 
 | 
 | ||||||
|   /** Selection callback. Returns the new input value after selection */ |   /** Selection callback. Returns the new input value after selection */ | ||||||
|   export let onSelect: (item: T, kb: boolean) => OnSelectResult | void = () => {}; |   export let onSelect: (item: SelectionOrText, kb: boolean) => OnSelectResult = ( | ||||||
|  |     item, | ||||||
|  |     kb | ||||||
|  |   ) => { | ||||||
|  |     return { newValue: item.name ?? "", close: true }; | ||||||
|  |   }; | ||||||
|   export let onUnselect = () => {}; |   export let onUnselect = () => {}; | ||||||
|   export let onClose = (kb: boolean) => {}; |   export let onClose = (kb: boolean) => {}; | ||||||
|   export let onBackspace = () => {}; |   export let onBackspace = () => {}; | ||||||
|  | @ -57,11 +44,10 @@ | ||||||
|   let opened = false; |   let opened = false; | ||||||
|   let highlightIndex = 0; |   let highlightIndex = 0; | ||||||
|   let isLoading = false; |   let isLoading = false; | ||||||
|   let srcItems: T[]; |   let srcItems: BaseItem[]; | ||||||
|   let filteredItems: T[] = []; |   let filteredItems: BaseItem[] = []; | ||||||
| 
 | 
 | ||||||
|   // Reload search items if hidden ids are changed |   $: if (hiddenIds || selection) updateSearch(); | ||||||
|   $: if (srcItems && hiddenIds) updateSearch(); |  | ||||||
| 
 | 
 | ||||||
|   // HTML elements |   // HTML elements | ||||||
|   let inputElm: HTMLInputElement | undefined; |   let inputElm: HTMLInputElement | undefined; | ||||||
|  | @ -106,7 +92,7 @@ | ||||||
|         if (i !== -1) { |         if (i !== -1) { | ||||||
|           highlightIndex = i; |           highlightIndex = i; | ||||||
|         } |         } | ||||||
|         if (!partOfFilterbar) setInputValue(selection.name ?? ""); |         if (asTextInput) setInputValue(selection.name ?? ""); | ||||||
|       } else { |       } else { | ||||||
|         highlightIndex = 0; |         highlightIndex = 0; | ||||||
|         setInputValue(selection.name ?? ""); |         setInputValue(selection.name ?? ""); | ||||||
|  | @ -156,34 +142,30 @@ | ||||||
|     if (inputElm) inputElm.focus(); |     if (inputElm) inputElm.focus(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   export function close(kb = true) { |   export function close(kb: boolean) { | ||||||
|     if (opened) { |     if (opened) { | ||||||
|       onClose(kb); |       onClose(kb); | ||||||
|     } |     } | ||||||
|     opened = false; |     opened = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function selectListItem(item: T | undefined, kb: boolean) { |   function selectListItem(item: BaseItem | undefined, kb: boolean) { | ||||||
|     if (item) { |     selection = { id: item?.id, name: item?.name }; | ||||||
|       selection = item; |     const selRes = onSelect(item ?? { name: inputValue() }, kb); | ||||||
|       const selRes = onSelect(item, kb); |     setInputValue(selRes.newValue); | ||||||
|       setInputValue(selRes ? selRes.newValue : item.name); |     if (selRes.close) { | ||||||
|       if (!selRes || selRes.close) { |       close(kb); | ||||||
|         close(kb); |  | ||||||
|       } else { |  | ||||||
|         updateSearch(); |  | ||||||
|       } |  | ||||||
|     } else { |     } else { | ||||||
|       selection = null; |       updateSearch(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function onKeyDown(e: KeyboardEvent) { |   function onKeyDown(e: KeyboardEvent) { | ||||||
|     let key = e.key; |     let key = e.key; | ||||||
|     if (key === "Tab" && e.shiftKey) key = "ShiftTab"; |     if (key === "Tab" && e.shiftKey) key = "ShiftTab"; | ||||||
|     const fnmap: { [key: string]: () => void } = { |     const fnmap = { | ||||||
|       Tab: () => close, |       Tab: close, | ||||||
|       ShiftTab: () => close, |       ShiftTab: close, | ||||||
|       ArrowDown: () => { |       ArrowDown: () => { | ||||||
|         open(); |         open(); | ||||||
|         if (highlightIndex < filteredItems.length - 1) { |         if (highlightIndex < filteredItems.length - 1) { | ||||||
|  | @ -202,7 +184,7 @@ | ||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
|         if (opened) { |         if (opened) { | ||||||
|           if (inputElm) inputElm.focus(); |           if (inputElm) inputElm.focus(); | ||||||
|           close(); |           close(true); | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       Backspace: () => { |       Backspace: () => { | ||||||
|  | @ -213,9 +195,10 @@ | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|     }; |     }; | ||||||
|  |     // @ts-expect-error unknown map type | ||||||
|     const fn = fnmap[key]; |     const fn = fnmap[key]; | ||||||
|     if (typeof fn === "function") { |     if (typeof fn === "function") { | ||||||
|       fn(); |       fn(e); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -267,14 +250,6 @@ | ||||||
|     placement: "bottom-start", |     placement: "bottom-start", | ||||||
|     middleware: [shift()], |     middleware: [shift()], | ||||||
|   }); |   }); | ||||||
| 
 |  | ||||||
|   if (!partOfFilterbar) { |  | ||||||
|     onMount(() => { |  | ||||||
|       if (selection?.name) { |  | ||||||
|         setInputValue(selection.name); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <div class="flex-grow {cls}" use:outclick on:outclick={close}> | <div class="flex-grow {cls}" use:outclick on:outclick={close}> | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|   /** True if a separate search field should be displayed */ |   /** True if a separate search field should be displayed */ | ||||||
|   export let search = false; |   export let search = false; | ||||||
| 
 | 
 | ||||||
|   let autocomplete: Autocomplete<BaseItem> | undefined; |   let autocomplete: Autocomplete | undefined; | ||||||
|   let activeFilters: FilterData[] = []; |   let activeFilters: FilterData[] = []; | ||||||
|   let cache: { [key: string]: BaseItem[] } = {}; |   let cache: { [key: string]: BaseItem[] } = {}; | ||||||
|   let searchVal = ""; |   let searchVal = ""; | ||||||
|  | @ -216,7 +216,6 @@ | ||||||
|         activeFilters = activeFilters; |         activeFilters = activeFilters; | ||||||
|         updateFilter(); |         updateFilter(); | ||||||
|       }} |       }} | ||||||
|       partOfFilterbar |  | ||||||
|     /> |     /> | ||||||
|     <button |     <button | ||||||
|       class="btn btn-sm btn-circle btn-ghost absolute bottom-0 right-0" |       class="btn btn-sm btn-circle btn-ghost absolute bottom-0 right-0" | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
|   export let onRemove = () => {}; |   export let onRemove = () => {}; | ||||||
|   export let onSelection = (selection: SelectionOrText, kb: boolean) => {}; |   export let onSelection = (selection: SelectionOrText, kb: boolean) => {}; | ||||||
| 
 | 
 | ||||||
|   let autocomplete: Autocomplete<BaseItem> | undefined; |   let autocomplete: Autocomplete | undefined; | ||||||
| 
 | 
 | ||||||
|   function startEditing() { |   function startEditing() { | ||||||
|     fdata.editing = true; |     fdata.editing = true; | ||||||
|  | @ -76,9 +76,7 @@ gap-1 pl-1" | ||||||
|           hiddenIds={hids} |           hiddenIds={hids} | ||||||
|           {cache} |           {cache} | ||||||
|           cacheKey={filter.id} |           cacheKey={filter.id} | ||||||
|           selection={fdata.selection?.id |           selection={fdata.selection} | ||||||
|             ? { id: fdata.selection?.id, name: fdata.selection?.name ?? "" } |  | ||||||
|             : null} |  | ||||||
|           padding={false} |           padding={false} | ||||||
|           onSelect={(item) => { |           onSelect={(item) => { | ||||||
|             // Accept the selection if this is a free text field or the user selected a variant |             // Accept the selection if this is a free text field or the user selected a variant | ||||||
|  | @ -91,7 +89,6 @@ gap-1 pl-1" | ||||||
|           }} |           }} | ||||||
|           {onClose} |           {onClose} | ||||||
|           onBackspace={onRemove} |           onBackspace={onRemove} | ||||||
|           partOfFilterbar |  | ||||||
|         /> |         /> | ||||||
|       {:else} |       {:else} | ||||||
|         <!-- svelte-ignore a11y-autofocus --> |         <!-- svelte-ignore a11y-autofocus --> | ||||||
|  |  | ||||||
|  | @ -68,12 +68,16 @@ | ||||||
|         }} |         }} | ||||||
|         selection={patient?.room} |         selection={patient?.room} | ||||||
|         onSelect={(item) => { |         onSelect={(item) => { | ||||||
|  |           // @ts-expect-error room items have station attr | ||||||
|           station = item.station; |           station = item.station; | ||||||
|  |           // @ts-expect-error ids are always numeric | ||||||
|           $form.room_id = item.id; |           $form.room_id = item.id; | ||||||
|  |           return { newValue: item.name ?? "", close: true }; | ||||||
|         }} |         }} | ||||||
|         onUnselect={() => { |         onUnselect={() => { | ||||||
|           $form.room_id = null; |           $form.room_id = null; | ||||||
|         }} |         }} | ||||||
|  |         asTextInput | ||||||
|         idInputName="room_id" |         idInputName="room_id" | ||||||
|       /> |       /> | ||||||
|     </FormField> |     </FormField> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  |   import FormField from "./FormField.svelte"; | ||||||
|   import Markdown from "./Markdown.svelte"; |   import Markdown from "./Markdown.svelte"; | ||||||
|   import type { InputConstraint } from "sveltekit-superforms"; |   import type { InputConstraint } from "sveltekit-superforms"; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  |   import { goto } from "$app/navigation"; | ||||||
|   import { mdiChevronLeft, mdiChevronRight, mdiPageFirst, mdiPageLast } from "@mdi/js"; |   import { mdiChevronLeft, mdiChevronRight, mdiPageFirst, mdiPageLast } from "@mdi/js"; | ||||||
| 
 | 
 | ||||||
|   import type { Pagination, PaginationRequest } from "$lib/shared/model"; |   import type { Pagination, PaginationRequest } from "$lib/shared/model"; | ||||||
|  |  | ||||||
|  | @ -117,10 +117,10 @@ export async function newEntryVersion( | ||||||
| 
 | 
 | ||||||
|     // Check if there are any updates
 |     // Check if there are any updates
 | ||||||
|     if ( |     if ( | ||||||
|       cver?.text === updatedVersion.text && |       cver.text === updatedVersion.text && | ||||||
|       cver?.date.getTime() === updatedVersion.date.getTime() && |       cver.date.getTime() === updatedVersion.date.getTime() && | ||||||
|       cver?.category_id === updatedVersion.category_id && |       cver.category_id === updatedVersion.category_id && | ||||||
|       cver?.priority === updatedVersion.priority |       cver.priority === updatedVersion.priority | ||||||
|     ) { |     ) { | ||||||
|       return cver.id; |       return cver.id; | ||||||
|     } |     } | ||||||
|  | @ -160,7 +160,7 @@ export async function newEntryExecution( | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Check if there are any updates
 |     // Check if there are any updates
 | ||||||
|     if (execution.text === cex?.text) { |     if (execution.text === cex.text) { | ||||||
|       return cex.id; |       return cex.id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -87,18 +87,15 @@ class SearchQueryComponents { | ||||||
|  */ |  */ | ||||||
| export function parseSearchQuery(q: string): SearchQueryComponents { | export function parseSearchQuery(q: string): SearchQueryComponents { | ||||||
|   const regexpParts = /(-)?(?:"([^"]*)"|([^"\s]+))(?:\s|$)/g; |   const regexpParts = /(-)?(?:"([^"]*)"|([^"\s]+))(?:\s|$)/g; | ||||||
|   const components = Array.from( |   const components = Array.from(q.replaceAll("'", '"').matchAll(regexpParts), (m) => { | ||||||
|     q.replaceAll("'", '"').replaceAll("\\", "\\\\").matchAll(regexpParts), |     const negative = m[1] === "-"; | ||||||
|     (m) => { |     // Exact
 | ||||||
|       const negative = m[1] === "-"; |     if (m[2]) { | ||||||
|       // Exact
 |       return new SearchQueryComponent(m[2], QueryComponentType.Exact, negative); | ||||||
|       if (m[2]) { |     } else { | ||||||
|         return new SearchQueryComponent(m[2], QueryComponentType.Exact, negative); |       return new SearchQueryComponent(m[3], QueryComponentType.Normal, negative); | ||||||
|       } else { |  | ||||||
|         return new SearchQueryComponent(m[3], QueryComponentType.Normal, negative); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   ); |   }); | ||||||
| 
 | 
 | ||||||
|   if ( |   if ( | ||||||
|     components.length > 0 && |     components.length > 0 && | ||||||
|  |  | ||||||
|  | @ -74,7 +74,7 @@ export function formatBool(val: boolean): string { | ||||||
| 
 | 
 | ||||||
| export function getQueryUrl(q: EntityQuery, basePath: string): string { | export function getQueryUrl(q: EntityQuery, basePath: string): string { | ||||||
|   if (Object.values(q).filter((q) => q !== undefined).length === 0) return basePath; |   if (Object.values(q).filter((q) => q !== undefined).length === 0) return basePath; | ||||||
|   return encodeURI(basePath + "/" + encodeURIComponent(JSON.stringify(q))); |   return basePath + "/" + JSON.stringify(q); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function gotoEntityQuery(q: EntityQuery, basePath: string) { | export function gotoEntityQuery(q: EntityQuery, basePath: string) { | ||||||
|  |  | ||||||
|  | @ -51,12 +51,14 @@ | ||||||
|         }} |         }} | ||||||
|         selection={data.entry.current_version.category} |         selection={data.entry.current_version.category} | ||||||
|         onSelect={(item) => { |         onSelect={(item) => { | ||||||
|  |           // @ts-expect-error ids are always numeric | ||||||
|           $form.category_id = item.id; |           $form.category_id = item.id; | ||||||
|           return { newValue: item.name ?? "", close: true }; |           return { newValue: item.name ?? "", close: true }; | ||||||
|         }} |         }} | ||||||
|         onUnselect={() => { |         onUnselect={() => { | ||||||
|           $form.category_id = null; |           $form.category_id = null; | ||||||
|         }} |         }} | ||||||
|  |         asTextInput | ||||||
|         idInputName="category_id" |         idInputName="category_id" | ||||||
|       /> |       /> | ||||||
|     </FormField> |     </FormField> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|  |   import CategoryField from "$lib/components/table/CategoryField.svelte"; | ||||||
|   import UserField from "$lib/components/table/UserField.svelte"; |   import UserField from "$lib/components/table/UserField.svelte"; | ||||||
|   import { formatDate } from "$lib/shared/util"; |   import { formatBool, formatDate } from "$lib/shared/util"; | ||||||
|   import type { PageData } from "./$types"; |   import type { PageData } from "./$types"; | ||||||
|   import Header from "$lib/components/ui/Header.svelte"; |   import Header from "$lib/components/ui/Header.svelte"; | ||||||
|   import { page } from "$app/stores"; |   import { page } from "$app/stores"; | ||||||
|  |  | ||||||
|  | @ -27,12 +27,14 @@ | ||||||
|           return await trpc().room.list.query(); |           return await trpc().room.list.query(); | ||||||
|         }} |         }} | ||||||
|         onSelect={(item) => { |         onSelect={(item) => { | ||||||
|  |           // @ts-expect-error ids are always numeric | ||||||
|           $form.room_id = item.id; |           $form.room_id = item.id; | ||||||
|           return { newValue: item.name ?? "", close: true }; |           return { newValue: item.name ?? "", close: true }; | ||||||
|         }} |         }} | ||||||
|         onUnselect={() => { |         onUnselect={() => { | ||||||
|           $form.room_id = null; |           $form.room_id = null; | ||||||
|         }} |         }} | ||||||
|  |         asTextInput | ||||||
|         idInputName="room_id" |         idInputName="room_id" | ||||||
|       /> |       /> | ||||||
|     </FormField> |     </FormField> | ||||||
|  | @ -44,8 +46,10 @@ | ||||||
|           return await trpc().patient.getNames.query(); |           return await trpc().patient.getNames.query(); | ||||||
|         }} |         }} | ||||||
|         onSelect={(item) => { |         onSelect={(item) => { | ||||||
|  |           // @ts-expect-error patient id | ||||||
|           $form.patient_id = item.id; |           $form.patient_id = item.id; | ||||||
|           trpc() |           trpc() | ||||||
|  |             // @ts-expect-error patient id | ||||||
|             .patient.get.query(item.id) |             .patient.get.query(item.id) | ||||||
|             .then((p) => { |             .then((p) => { | ||||||
|               $form.patient_first_name = p.first_name; |               $form.patient_first_name = p.first_name; | ||||||
|  | @ -60,9 +64,11 @@ | ||||||
|           $form.patient_last_name = null; |           $form.patient_last_name = null; | ||||||
|           $form.patient_age = null; |           $form.patient_age = null; | ||||||
|         }} |         }} | ||||||
|  |         asTextInput | ||||||
|         noAutoselect1 |         noAutoselect1 | ||||||
|         idInputName="patient_id" |         idInputName="patient_id" | ||||||
|         filterFn={(itm) => { |         filterFn={(itm) => { | ||||||
|  |           // @ts-expect-error patient items have room attr | ||||||
|           return $form.room_id === null || itm.room_id === $form.room_id; |           return $form.room_id === null || itm.room_id === $form.room_id; | ||||||
|         }} |         }} | ||||||
|         placeholder="Neuer Patient" |         placeholder="Neuer Patient" | ||||||
|  | @ -113,12 +119,14 @@ | ||||||
|           return await trpc().category.list.query(); |           return await trpc().category.list.query(); | ||||||
|         }} |         }} | ||||||
|         onSelect={(item) => { |         onSelect={(item) => { | ||||||
|  |           // @ts-expect-error ids are always numeric | ||||||
|           $form.category_id = item.id; |           $form.category_id = item.id; | ||||||
|           return { newValue: item.name ?? "", close: true }; |           return { newValue: item.name ?? "", close: true }; | ||||||
|         }} |         }} | ||||||
|         onUnselect={() => { |         onUnselect={() => { | ||||||
|           $form.category_id = null; |           $form.category_id = null; | ||||||
|         }} |         }} | ||||||
|  |         asTextInput | ||||||
|         idInputName="category_id" |         idInputName="category_id" | ||||||
|       /> |       /> | ||||||
|     </FormField> |     </FormField> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,4 @@ | ||||||
| import { z } from "zod"; | import { ZPatientNew, ZUrlEntityId } from "$lib/shared/model/validation"; | ||||||
| 
 |  | ||||||
| import { ZEntriesQuery, ZPatientNew, ZUrlEntityId } from "$lib/shared/model/validation"; |  | ||||||
| import { trpc } from "$lib/shared/trpc"; | import { trpc } from "$lib/shared/trpc"; | ||||||
| import { loadWrap } from "$lib/shared/util"; | import { loadWrap } from "$lib/shared/util"; | ||||||
| import { superValidate } from "sveltekit-superforms"; | import { superValidate } from "sveltekit-superforms"; | ||||||
|  | @ -10,9 +8,10 @@ import type { PageLoad } from "./$types"; | ||||||
| export const load: PageLoad = async (event) => { | export const load: PageLoad = async (event) => { | ||||||
|   const id = ZUrlEntityId.parse(event.params.id); |   const id = ZUrlEntityId.parse(event.params.id); | ||||||
| 
 | 
 | ||||||
|   let query: z.infer<typeof ZEntriesQuery> = {}; |   // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
|  |   let query: any = {}; | ||||||
|   if (event.params.query) { |   if (event.params.query) { | ||||||
|     query = ZEntriesQuery.parse(JSON.parse(decodeURIComponent(event.params.query))); |     query = JSON.parse(event.params.query); | ||||||
|   } |   } | ||||||
|   if (!query.filter) query.filter = {}; |   if (!query.filter) query.filter = {}; | ||||||
|   query.filter.patient = [{ id }]; |   query.filter.patient = [{ id }]; | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ export const load: PageLoad = async (event) => { | ||||||
|   let query: z.infer<typeof ZPatientsQuery> = {}; |   let query: z.infer<typeof ZPatientsQuery> = {}; | ||||||
| 
 | 
 | ||||||
|   if (event.params.query) { |   if (event.params.query) { | ||||||
|     query = ZPatientsQuery.parse(JSON.parse(decodeURIComponent(event.params.query))); |     query = ZPatientsQuery.parse(JSON.parse(event.params.query)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const patients = await loadWrap(async () => trpc(event).patient.list.query(query)); |   const patients = await loadWrap(async () => trpc(event).patient.list.query(query)); | ||||||
|  |  | ||||||
|  | @ -1,15 +1,12 @@ | ||||||
| import { z } from "zod"; |  | ||||||
| 
 |  | ||||||
| import { ZEntriesQuery } from "$lib/shared/model/validation"; |  | ||||||
| import { trpc } from "$lib/shared/trpc"; | import { trpc } from "$lib/shared/trpc"; | ||||||
| import { loadWrap } from "$lib/shared/util"; | import { loadWrap } from "$lib/shared/util"; | ||||||
| import type { PageLoad } from "./$types"; | import type { PageLoad } from "./$types"; | ||||||
| 
 | 
 | ||||||
| export const load: PageLoad = async (event) => { | export const load: PageLoad = async (event) => { | ||||||
|   let query: z.infer<typeof ZEntriesQuery> = {}; |   let query = {}; | ||||||
| 
 | 
 | ||||||
|   if (event.params.query) { |   if (event.params.query) { | ||||||
|     query = ZEntriesQuery.parse(JSON.parse(decodeURIComponent(event.params.query))); |     query = JSON.parse(event.params.query); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const entries = await loadWrap(() => trpc(event).entry.list.query(query)); |   const entries = await loadWrap(() => trpc(event).entry.list.query(query)); | ||||||
|  |  | ||||||
|  | @ -9,7 +9,9 @@ const config = { | ||||||
| 
 | 
 | ||||||
|   kit: { |   kit: { | ||||||
|     // See https://kit.svelte.dev/docs/adapters for more information about adapters.
 |     // See https://kit.svelte.dev/docs/adapters for more information about adapters.
 | ||||||
|     adapter: adapter(), |     adapter: adapter({ | ||||||
|  |       precompress: true, | ||||||
|  |     }), | ||||||
| 
 | 
 | ||||||
|     alias: { |     alias: { | ||||||
|       $api: "./src/api", |       $api: "./src/api", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue