mirror of
https://github.com/dsec-hub/dsec-notebook.git
synced 2026-09-22 07:24:27 +00:00
171 lines
4.2 KiB
Svelte
171 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
import { query, mutation } from "$lib/api";
|
|
import { isAuthenticated, getToken, initAuth } from "$lib/stores/auth";
|
|
import { goto } from "$app/navigation";
|
|
import { onMount } from "svelte";
|
|
import { get } from "svelte/store";
|
|
import MarkdownEditor from "$lib/components/MarkdownEditor.svelte";
|
|
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);
|
|
|
|
onMount(async () => {
|
|
await initAuth();
|
|
if (!get(isAuthenticated)) {
|
|
goto("/auth/login", { replaceState: true });
|
|
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 = "";
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
const id = (await mutation("notes:create", {
|
|
token,
|
|
title: title.trim(),
|
|
content: content.trim(),
|
|
topicId: selectedTopicId,
|
|
unitId,
|
|
})) as string;
|
|
|
|
goto(`/notes/${id}`);
|
|
} 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="text-ink font-serif text-4xl font-medium">Post a note</h1>
|
|
<p class="text-muted mt-2 mb-10 text-[15px]">
|
|
Share study notes with the Deakin community. Focus on a specific topic, not an entire unit.
|
|
</p>
|
|
|
|
<form onsubmit={handleSubmit} class="border-rule space-y-6 border-t 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>
|
|
<MarkdownEditor
|
|
bind:content
|
|
label="Content"
|
|
placeholder="Write your notes here..."
|
|
rows={10}
|
|
/>
|
|
</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.code2 ? ` / ${unit.code2}` : ""} — {unit.name}</option
|
|
>
|
|
{/each}
|
|
</select>
|
|
{/if}
|
|
|
|
<button
|
|
type="button"
|
|
onclick={() => {
|
|
useCustomUnit = !useCustomUnit;
|
|
customUnit = "";
|
|
}}
|
|
class="text-secondary hover:text-secondary-dark mt-2 text-xs"
|
|
>
|
|
{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-primary text-sm">{error}</p>
|
|
{/if}
|
|
|
|
<button type="submit" disabled={loading} class="btn-primary">
|
|
{loading ? "Publishing..." : "Publish note"}
|
|
</button>
|
|
</form>
|
|
</div>
|