obsidian-book-tracker/src/ui/code-blocks/ReadingLogCodeBlockView.svelte

186 lines
4.1 KiB
Svelte

<script lang="ts">
import type { ReadingLogEntry } from "@utils/ReadingLog";
import { Edit, Trash, Plus } from "lucide-svelte";
import { ReadingLogEntryEditModal } from "@ui/modals";
import type BookTrackerPlugin from "@src/main";
import { createReadingLog } from "@ui/stores/reading-log.svelte";
import { ALL_TIME } from "@ui/stores/date-filter.svelte";
import { onDestroy } from "svelte";
import { getLinkpath } from "obsidian";
interface Props {
plugin: BookTrackerPlugin;
}
const { plugin }: Props = $props();
function bookUri(book: string) {
return getLinkpath(book + ".md");
}
const store = createReadingLog(plugin.readingLog);
onDestroy(() => store.destroy());
function createEntry() {
const modal = new ReadingLogEntryEditModal(plugin, async (entry) => {
modal.close();
await store.addEntry(entry);
});
modal.open();
}
function editEntry(entry: ReadingLogEntry) {
const modal = new ReadingLogEntryEditModal(
plugin,
async (entry) => {
modal.close();
await store.updateEntry(entry);
},
entry,
);
modal.open();
}
async function removeEntry(entry: ReadingLogEntry) {
await store.removeEntry(entry);
}
</script>
<div class="obt-reading-log-viewer">
<div class="controls">
<div class="left">
<select class="year-filter" bind:value={store.filterYear}>
{#each store.filterYears as year}
<option value={year}>{year}</option>
{/each}
<option value={ALL_TIME}>All Time</option>
</select>
{#if store.filterYear !== ALL_TIME}
<select class="month-filter" bind:value={store.filterMonth}>
<option value={ALL_TIME}>Select Month</option>
<option value={1}>January</option>
<option value={2}>February</option>
<option value={3}>March</option>
<option value={4}>April</option>
<option value={5}>May</option>
<option value={6}>June</option>
<option value={7}>July</option>
<option value={8}>August</option>
<option value={9}>September</option>
<option value={10}>October</option>
<option value={11}>November</option>
<option value={12}>December</option>
</select>
{/if}
</div>
<div class="right">
<button onclick={createEntry} class="create-entry" type="button">
<Plus />
Create
</button>
</div>
</div>
<table>
<thead>
<tr>
<th class="date">Date</th>
<th class="book">Book</th>
<th class="pages-read">Pages Read</th>
<th class="percent-complete">Percent Complete</th>
<th class="actions">Actions</th>
</tr>
</thead>
<tbody>
{#each store.entries as entry}
<tr>
<td class="date">{entry.createdAt.format("YYYY-MM-DD")}</td>
<td class="book"
><a href={bookUri(entry.book)}>{entry.book}</a></td
>
<td class="pages-read">{entry.pagesRead}</td>
<td class="percent-complete">
{Math.round(
(entry.pagesReadTotal /
(entry.pagesReadTotal + entry.pagesRemaining)) *
100,
)}%
</td>
<td class="actions">
<button onclick={() => editEntry(entry)}>
<Edit />
<span>Edit</span>
</button>
<button onclick={() => removeEntry(entry)}>
<Trash />
<span>Delete</span>
</button>
</td>
</tr>
{:else}
<tr>
<td colspan="5" class="no-entries">No entries found</td>
</tr>
{/each}
</tbody>
</table>
</div>
<!-- svelte-ignore css_unused_selector -->
<style lang="scss">
@use "../styles/utils";
.obt-reading-log-viewer {
.controls {
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
.right {
text-align: right;
button.create-entry {
display: inline-flex;
gap: var(--size-2-2);
align-items: center;
:global(svg) {
width: var(--icon-s);
height: var(--icon-s);
}
}
}
}
table {
width: 100%;
td.book {
width: 100%;
}
th,
td:not(.book) {
white-space: nowrap;
}
td.pages-read,
td.percent-complete {
text-align: center;
}
td.actions span {
@include utils.visually-hidden;
}
td.no-entries {
text-align: center;
font-size: 1.5rem;
font-weight: var(--bold-weight);
font-style: italic;
padding: var(--size-4-6);
}
}
}
</style>