added wiedervorlage
This commit is contained in:
parent
ed9597c2dd
commit
1da4050813
|
|
@ -8,6 +8,7 @@
|
||||||
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
import RenderedMarkdown from './RenderedMarkdown.svelte';
|
||||||
import DateNavigator from './DateNavigator.svelte';
|
import DateNavigator from './DateNavigator.svelte';
|
||||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||||
|
import WiedervorlageSection from './WiedervorlageSection.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contextId: string;
|
contextId: string;
|
||||||
|
|
@ -22,6 +23,7 @@
|
||||||
let entryEditor: MarkdownEditor;
|
let entryEditor: MarkdownEditor;
|
||||||
let selectedDate = $state(today());
|
let selectedDate = $state(today());
|
||||||
let selectedLinkedContextId = $state('');
|
let selectedLinkedContextId = $state('');
|
||||||
|
let wiedervorlageChecked = $state(false);
|
||||||
|
|
||||||
// All meeting contexts for the link dropdown
|
// All meeting contexts for the link dropdown
|
||||||
const meetingContexts = liveQuery(() =>
|
const meetingContexts = liveQuery(() =>
|
||||||
|
|
@ -70,13 +72,14 @@
|
||||||
} else {
|
} else {
|
||||||
const text = body ? `${title}\n${body}` : title;
|
const text = body ? `${title}\n${body}` : title;
|
||||||
await getOrCreateJournalTopic();
|
await getOrCreateJournalTopic();
|
||||||
await createHistoryEntry(JOURNAL_TOPIC_ID, selectedDate, text);
|
await createHistoryEntry(JOURNAL_TOPIC_ID, selectedDate, text, null, wiedervorlageChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
entryTitle = '';
|
entryTitle = '';
|
||||||
entryText = '';
|
entryText = '';
|
||||||
entryEditor?.clear();
|
entryEditor?.clear();
|
||||||
selectedLinkedContextId = '';
|
selectedLinkedContextId = '';
|
||||||
|
wiedervorlageChecked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTitleKeypress(e: KeyboardEvent) {
|
function handleTitleKeypress(e: KeyboardEvent) {
|
||||||
|
|
@ -192,14 +195,24 @@
|
||||||
minHeight="60px"
|
minHeight="60px"
|
||||||
onchange={(md) => entryText = md}
|
onchange={(md) => entryText = md}
|
||||||
/>
|
/>
|
||||||
<button
|
<div class="flex items-center gap-4">
|
||||||
class="self-start rounded bg-accent px-4 py-2 font-bold text-white hover:brightness-110"
|
<button
|
||||||
onclick={handleAddEntry}
|
class="rounded bg-accent px-4 py-2 font-bold text-white hover:brightness-110"
|
||||||
>
|
onclick={handleAddEntry}
|
||||||
+ {selectedLinkedContextId ? 'Thema hinzufügen' : 'Notiz hinzufügen'}
|
>
|
||||||
</button>
|
+ {selectedLinkedContextId ? 'Thema hinzufügen' : 'Notiz hinzufügen'}
|
||||||
|
</button>
|
||||||
|
{#if !selectedLinkedContextId}
|
||||||
|
<label class="flex cursor-pointer items-center gap-1.5 text-sm text-muted">
|
||||||
|
<input type="checkbox" bind:checked={wiedervorlageChecked} class="accent-amber-500" />
|
||||||
|
Wiedervorlage
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<WiedervorlageSection date={selectedDate} />
|
||||||
|
|
||||||
{#if filteredEntries.length > 0}
|
{#if filteredEntries.length > 0}
|
||||||
<div class="mb-8 border-l-2 border-[#555] pl-5">
|
<div class="mb-8 border-l-2 border-[#555] pl-5">
|
||||||
<div class="mb-4 text-xl font-bold text-accent">{selectedDate}</div>
|
<div class="mb-4 text-xl font-bold text-accent">{selectedDate}</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { liveQuery } from 'dexie';
|
||||||
|
import { db } from '$lib/db/schema';
|
||||||
|
import { resolveWiedervorlage, setWiedervorlage, convertToTopic } from '$lib/db/repositories';
|
||||||
|
import type { HistoryEntry } from '@ka-note/shared';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
entry: HistoryEntry;
|
||||||
|
}
|
||||||
|
let { entry }: Props = $props();
|
||||||
|
|
||||||
|
let showVerschieben = $state(false);
|
||||||
|
let newDate = $state('');
|
||||||
|
let showConvert = $state(false);
|
||||||
|
let selectedContextId = $state('');
|
||||||
|
|
||||||
|
const meetingContexts = liveQuery(() =>
|
||||||
|
db.contexts
|
||||||
|
.filter(c => !c.deletedAt && c.type === 'meeting' && c.id !== 'daily-log')
|
||||||
|
.sortBy('sortOrder')
|
||||||
|
);
|
||||||
|
|
||||||
|
const lines = $derived(entry.text.split('\n'));
|
||||||
|
const title = $derived(lines[0]);
|
||||||
|
const body = $derived(lines.slice(1).join('\n').trim());
|
||||||
|
|
||||||
|
async function handleOk() {
|
||||||
|
await resolveWiedervorlage(entry.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleVerschieben() {
|
||||||
|
if (!newDate) return;
|
||||||
|
await setWiedervorlage(entry.id, newDate);
|
||||||
|
showVerschieben = false;
|
||||||
|
newDate = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConvert() {
|
||||||
|
if (!selectedContextId) return;
|
||||||
|
await convertToTopic(entry.id, selectedContextId);
|
||||||
|
showConvert = false;
|
||||||
|
selectedContextId = '';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mb-3 rounded-lg border border-amber-500/60 bg-amber-950/30 p-3">
|
||||||
|
<div class="mb-2 flex items-start gap-2">
|
||||||
|
<span class="text-xs font-semibold uppercase tracking-wider text-amber-400">⏰ Wiedervorlage {entry.wiedervorlageDate}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="font-bold text-white">{title}</div>
|
||||||
|
{#if body}
|
||||||
|
<div class="mt-1 text-sm text-[#ccc]">{body}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if showVerschieben}
|
||||||
|
<div class="mb-2 flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
class="rounded border border-[#444] bg-bg px-2 py-1 text-sm text-white"
|
||||||
|
bind:value={newDate}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="rounded bg-amber-600 px-3 py-1 text-sm font-bold text-white hover:brightness-110"
|
||||||
|
onclick={handleVerschieben}
|
||||||
|
>OK</button>
|
||||||
|
<button
|
||||||
|
class="rounded bg-[#444] px-3 py-1 text-sm text-white hover:bg-[#555]"
|
||||||
|
onclick={() => showVerschieben = false}
|
||||||
|
>Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
{:else if showConvert}
|
||||||
|
<div class="mb-2 flex items-center gap-2">
|
||||||
|
<select
|
||||||
|
class="flex-1 rounded border border-[#444] bg-bg px-2 py-1 text-sm text-white"
|
||||||
|
bind:value={selectedContextId}
|
||||||
|
>
|
||||||
|
<option value="">— Kontext wählen —</option>
|
||||||
|
{#each $meetingContexts ?? [] as ctx}
|
||||||
|
<option value={ctx.id}>{ctx.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
class="rounded bg-accent px-3 py-1 text-sm font-bold text-white hover:brightness-110"
|
||||||
|
onclick={handleConvert}
|
||||||
|
disabled={!selectedContextId}
|
||||||
|
>Erstellen</button>
|
||||||
|
<button
|
||||||
|
class="rounded bg-[#444] px-3 py-1 text-sm text-white hover:bg-[#555]"
|
||||||
|
onclick={() => showConvert = false}
|
||||||
|
>Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
class="rounded bg-green-700 px-3 py-1 text-sm font-bold text-white hover:brightness-110"
|
||||||
|
onclick={handleOk}
|
||||||
|
>Ok</button>
|
||||||
|
<button
|
||||||
|
class="rounded bg-amber-700 px-3 py-1 text-sm text-white hover:brightness-110"
|
||||||
|
onclick={() => showVerschieben = true}
|
||||||
|
>Verschieben</button>
|
||||||
|
<button
|
||||||
|
class="rounded bg-[#444] px-3 py-1 text-sm text-white hover:bg-[#555]"
|
||||||
|
onclick={() => showConvert = true}
|
||||||
|
>In Thema wandeln</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { pendingWiedervorlage } from '$lib/stores/agenda';
|
||||||
|
import WiedervorlageCard from './WiedervorlageCard.svelte';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
let { date }: Props = $props();
|
||||||
|
|
||||||
|
const pending = $derived(pendingWiedervorlage(date));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if ($pending ?? []).length > 0}
|
||||||
|
<div class="mb-6 rounded-lg border border-amber-500/40 bg-amber-950/20 p-4">
|
||||||
|
<div class="mb-3 text-sm font-semibold uppercase tracking-wider text-amber-400">
|
||||||
|
Wiedervorlage ({($pending ?? []).length})
|
||||||
|
</div>
|
||||||
|
{#each $pending ?? [] as entry (entry.id)}
|
||||||
|
<WiedervorlageCard {entry} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { db } from './schema';
|
import { db } from './schema';
|
||||||
import { newId, now } from './helpers';
|
import { newId, now, today } from './helpers';
|
||||||
import type { AgendaContext, Topic, HistoryEntry, Rating, ContextType, TopicStatus, ProjectMeta, PersonMeta, CompanyMeta } from '@ka-note/shared';
|
import type { AgendaContext, Topic, HistoryEntry, Rating, ContextType, TopicStatus, ProjectMeta, PersonMeta, CompanyMeta } from '@ka-note/shared';
|
||||||
|
|
||||||
// --- Contexts ---
|
// --- Contexts ---
|
||||||
|
|
@ -203,8 +203,9 @@ export async function getAllHistoryByContext(contextId: string): Promise<(Histor
|
||||||
return allHistory;
|
return allHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createHistoryEntry(topicId: string, date: string, text: string, linkedContextId: string | null = null): Promise<HistoryEntry> {
|
export async function createHistoryEntry(topicId: string, date: string, text: string, linkedContextId: string | null = null, wiedervorlage = false): Promise<HistoryEntry> {
|
||||||
const existing = await getHistoryByTopic(topicId);
|
const existing = await getHistoryByTopic(topicId);
|
||||||
|
const autoWiedervorlage = date > today() || wiedervorlage;
|
||||||
const entry: HistoryEntry = {
|
const entry: HistoryEntry = {
|
||||||
id: newId(),
|
id: newId(),
|
||||||
topicId,
|
topicId,
|
||||||
|
|
@ -213,6 +214,8 @@ export async function createHistoryEntry(topicId: string, date: string, text: st
|
||||||
sortOrder: existing.length,
|
sortOrder: existing.length,
|
||||||
linkedContextId,
|
linkedContextId,
|
||||||
doneAt: null,
|
doneAt: null,
|
||||||
|
wiedervorlageDate: autoWiedervorlage ? date : null,
|
||||||
|
wiedervorlageResolvedAt: null,
|
||||||
updatedAt: now(),
|
updatedAt: now(),
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
version: 1
|
version: 1
|
||||||
|
|
@ -221,6 +224,30 @@ export async function createHistoryEntry(topicId: string, date: string, text: st
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setWiedervorlage(id: string, date: string): Promise<void> {
|
||||||
|
const entry = await db.historyEntries.get(id);
|
||||||
|
if (entry) {
|
||||||
|
await db.historyEntries.update(id, { wiedervorlageDate: date, wiedervorlageResolvedAt: null, updatedAt: now(), version: entry.version + 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveWiedervorlage(id: string): Promise<void> {
|
||||||
|
const entry = await db.historyEntries.get(id);
|
||||||
|
if (entry) {
|
||||||
|
await db.historyEntries.update(id, { wiedervorlageResolvedAt: now(), updatedAt: now(), version: entry.version + 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToTopic(entryId: string, contextId: string): Promise<Topic> {
|
||||||
|
const entry = await db.historyEntries.get(entryId);
|
||||||
|
if (!entry) throw new Error('Entry not found');
|
||||||
|
const lines = entry.text.split('\n');
|
||||||
|
const title = lines[0].trim();
|
||||||
|
const topic = await createTopic(contextId, title);
|
||||||
|
await resolveWiedervorlage(entryId);
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateHistoryEntry(id: string, text: string): Promise<void> {
|
export async function updateHistoryEntry(id: string, text: string): Promise<void> {
|
||||||
const entry = await db.historyEntries.get(id);
|
const entry = await db.historyEntries.get(id);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,15 @@ export class KaNoteDB extends Dexie {
|
||||||
if (ctx.isFavorite === undefined) ctx.isFavorite = false;
|
if (ctx.isFavorite === undefined) ctx.isFavorite = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.version(7).stores({
|
||||||
|
historyEntries: 'id, topicId, date, sortOrder, deletedAt, linkedContextId, doneAt, wiedervorlageDate, wiedervorlageResolvedAt'
|
||||||
|
}).upgrade(tx => {
|
||||||
|
return tx.table('historyEntries').toCollection().modify(entry => {
|
||||||
|
if (entry.wiedervorlageDate === undefined) entry.wiedervorlageDate = null;
|
||||||
|
if (entry.wiedervorlageResolvedAt === undefined) entry.wiedervorlageResolvedAt = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,3 +118,12 @@ export function ratingsForPerson(personName: string) {
|
||||||
.toArray()
|
.toArray()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pendingWiedervorlage(date: string) {
|
||||||
|
return liveQuery(() =>
|
||||||
|
db.historyEntries
|
||||||
|
.where('wiedervorlageDate').belowOrEqual(date)
|
||||||
|
.filter(e => !e.wiedervorlageResolvedAt && !e.deletedAt)
|
||||||
|
.toArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ function mapHistoryEntry(row: typeof historyEntries.$inferSelect): HistoryEntry
|
||||||
sortOrder: row.sortOrder,
|
sortOrder: row.sortOrder,
|
||||||
linkedContextId: row.linkedContextId,
|
linkedContextId: row.linkedContextId,
|
||||||
doneAt: row.doneAt,
|
doneAt: row.doneAt,
|
||||||
|
wiedervorlageDate: (row as any).wiedervorlageDate ?? null,
|
||||||
|
wiedervorlageResolvedAt: (row as any).wiedervorlageResolvedAt ?? null,
|
||||||
updatedAt: row.updatedAt,
|
updatedAt: row.updatedAt,
|
||||||
deletedAt: row.deletedAt,
|
deletedAt: row.deletedAt,
|
||||||
version: row.version,
|
version: row.version,
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@ export interface HistoryEntry extends SyncEntity {
|
||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
linkedContextId: string | null;
|
linkedContextId: string | null;
|
||||||
doneAt: string | null;
|
doneAt: string | null;
|
||||||
|
wiedervorlageDate: string | null; // YYYY-MM-DD
|
||||||
|
wiedervorlageResolvedAt: string | null; // ISO timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Rating extends SyncEntity {
|
export interface Rating extends SyncEntity {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue