|
2 | 2 | import { superForm } from "$lib/utils/client/superForms"; |
3 | 3 | import PolicyTreeNode from "$lib/components/access/PolicyTreeNode.svelte"; |
4 | 4 | import type { PolicyNode } from "$lib/components/access/PolicyTreeNode.svelte"; |
| 5 | + import { writable, derived } from "svelte/store"; |
5 | 6 |
|
6 | 7 | let { data } = $props(); |
7 | 8 |
|
| 9 | + const search = writable(""); |
| 10 | +
|
| 11 | + // Filtered trees, reactive to search |
| 12 | + const filteredPosTrees = derived(search, ($search) => { |
| 13 | + const q = $search.toLowerCase().trim(); |
| 14 | +
|
| 15 | + return Array.from(data.posToAccessPolicies.entries()) |
| 16 | + .sort((a, b) => (a[0] < b[0] ? -1 : 1)) |
| 17 | + .map(([position, policies]) => { |
| 18 | + const positionMatches = position.toLowerCase().includes(q); |
| 19 | +
|
| 20 | + const visibleApiNames = positionMatches |
| 21 | + ? policies.map((p) => p.apiName) |
| 22 | + : policies |
| 23 | + .filter((p) => p.apiName.toLowerCase().includes(q)) |
| 24 | + .map((p) => p.apiName); |
| 25 | +
|
| 26 | + return { |
| 27 | + position, |
| 28 | + policies, |
| 29 | + tree: buildPolicyTreeForPolicies(visibleApiNames), |
| 30 | + }; |
| 31 | + }) |
| 32 | + .filter(({ tree }) => Object.keys(tree).length > 0); |
| 33 | + }); |
| 34 | +
|
8 | 35 | const { |
9 | 36 | form: createForm, |
10 | 37 | errors: createErrors, |
|
33 | 60 | policies.forEach((p) => insertPolicy(root, p)); |
34 | 61 | return root.children; // return children instead of the root node |
35 | 62 | } |
36 | | -
|
37 | | - const posTrees: Array<{ |
38 | | - position: string; |
39 | | - tree: Record<string, PolicyNode>; |
40 | | - policies: Array<{ apiName: string; id: string }>; |
41 | | - }> = Array.from(data.posToAccessPolicies.entries()) |
42 | | - .sort((a, b) => (a[0] < b[0] ? -1 : 1)) |
43 | | - .map(([position, policies]) => ({ |
44 | | - position, |
45 | | - tree: buildPolicyTreeForPolicies(policies.map((p) => p.apiName)), |
46 | | - policies, |
47 | | - })); |
48 | 63 | </script> |
49 | 64 |
|
| 65 | +<!-- Search --> |
| 66 | +<div class="mb-4 flex items-center gap-2"> |
| 67 | + <input |
| 68 | + type="text" |
| 69 | + placeholder="Search policies..." |
| 70 | + class="input input-bordered w-full max-w-md" |
| 71 | + bind:value={$search} |
| 72 | + /> |
| 73 | + |
| 74 | + {#if $search} |
| 75 | + <button class="btn btn-sm" onclick={() => search.set("")}> Clear </button> |
| 76 | + {/if} |
| 77 | +</div> |
| 78 | + |
50 | 79 | <div class="overflow-x-auto"> |
51 | 80 | <table class="table w-full"> |
52 | 81 | <thead> |
|
62 | 91 | </thead> |
63 | 92 |
|
64 | 93 | <tbody> |
65 | | - {#each posTrees as { position, tree, policies }} |
| 94 | + {#each $filteredPosTrees as { position, tree, policies }} |
66 | 95 | <tr class="odd:bg-gray/10 even:bg-base-200/50"> |
67 | 96 | <td class="font-medium">{position}</td> |
68 | 97 | <td> |
|
0 commit comments