feat: mvp

This commit is contained in:
2025-11-26 21:48:06 +01:00
commit c1521af887
57 changed files with 8140 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
import { redirect, type Actions } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { extractFormData } from '$lib/extractFormData';
import { resolve } from '$app/paths';
import * as v from 'valibot';
import { ensureAuth } from '$lib/auth';
import { db } from '$lib/server/db';
import { aktis } from '$lib/server/db/schema';
export const load: PageServerLoad = async (event) => {
await ensureAuth(event);
return {};
};
export const actions = {
default: async (event) => {
const user = await ensureAuth(event);
const akti = (
await extractFormData(
event.request,
v.object({
title: v.pipe(v.string(), v.minLength(5)),
summary: v.pipe(v.string(), v.minLength(5)),
body: v.pipe(v.string(), v.minLength(5))
})
)
).data;
if (!akti) return {};
const res = await db
.insert(aktis)
.values({ ...akti, author: user.id! })
.returning({ id: aktis.id });
return redirect(303, resolve(`/akti/[aktiId]`, { aktiId: res[0].id }));
}
} satisfies Actions;
+5
View File
@@ -0,0 +1,5 @@
<script lang="ts">
import AktiEditor from '$lib/components/akti/AktiEditor.svelte';
</script>
<AktiEditor akti={{ title: '', summary: '', body: '' }} />
+66
View File
@@ -0,0 +1,66 @@
import { db } from '$lib/server/db';
import { aktis, ratings } from '$lib/server/db/schema';
import { error, redirect, type Actions } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import type { PageServerLoad } from './$types';
import { ensureAuth } from '$lib/auth';
import { extractFormData } from '$lib/extractFormData';
import * as v from 'valibot';
import { resolve } from '$app/paths';
export const load: PageServerLoad = async (event) => {
const akti = await db.query.aktis.findFirst({
where: eq(aktis.id, event.params.aktiId),
with: { author: true }
});
const r = await db.query.ratings.findMany({
with: { user: true },
where: eq(ratings.aktiId, event.params.aktiId)
});
if (!akti) {
error(404, { message: 'Die Akti gits garnid, sorry...' });
}
return {
akti,
ratings: r
};
};
export const actions = {
default: async (event) => {
const user = await ensureAuth(event);
if (!event.params.aktiId) return error(404);
const akti = await db
.select({ id: aktis.id, version: aktis.version, author: aktis.author })
.from(aktis)
.limit(1)
.where(eq(aktis.id, event.params.aktiId));
if (!akti || akti.length == 0) return error(404);
if (akti[0].author != user.id) return error(403);
const changeRequest = (
await extractFormData(
event.request,
v.object({
title: v.pipe(v.string(), v.minLength(5)),
summary: v.pipe(v.string(), v.minLength(5)),
body: v.pipe(v.string(), v.minLength(5))
})
)
).data;
if (!changeRequest) return error(400);
const res = await db
.insert(aktis)
.values({ ...changeRequest, author: user.id, version: akti[0].version + 1 })
.returning({ id: aktis.id });
return redirect(303, resolve(`/akti/[aktiId]`, { aktiId: res[0].id }));
}
} satisfies Actions;
+40
View File
@@ -0,0 +1,40 @@
<script lang="ts">
import { Button } from 'flowbite-svelte';
import type { PageProps } from './$types';
import { EditOutline, CloseOutline } from 'flowbite-svelte-icons';
import AktiEditor from '$lib/components/akti/AktiEditor.svelte';
import UserDisplay from '$lib/components/UserDisplay.svelte';
let { data }: PageProps = $props();
let edit = $state(false);
</script>
<div class="flex justify-between">
<h2>{data.akti?.title} <span class="text-xs text-gray-400">v{data.akti.version}</span></h2>
{#if data.session?.user?.id === data.akti.author?.id && data.akti.author?.id}
<Button onclick={() => (edit = !edit)} color={edit ? 'gray' : 'primary'}>
{#if edit}
<CloseOutline class="shrink-0 h-6 w-6" />
{:else}
<EditOutline class="shrink-0 h-6 w-6 -mr-0.5 ml-0.5" />
{/if}
</Button>
{:else}
<div class="flex gap-5 items-center">
<p>gschribe vo:</p>
<UserDisplay user={data.akti.author} />
</div>
{/if}
</div>
{#if edit}
<AktiEditor akti={data.akti} />
{:else}
<div class="p-5 my-5 bg-gray-200 rounded-md">
<h3 class="mb-2">Zämefassig</h3>
<p>{data.akti.summary}</p>
</div>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html data.akti.body}
{/if}