Skip to content

Commit e609648

Browse files
authored
Add position based access admin interface (#927)
1 parent 540f169 commit e609648

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

src/routes/(app)/admin/access/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
}}
2121
/>
2222

23+
<a href="access/positions" class="link-primary mb-4">View per position</a>
24+
2325
<div class="overflow-x-auto">
2426
<table class="table">
2527
<!-- head -->
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { fail, superValidate } from "sveltekit-superforms";
2+
import type { Actions, PageServerLoad } from "./$types";
3+
import { z } from "zod";
4+
import { zod } from "sveltekit-superforms/adapters";
5+
6+
const deletePolicySchema = z.object({ policyId: z.string() });
7+
const createPolicySchema = z.object({
8+
position: z.string().nullable(),
9+
apiName: z.string(),
10+
studentId: z.string().nullable(),
11+
});
12+
13+
export const load: PageServerLoad = async ({ locals }) => {
14+
const { prisma } = locals;
15+
const accesspolicies = await prisma.accessPolicy.findMany({
16+
select: { role: true, apiName: true, id: true },
17+
});
18+
const posToAccessPolicies = new Map<
19+
string,
20+
Array<{ apiName: string; id: string }>
21+
>();
22+
accesspolicies.forEach((a) => {
23+
if (a.role) {
24+
posToAccessPolicies.set(a.role, [
25+
...(posToAccessPolicies.get(a.role) ?? []),
26+
{ apiName: a.apiName, id: a.id },
27+
]);
28+
}
29+
});
30+
const createForm = await superValidate(zod(createPolicySchema));
31+
const deleteForm = await superValidate(zod(deletePolicySchema));
32+
return { posToAccessPolicies, createForm, deleteForm };
33+
};
34+
35+
export const actions: Actions = {
36+
deletePolicy: async ({ locals, request }) => {
37+
const { prisma } = locals;
38+
const form = await superValidate(request, zod(deletePolicySchema));
39+
if (!form.valid) return fail(400, { form });
40+
await prisma.accessPolicy.delete({
41+
where: {
42+
id: form.data.policyId,
43+
},
44+
});
45+
},
46+
createPolicy: async ({ locals, request }) => {
47+
const { prisma } = locals;
48+
const form = await superValidate(request, zod(createPolicySchema));
49+
if (!form.valid) return fail(400, { form });
50+
await prisma.accessPolicy.create({
51+
data: {
52+
apiName: form.data.apiName,
53+
role: form.data.position,
54+
studentId: form.data.studentId,
55+
},
56+
});
57+
},
58+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<script lang="ts">
2+
import { superForm } from "$lib/utils/client/superForms";
3+
4+
let { data } = $props();
5+
6+
const {
7+
form: createForm,
8+
errors: createErrors,
9+
constraints: createFormConstraints,
10+
enhance: createFormEnhance,
11+
} = superForm(data.createForm);
12+
const { enhance: deleteFormEnhance } = superForm(data.deleteForm);
13+
</script>
14+
15+
<a href="." class="link-primary mb-4">View per apiname</a>
16+
17+
<div class="overflow-x-auto">
18+
<table class="table">
19+
<!-- head -->
20+
<thead>
21+
<tr class="bg-base-200">
22+
<th>Position</th>
23+
<th>Number of policies</th>
24+
</tr>
25+
</thead>
26+
<tbody class="">
27+
{#each Array.from(data.posToAccessPolicies.entries()).sort( (a, b) => (a[0] < b[0] ? -1 : 1), ) as pos}
28+
<tr class="odd:bg-gray even:bg-base-200">
29+
<td class="font-medium">{pos[0]}</td>
30+
<td class="text-right">
31+
<div class="collapse collapse-arrow">
32+
<input type="checkbox" class="w-full" />
33+
<div class="collapse-title w-full">
34+
{pos[1].length} access accessPolicies
35+
</div>
36+
37+
<div class="collapse-content grid grid-cols-2">
38+
{#each pos[1] as policy}
39+
<form
40+
method="POST"
41+
action="?/deletePolicy"
42+
class="col-span-1"
43+
use:deleteFormEnhance
44+
>
45+
<input
46+
type="text"
47+
name="policyId"
48+
hidden
49+
value={policy.id}
50+
/>
51+
{policy.apiName}
52+
<button type="submit" aria-label="delete" class="ml-3">
53+
<span
54+
class="i-mdi-trash-can bg-primary hover:bg-primary/70"
55+
></span>
56+
</button>
57+
</form>
58+
{/each}
59+
<div class="col-span-2 pt-4">
60+
<form
61+
method="POST"
62+
action="?/createPolicy"
63+
use:createFormEnhance
64+
>
65+
<input type="hidden" value={pos[0]} name="position" />
66+
<input
67+
type="text"
68+
name="apiName"
69+
placeholder="Policy name"
70+
aria-invalid={$createErrors.apiName ? "true" : undefined}
71+
class="input join-item input-bordered input-primary md:flex-1"
72+
bind:value={$createForm.apiName}
73+
{...$createFormConstraints.apiName}
74+
/>
75+
<button type="submit" class="btn btn-primary">add</button>
76+
</form>
77+
</div>
78+
</div>
79+
</div>
80+
</td>
81+
</tr>
82+
{/each}
83+
</tbody>
84+
</table>
85+
</div>

0 commit comments

Comments
 (0)