added private journal
This commit is contained in:
parent
4d00be63e7
commit
63d36a1611
|
|
@ -1 +1 @@
|
||||||
1.0.52
|
1.0.54
|
||||||
|
|
@ -6,10 +6,13 @@
|
||||||
context: AgendaContext;
|
context: AgendaContext;
|
||||||
mode?: 'prep' | 'meeting';
|
mode?: 'prep' | 'meeting';
|
||||||
onmodechange?: (mode: 'prep' | 'meeting') => void;
|
onmodechange?: (mode: 'prep' | 'meeting') => void;
|
||||||
|
journalScope?: 'business' | 'private';
|
||||||
|
onjournalscopechange?: (scope: 'business' | 'private') => void;
|
||||||
}
|
}
|
||||||
let { context, mode, onmodechange }: Props = $props();
|
let { context, mode, onmodechange, journalScope, onjournalscopechange }: Props = $props();
|
||||||
|
|
||||||
const showModeSwitch = $derived(!!onmodechange && context.type === 'meeting' && context.id !== 'daily-log');
|
const showModeSwitch = $derived(!!onmodechange && context.type === 'meeting' && context.id !== 'daily-log');
|
||||||
|
const showScopeSwitch = $derived(!!onjournalscopechange && context.id === 'daily-log');
|
||||||
const canRename = $derived(context.id !== 'daily-log');
|
const canRename = $derived(context.id !== 'daily-log');
|
||||||
let editing = $state(false);
|
let editing = $state(false);
|
||||||
let nameInput = $state('');
|
let nameInput = $state('');
|
||||||
|
|
@ -83,4 +86,16 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if showScopeSwitch}
|
||||||
|
<div class="flex gap-1 rounded-full bg-[#333] p-1">
|
||||||
|
<button
|
||||||
|
class="rounded-full border-none px-4 py-2 font-bold transition-all {journalScope === 'business' ? 'bg-accent text-white shadow-md' : 'bg-transparent text-[#aaa] cursor-pointer'}"
|
||||||
|
onclick={() => onjournalscopechange?.('business')}
|
||||||
|
>Firma</button>
|
||||||
|
<button
|
||||||
|
class="rounded-full border-none px-4 py-2 font-bold transition-all {journalScope === 'private' ? 'bg-accent text-white shadow-md' : 'bg-transparent text-[#aaa] cursor-pointer'}"
|
||||||
|
onclick={() => onjournalscopechange?.('private')}
|
||||||
|
>Privat</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,15 @@
|
||||||
|
|
||||||
let mode = $state<'prep' | 'meeting'>(contextId === 'daily-log' ? 'meeting' : 'prep');
|
let mode = $state<'prep' | 'meeting'>(contextId === 'daily-log' ? 'meeting' : 'prep');
|
||||||
let activeView = $state('journal');
|
let activeView = $state('journal');
|
||||||
|
const SCOPE_KEY = 'journal-scope';
|
||||||
|
let journalScope = $state<'business' | 'private'>(
|
||||||
|
(typeof localStorage !== 'undefined' ? localStorage.getItem(SCOPE_KEY) : null) === 'private'
|
||||||
|
? 'private' : 'business'
|
||||||
|
);
|
||||||
|
function handleScopeChange(s: 'business' | 'private') {
|
||||||
|
journalScope = s;
|
||||||
|
localStorage.setItem(SCOPE_KEY, s);
|
||||||
|
}
|
||||||
let compact = $state(false);
|
let compact = $state(false);
|
||||||
|
|
||||||
// Rating modal state
|
// Rating modal state
|
||||||
|
|
@ -84,7 +93,7 @@
|
||||||
<div bind:this={containerEl}>
|
<div bind:this={containerEl}>
|
||||||
{#if $context}
|
{#if $context}
|
||||||
{#if isDailyLog}
|
{#if isDailyLog}
|
||||||
<ContextHeader context={$context} />
|
<ContextHeader context={$context} {journalScope} onjournalscopechange={handleScopeChange} />
|
||||||
<ViewTabs context={$context} {activeView} onviewchange={handleViewChange} />
|
<ViewTabs context={$context} {activeView} onviewchange={handleViewChange} />
|
||||||
{:else}
|
{:else}
|
||||||
<ContextHeader context={$context} {mode} onmodechange={handleModeChange} />
|
<ContextHeader context={$context} {mode} onmodechange={handleModeChange} />
|
||||||
|
|
@ -94,7 +103,7 @@
|
||||||
{#if activeView === 'agenda'}
|
{#if activeView === 'agenda'}
|
||||||
<AgendaView {contextId} {mode} />
|
<AgendaView {contextId} {mode} />
|
||||||
{:else if activeView === 'journal'}
|
{:else if activeView === 'journal'}
|
||||||
<JournalView {contextId} />
|
<JournalView {contextId} {journalScope} />
|
||||||
{:else if activeView === 'persons'}
|
{:else if activeView === 'persons'}
|
||||||
<PersonsView {contextId} />
|
<PersonsView {contextId} />
|
||||||
{:else if activeView === 'snoozed'}
|
{:else if activeView === 'snoozed'}
|
||||||
|
|
|
||||||
|
|
@ -221,14 +221,36 @@
|
||||||
upsertContext({ id: context.id, meta: meta as any });
|
upsertContext({ id: context.id, meta: meta as any });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calcAge(birthday: string | undefined): string {
|
||||||
|
if (!birthday) return '';
|
||||||
|
const b = new Date(birthday);
|
||||||
|
if (isNaN(b.getTime())) return '';
|
||||||
|
const now = new Date();
|
||||||
|
let age = now.getFullYear() - b.getFullYear();
|
||||||
|
const md = now.getMonth() - b.getMonth();
|
||||||
|
if (md < 0 || (md === 0 && now.getDate() < b.getDate())) age--;
|
||||||
|
return age >= 0 ? `${age} J.` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcTenure(joinDate: string | undefined): string {
|
||||||
|
if (!joinDate) return '';
|
||||||
|
const j = new Date(joinDate);
|
||||||
|
if (isNaN(j.getTime())) return '';
|
||||||
|
const now = new Date();
|
||||||
|
let years = now.getFullYear() - j.getFullYear();
|
||||||
|
const md = now.getMonth() - j.getMonth();
|
||||||
|
if (md < 0 || (md === 0 && now.getDate() < j.getDate())) years--;
|
||||||
|
return years >= 0 ? `${years} Jahr${years !== 1 ? 'e' : ''}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Person sub-type selector (always visible for persons) -->
|
<!-- Person sub-type selector (always visible for persons) -->
|
||||||
{#if isPerson}
|
{#if isPerson}
|
||||||
{@const subTypeColors = { contact: 'border-[#555] text-[#ccc]', employee: 'border-accent text-accent', colleague: 'border-[#00b894] text-[#00b894]' } as Record<PersonSubType, string>}
|
{@const subTypeColors = { contact: 'border-[#555] text-[#ccc]', employee: 'border-accent text-accent', colleague: 'border-[#00b894] text-[#00b894]', family: 'border-[#e84393] text-[#e84393]', acquaintance: 'border-[#a29bfe] text-[#a29bfe]' } as Record<PersonSubType, string>}
|
||||||
<div class="mb-5 flex items-center gap-3">
|
<div class="mb-5 flex flex-wrap items-center gap-3">
|
||||||
<span class="text-sm text-[#aaa]">Typ:</span>
|
<span class="text-sm text-[#aaa]">Typ:</span>
|
||||||
{#each [['contact', 'Kontakt'], ['employee', 'Mitarbeiter'], ['colleague', 'Kollege']] as [value, label]}
|
{#each [['contact', 'Kontakt'], ['employee', 'Mitarbeiter'], ['colleague', 'Kollege'], ['family', 'Familie'], ['acquaintance', 'Bekannte']] as [value, label]}
|
||||||
<button
|
<button
|
||||||
class="rounded-full border px-3 py-1 text-sm transition-colors {personSubType === value ? subTypeColors[value as PersonSubType] + ' bg-white/10 font-bold' : 'border-[#333] text-[#666] hover:border-[#555] hover:text-[#aaa]'}"
|
class="rounded-full border px-3 py-1 text-sm transition-colors {personSubType === value ? subTypeColors[value as PersonSubType] + ' bg-white/10 font-bold' : 'border-[#333] text-[#666] hover:border-[#555] hover:text-[#aaa]'}"
|
||||||
onclick={() => handleSubTypeChange(value as PersonSubType)}
|
onclick={() => handleSubTypeChange(value as PersonSubType)}
|
||||||
|
|
@ -347,25 +369,46 @@
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
{@const meta = (context.meta ?? { fullName: '', email: '', phone: '', duSince: '' }) as PersonMeta}
|
{@const meta = (context.meta ?? { fullName: '', email: '', phone: '', duSince: '' }) as PersonMeta}
|
||||||
|
{@const age = calcAge(meta.birthday)}
|
||||||
|
{@const tenure = calcTenure(meta.joinDate)}
|
||||||
|
{@const showJoinDate = personSubType === 'employee' || personSubType === 'colleague'}
|
||||||
<div class="mb-2.5 flex flex-col">
|
<div class="mb-2.5 flex flex-col">
|
||||||
<label class="mb-1 text-sm text-[#aaa]">Voller Name:</label>
|
<label class="mb-1 text-sm text-[#aaa]">Voller Name:</label>
|
||||||
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
value={meta.fullName} onchange={(e) => updateMeta('fullName', e.currentTarget.value)} />
|
value={meta.fullName} onchange={(e) => updateMeta('fullName', e.currentTarget.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2.5 flex flex-col">
|
<div class="mb-2.5 grid grid-cols-2 gap-3">
|
||||||
<label class="mb-1 text-sm text-[#aaa]">Email:</label>
|
<div class="flex flex-col">
|
||||||
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
<label class="mb-1 text-sm text-[#aaa]">Email:</label>
|
||||||
value={meta.email} onchange={(e) => updateMeta('email', e.currentTarget.value)} />
|
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
</div>
|
value={meta.email} onchange={(e) => updateMeta('email', e.currentTarget.value)} />
|
||||||
<div class="mb-2.5 flex flex-col">
|
</div>
|
||||||
<label class="mb-1 text-sm text-[#aaa]">Telefon:</label>
|
<div class="flex flex-col">
|
||||||
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
<label class="mb-1 text-sm text-[#aaa]">Telefon:</label>
|
||||||
value={meta.phone} onchange={(e) => updateMeta('phone', e.currentTarget.value)} />
|
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
</div>
|
value={meta.phone} onchange={(e) => updateMeta('phone', e.currentTarget.value)} />
|
||||||
<div class="mb-2.5 flex flex-col">
|
</div>
|
||||||
<label class="mb-1 text-sm text-[#aaa]">Per Du seit:</label>
|
<div class="flex flex-col">
|
||||||
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
<label class="mb-1 text-sm text-[#aaa]">
|
||||||
value={meta.duSince} onchange={(e) => updateMeta('duSince', e.currentTarget.value)} />
|
Geburtstag:{#if age} <span class="text-info font-normal">{age}</span>{/if}
|
||||||
|
</label>
|
||||||
|
<input type="date" class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
|
value={meta.birthday ?? ''} onchange={(e) => updateMeta('birthday', e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<label class="mb-1 text-sm text-[#aaa]">Per Du seit:</label>
|
||||||
|
<input class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
|
value={meta.duSince} onchange={(e) => updateMeta('duSince', e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
{#if showJoinDate}
|
||||||
|
<div class="col-span-2 flex flex-col">
|
||||||
|
<label class="mb-1 text-sm text-[#aaa]">
|
||||||
|
Eintrittsdatum:{#if tenure} <span class="text-[#00b894] font-normal">{tenure}</span>{/if}
|
||||||
|
</label>
|
||||||
|
<input type="date" class="rounded border border-[#555] bg-[#111] px-2.5 py-1.5 text-[#ddd]"
|
||||||
|
value={meta.joinDate ?? ''} onchange={(e) => updateMeta('joinDate', e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2.5 flex flex-col">
|
<div class="mb-2.5 flex flex-col">
|
||||||
<label class="mb-1 text-sm text-[#aaa]">Notizen:</label>
|
<label class="mb-1 text-sm text-[#aaa]">Notizen:</label>
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
contextId: string;
|
contextId: string;
|
||||||
|
journalScope?: 'business' | 'private';
|
||||||
}
|
}
|
||||||
let { contextId }: Props = $props();
|
let { contextId, journalScope = 'business' }: Props = $props();
|
||||||
|
|
||||||
const isDailyLog = $derived(contextId === 'daily-log');
|
const isDailyLog = $derived(contextId === 'daily-log');
|
||||||
|
|
||||||
|
|
@ -45,10 +46,10 @@
|
||||||
.toArray();
|
.toArray();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter journal entries by selected date
|
// Filter journal entries by selected date and scope
|
||||||
const filteredEntries = $derived(
|
const filteredEntries = $derived(
|
||||||
($journalEntries ?? [])
|
($journalEntries ?? [])
|
||||||
.filter(e => e.date === selectedDate)
|
.filter(e => e.date === selectedDate && (journalScope === 'private' ? !!e.isPrivate : !e.isPrivate))
|
||||||
.sort((a, b) => b.sortOrder - a.sortOrder)
|
.sort((a, b) => b.sortOrder - a.sortOrder)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -86,15 +87,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isPrivate = journalScope === 'private';
|
||||||
if (selectedLinkedContextId) {
|
if (selectedLinkedContextId) {
|
||||||
const topic = await createTopic(selectedLinkedContextId, title);
|
const topic = await createTopic(selectedLinkedContextId, title);
|
||||||
if (body) {
|
if (body) {
|
||||||
await createHistoryEntry(topic.id, selectedDate, body);
|
await createHistoryEntry(topic.id, selectedDate, body, null, false, isPrivate);
|
||||||
}
|
}
|
||||||
} 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, null, wiedervorlageChecked);
|
await createHistoryEntry(JOURNAL_TOPIC_ID, selectedDate, text, null, wiedervorlageChecked, isPrivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
entryTitle = '';
|
entryTitle = '';
|
||||||
|
|
@ -186,12 +188,40 @@
|
||||||
}
|
}
|
||||||
return [...groups.entries()].sort(([a], [b]) => b.localeCompare(a));
|
return [...groups.entries()].sort(([a], [b]) => b.localeCompare(a));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Birthday banner — filtered by scope
|
||||||
|
const allPersons = liveQuery(() =>
|
||||||
|
db.contexts.filter(c => !c.deletedAt && c.type === 'person').toArray()
|
||||||
|
);
|
||||||
|
const BUSINESS_SUBTYPES = new Set(['employee', 'colleague']);
|
||||||
|
const PRIVATE_SUBTYPES = new Set(['family', 'acquaintance']);
|
||||||
|
const birthdayPersons = $derived(
|
||||||
|
($allPersons ?? []).filter(p => {
|
||||||
|
const meta = p.meta as { birthday?: string; personSubType?: string } | null;
|
||||||
|
const bd = meta?.birthday;
|
||||||
|
if (!bd || bd.slice(5) !== selectedDate.slice(5)) return false;
|
||||||
|
const sub = meta?.personSubType;
|
||||||
|
if (journalScope === 'private') return !!sub && PRIVATE_SUBTYPES.has(sub);
|
||||||
|
// business: employee/colleague/contact/undefined
|
||||||
|
return !sub || !PRIVATE_SUBTYPES.has(sub);
|
||||||
|
})
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isDailyLog}
|
{#if isDailyLog}
|
||||||
<!-- Daily-log: chronological entry log with date navigation -->
|
<!-- Daily-log: chronological entry log with date navigation -->
|
||||||
<DateNavigator {selectedDate} onchange={(d) => selectedDate = d} />
|
<DateNavigator {selectedDate} onchange={(d) => selectedDate = d} />
|
||||||
|
|
||||||
|
{#if birthdayPersons.length > 0}
|
||||||
|
<div class="mb-4 flex flex-wrap items-center gap-2 rounded-lg border border-[#d4a017] bg-[#2a1f00] px-4 py-3">
|
||||||
|
<span class="text-lg">🎂</span>
|
||||||
|
<span class="text-sm font-bold text-[#d4a017]">Geburtstag heute:</span>
|
||||||
|
{#each birthdayPersons as p}
|
||||||
|
<span class="rounded-full bg-[#d4a017]/20 px-2 py-0.5 text-sm text-[#f0c040]">{p.name}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="mb-8 flex flex-col gap-2.5 rounded-lg border border-border bg-sidebar p-4">
|
<div class="mb-8 flex flex-col gap-2.5 rounded-lg border border-border bg-sidebar p-4">
|
||||||
<div class="relative flex items-center gap-2">
|
<div class="relative flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
|
@ -257,7 +287,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WiedervorlageSection date={selectedDate} />
|
<WiedervorlageSection date={selectedDate} {journalScope} />
|
||||||
|
|
||||||
{#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">
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,24 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
date: string;
|
date: string;
|
||||||
|
journalScope?: 'business' | 'private';
|
||||||
}
|
}
|
||||||
let { date }: Props = $props();
|
let { date, journalScope = 'business' }: Props = $props();
|
||||||
|
|
||||||
const pending = $derived(pendingWiedervorlage(date));
|
const allPending = $derived(pendingWiedervorlage(date));
|
||||||
|
const pending = $derived(
|
||||||
|
($allPending ?? []).filter(e =>
|
||||||
|
journalScope === 'private' ? !!e.isPrivate : !e.isPrivate
|
||||||
|
)
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if ($pending ?? []).length > 0}
|
{#if pending.length > 0}
|
||||||
<div class="mb-6 rounded-lg border border-amber-500/40 bg-amber-950/20 p-4">
|
<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">
|
<div class="mb-3 text-sm font-semibold uppercase tracking-wider text-amber-400">
|
||||||
Wiedervorlage ({($pending ?? []).length})
|
Wiedervorlage ({pending.length})
|
||||||
</div>
|
</div>
|
||||||
{#each $pending ?? [] as entry (entry.id)}
|
{#each pending as entry (entry.id)}
|
||||||
<WiedervorlageCard {entry} />
|
<WiedervorlageCard {entry} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ 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, wiedervorlage = false): Promise<HistoryEntry> {
|
export async function createHistoryEntry(topicId: string, date: string, text: string, linkedContextId: string | null = null, wiedervorlage = false, isPrivate = false): Promise<HistoryEntry> {
|
||||||
const existing = await getHistoryByTopic(topicId);
|
const existing = await getHistoryByTopic(topicId);
|
||||||
const autoWiedervorlage = date > today() || wiedervorlage;
|
const autoWiedervorlage = date > today() || wiedervorlage;
|
||||||
const entry: HistoryEntry = {
|
const entry: HistoryEntry = {
|
||||||
|
|
@ -226,7 +226,8 @@ export async function createHistoryEntry(topicId: string, date: string, text: st
|
||||||
wiedervorlageResolvedAt: null,
|
wiedervorlageResolvedAt: null,
|
||||||
updatedAt: now(),
|
updatedAt: now(),
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
version: 1
|
version: 1,
|
||||||
|
...(isPrivate ? { isPrivate: true } : {})
|
||||||
};
|
};
|
||||||
await db.historyEntries.put(entry);
|
await db.historyEntries.put(entry);
|
||||||
return entry;
|
return entry;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export interface SyncEntity {
|
||||||
|
|
||||||
export type ContextType = 'meeting' | 'project' | 'person' | 'company';
|
export type ContextType = 'meeting' | 'project' | 'person' | 'company';
|
||||||
|
|
||||||
export type PersonSubType = 'contact' | 'employee' | 'colleague';
|
export type PersonSubType = 'contact' | 'employee' | 'colleague' | 'family' | 'acquaintance';
|
||||||
|
|
||||||
export interface AgendaContext extends SyncEntity {
|
export interface AgendaContext extends SyncEntity {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -30,6 +30,8 @@ export interface PersonMeta {
|
||||||
email: string;
|
email: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
duSince: string;
|
duSince: string;
|
||||||
|
birthday?: string; // YYYY-MM-DD
|
||||||
|
joinDate?: string; // YYYY-MM-DD (employee/colleague)
|
||||||
personSubType?: PersonSubType;
|
personSubType?: PersonSubType;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -60,6 +62,7 @@ export interface HistoryEntry extends SyncEntity {
|
||||||
doneAt: string | null;
|
doneAt: string | null;
|
||||||
wiedervorlageDate: string | null; // YYYY-MM-DD
|
wiedervorlageDate: string | null; // YYYY-MM-DD
|
||||||
wiedervorlageResolvedAt: string | null; // ISO timestamp
|
wiedervorlageResolvedAt: string | null; // ISO timestamp
|
||||||
|
isPrivate?: boolean; // undefined/false = business (default)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Rating extends SyncEntity {
|
export interface Rating extends SyncEntity {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue