dsec-notebook/src/routes/post/note/+page.svelte
2026-08-29 20:05:06 +08:00

179 lines
4.8 KiB
Svelte

<script lang="ts">
import { query, mutation } from '$lib/api';
import { isAuthenticated, getToken } from '$lib/stores/auth';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import type { TopicDoc, UnitDoc } from '$lib/types';
let topics: TopicDoc[] = $state([]);
let units: UnitDoc[] = $state([]);
let title = $state('');
let content = $state('');
let selectedTopicId = $state('');
let selectedUnitId = $state('');
let customUnit = $state('');
let useCustomUnit = $state(false);
let error = $state('');
let loading = $state(false);
let success = $state('');
onMount(async () => {
if (!get(isAuthenticated)) {
goto('/auth/login');
return;
}
const [t, u] = await Promise.all([query('topics:getAll'), query('units:getAll')]);
topics = t as TopicDoc[];
units = u as UnitDoc[];
});
async function handleSubmit(e: SubmitEvent) {
e.preventDefault();
error = '';
success = '';
if (!title.trim() || !content.trim()) {
error = 'Title and content are required';
return;
}
if (!selectedTopicId) {
error = 'Please select a topic';
return;
}
const token = getToken();
if (!token) {
error = 'You must be signed in';
return;
}
loading = true;
try {
let unitId = selectedUnitId;
if (useCustomUnit && customUnit.trim()) {
const existing = units.find((u) => u.code.toLowerCase() === customUnit.trim().toUpperCase());
if (existing) {
unitId = existing._id;
} else {
unitId = (await mutation('units:createCustom', {
code: customUnit.trim().toUpperCase(),
name: customUnit.trim().toUpperCase()
})) as string;
}
}
await mutation('notes:create', {
token,
title: title.trim(),
content: content.trim(),
topicId: selectedTopicId,
unitId
});
success = 'Note published.';
title = '';
content = '';
selectedTopicId = '';
selectedUnitId = '';
customUnit = '';
useCustomUnit = false;
setTimeout(() => {
success = '';
}, 3000);
} catch (err: any) {
error = err.message ?? 'Failed to publish note';
} finally {
loading = false;
}
}
</script>
<svelte:head>
<title>Post a note — Notebook</title>
</svelte:head>
<div class="page">
<h1 class="font-serif text-4xl font-medium text-ink">Post a note</h1>
<p class="mt-2 mb-10 text-[15px] text-muted">
Share study notes with the Deakin community. Focus on a specific topic, not an entire unit.
</p>
{#if success}
<p class="mb-6 text-sm text-secondary">{success}</p>
{/if}
<form onsubmit={handleSubmit} class="space-y-6 border-t border-rule pt-8">
<div>
<label for="title" class="kicker mb-2 block">Title</label>
<input
id="title"
type="text"
bind:value={title}
placeholder="e.g., Binary Search Tree Implementation in Python"
class="field"
required
/>
</div>
<div>
<label for="content" class="kicker mb-2 block">Content</label>
<textarea
id="content"
bind:value={content}
rows={10}
placeholder="Write your notes here..."
class="field resize-y"
required
></textarea>
</div>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<label for="topic" class="kicker mb-2 block">Topic</label>
<select id="topic" bind:value={selectedTopicId} class="field" required>
<option value="">Select a topic...</option>
{#each topics as topic}
<option value={topic._id}>{topic.name}</option>
{/each}
</select>
</div>
<div>
<label for="unit" class="kicker mb-2 block">Unit</label>
{#if !useCustomUnit}
<select id="unit" bind:value={selectedUnitId} class="field">
<option value="">Select a unit...</option>
{#each units as unit}
<option value={unit._id}>{unit.code} {unit.name}</option>
{/each}
</select>
{/if}
<button
type="button"
onclick={() => {
useCustomUnit = !useCustomUnit;
customUnit = '';
}}
class="mt-2 text-xs text-secondary hover:text-secondary-dark"
>
{useCustomUnit ? 'Choose from list' : 'Other unit...'}
</button>
{#if useCustomUnit}
<input type="text" bind:value={customUnit} placeholder="e.g., SIT384" class="field mt-2" />
{/if}
</div>
</div>
{#if error}
<p class="text-sm text-primary">{error}</p>
{/if}
<button type="submit" disabled={loading} class="btn-primary">
{loading ? 'Publishing...' : 'Publish note'}
</button>
</form>
</div>