quicknotes/frontend/src/routes/notes/[id]/+page.svelte

169 lines
4.3 KiB
Svelte
Raw Normal View History

<script lang="ts">
import { run, preventDefault } from 'svelte/legacy';
import { notes } from '$lib';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { renderMarkdown } from '$lib/markdown';
interface Note {
id: string;
title: string;
content: string;
createdAt: Date;
updatedAt: Date;
}
let note: Note | undefined = $state();
let isEditing = $state($page.url.searchParams.get('edit') === 'true');
let editedTitle = $state('');
let editedContent = $state('');
let id = $derived($page.params.id);
run(() => {
if ($notes && id) {
note = $notes.find((n) => n.id === id);
if (note) {
editedTitle = note.title;
editedContent = note.content;
} else if ($notes.length > 0) {
// Only redirect if we have loaded notes and didn't find this one
goto('/');
}
}
});
async function handleSave() {
if (!editedTitle || !editedContent) return;
try {
await notes.update(id, {
title: editedTitle,
content: editedContent
});
isEditing = false;
} catch (error) {
console.error('Failed to update note:', error);
}
}
async function handleLinkClick(event: MouseEvent | KeyboardEvent) {
const target = event.target as HTMLElement;
if (target.tagName === 'A' && target.classList.contains('note-link')) {
event.preventDefault();
const title = target.getAttribute('data-title');
if (!title) return;
// Check if a note with this title exists
const existingNote = $notes.find((n) => n.title === title);
if (existingNote) {
// Navigate to the existing note
goto(`/notes/${existingNote.id}`);
} else {
// Navigate to the new note page with the title pre-filled
goto(`/notes/new?title=${encodeURIComponent(title)}`);
}
}
}
</script>
<div
class="container"
onclick={handleLinkClick}
onkeydown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
handleLinkClick(event);
}
}}
role="button"
tabindex="0"
>
<section class="section">
{#if note}
{#if isEditing}
<form onsubmit={preventDefault(handleSave)}>
<div class="field">
<label class="label" for="title">Title</label>
<div class="control">
<input id="title" class="input" type="text" bind:value={editedTitle} required />
</div>
</div>
<div class="field">
<label class="label" for="content">Content</label>
<div class="control">
<textarea id="content" class="textarea" bind:value={editedContent} rows="10" required
></textarea>
</div>
<div class="mt-4 box">
<label class="label" for="preview">Preview</label>
<div id="preview" class="content" aria-live="polite">
{#await renderMarkdown(note.content)}
<p>Rendering markdown...</p>
{:then markdownContent}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html markdownContent}
{:catch err}
<p>Error rendering the markdown content!i {err.message}</p>
{/await}
</div>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button type="submit" class="button is-primary">Save</button>
</div>
<div class="control">
<button
type="button"
class="button is-light"
onclick={() => {
isEditing = false;
goto(`/notes/${id}`);
}}
>
Cancel
</button>
</div>
</div>
</form>
{:else}
<div class="level">
<div class="level-left">
<div class="level-item">
<h1 class="title">{note.title}</h1>
</div>
</div>
<div class="level-right">
<div class="level-item">
<button class="button is-primary" onclick={() => (isEditing = true)}> Edit </button>
</div>
</div>
</div>
<div class="content">
{#await renderMarkdown(note.content)}
<p>Rendering markdown...</p>
{:then markdownContent}
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html markdownContent}
{:catch err}
<p>Error rendering the markdown content!i {err.message}</p>
{/await}
</div>
<p class="has-text-grey is-size-7">
Last updated: {note.updatedAt.toLocaleString()}
</p>
{/if}
{:else}
<div class="notification is-danger">Note not found</div>
{/if}
<div class="mt-4">
<a href="/" class="button is-light">Back to Notes</a>
</div>
</section>
</div>