feat: Trail-Karten zeigen Marker und Kommentare, Icons fuer Laenge und Hoehe
Laenge und Hoehenmeter tragen dieselben Icons wie der Kopf der Trail-Seite. Dazu kommen Anzahl der Marker und Kommentare - und ein Punkt daneben, wenn davon etwas aus den letzten 14 Tagen stammt. Die blosse Zahl sagt nichts darueber, ob sich gerade etwas tut. Kommentare bekommen dafuer einen eigenen Store. Sie lagen bisher direkt in der Trail-Seite, was richtig war, solange es sie nur dort gab; fuer die Liste waere ein zweiter Ladeweg auf dieselben Daten einer zu viel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014dh9W1i7aLSdPYJzQPid5o
This commit is contained in:
parent
27d34ad0d3
commit
fb2652dcbf
6 changed files with 194 additions and 59 deletions
104
frontend/src/lib/stores/trailComments.svelte.ts
Normal file
104
frontend/src/lib/stores/trailComments.svelte.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { getContext, setContext } from 'svelte'
|
||||||
|
import { api, auth } from './pocketbase.svelte'
|
||||||
|
import { getTeamContext } from './teams.svelte'
|
||||||
|
import { isRecent } from '$lib/time'
|
||||||
|
import type { TrailCommentsResponse, UsersResponse } from '$lib/types'
|
||||||
|
|
||||||
|
const KEY = Symbol('trailComment')
|
||||||
|
|
||||||
|
/** Kommentar samt Verfasser, sofern dessen Datensatz sichtbar ist. */
|
||||||
|
export type TrailComment = Omit<TrailCommentsResponse, 'expand'> & {
|
||||||
|
expand?: { created_by?: UsersResponse }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kommentare lagen bisher direkt in der Trail-Seite — als es sie nur dort
|
||||||
|
* gab, war das die einfachere Lösung. Die Trail-Liste zeigt jetzt ebenfalls
|
||||||
|
* ihre Anzahl, und zwei Ladewege auf dieselben Daten wären einer zu viel.
|
||||||
|
*/
|
||||||
|
export class TrailCommentStore {
|
||||||
|
records = $state<TrailComment[]>([])
|
||||||
|
loading = $state(false)
|
||||||
|
error = $state<string | null>(null)
|
||||||
|
|
||||||
|
private unsubscribe: (() => void) | null = null
|
||||||
|
private teams = getTeamContext()
|
||||||
|
|
||||||
|
get scoped(): TrailComment[] {
|
||||||
|
const teamId = this.teams.activeId
|
||||||
|
if (!teamId) return []
|
||||||
|
return this.records.filter((r) => r.team === teamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Kommentare eines Trails, jüngste zuerst */
|
||||||
|
byTrail(trailId: string): TrailComment[] {
|
||||||
|
return this.scoped
|
||||||
|
.filter((r) => r.trail === trailId)
|
||||||
|
.sort((a, b) => (a.created < b.created ? 1 : -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gab es zuletzt Bewegung? Für die Hervorhebung in der Liste. */
|
||||||
|
hasRecent(trailId: string, days?: number): boolean {
|
||||||
|
return this.byTrail(trailId).some((r) => isRecent(r.created, days))
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.loading = true
|
||||||
|
this.error = null
|
||||||
|
try {
|
||||||
|
this.records = (await api.collection('trail_comments').getFullList({
|
||||||
|
sort: '-created',
|
||||||
|
expand: 'created_by',
|
||||||
|
requestKey: null,
|
||||||
|
})) as TrailComment[]
|
||||||
|
} catch (e: any) {
|
||||||
|
this.error = e.message ?? 'Fehler beim Laden der Kommentare'
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
if (this.unsubscribe) return
|
||||||
|
api.collection('trail_comments').subscribe('*', (e) => {
|
||||||
|
const record = e.record as TrailComment
|
||||||
|
const idx = this.records.findIndex((r) => r.id === record.id)
|
||||||
|
if (e.action === 'create' && idx === -1) this.records = [record, ...this.records]
|
||||||
|
else if (e.action === 'update' && idx !== -1) this.records[idx] = record
|
||||||
|
else if (e.action === 'delete' && idx !== -1) this.records = this.records.filter((r) => r.id !== record.id)
|
||||||
|
}, { expand: 'created_by' }).then((unsub) => {
|
||||||
|
this.unsubscribe = unsub
|
||||||
|
}).catch((e) => console.warn('trail_comments subscribe failed:', e))
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(trailId: string, teamId: string, text: string) {
|
||||||
|
return await api.collection('trail_comments').create({
|
||||||
|
trail: trailId,
|
||||||
|
team: teamId,
|
||||||
|
text: text.trim(),
|
||||||
|
created_by: auth.user?.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string) {
|
||||||
|
await api.collection('trail_comments').delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe()
|
||||||
|
this.unsubscribe = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setTrailCommentContext() {
|
||||||
|
const store = new TrailCommentStore()
|
||||||
|
setContext(KEY, store)
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTrailCommentContext(): TrailCommentStore {
|
||||||
|
return getContext<TrailCommentStore>(KEY)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { getContext, setContext } from 'svelte'
|
import { getContext, setContext } from 'svelte'
|
||||||
import { api, auth } from './pocketbase.svelte'
|
import { api, auth } from './pocketbase.svelte'
|
||||||
import { getTeamContext } from './teams.svelte'
|
import { getTeamContext } from './teams.svelte'
|
||||||
|
import { isRecent } from '$lib/time'
|
||||||
import type { TrailMarkersResponse, UsersResponse } from '$lib/types'
|
import type { TrailMarkersResponse, UsersResponse } from '$lib/types'
|
||||||
|
|
||||||
const KEY = Symbol('trailMarker')
|
const KEY = Symbol('trailMarker')
|
||||||
|
|
@ -28,6 +29,11 @@ export class TrailMarkerStore {
|
||||||
.sort((a, b) => (a.created < b.created ? 1 : -1))
|
.sort((a, b) => (a.created < b.created ? 1 : -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gab es zuletzt Bewegung? Für die Hervorhebung in der Liste. */
|
||||||
|
hasRecent(trailId: string, days?: number): boolean {
|
||||||
|
return this.byTrail(trailId).some((r) => isRecent(r.created, days))
|
||||||
|
}
|
||||||
|
|
||||||
/** Nur die offenen — was auf der Karte auffallen soll */
|
/** Nur die offenen — was auf der Karte auffallen soll */
|
||||||
openByTrail(trailId: string): TrailMarker[] {
|
openByTrail(trailId: string): TrailMarker[] {
|
||||||
return this.byTrail(trailId).filter((r) => !r.resolved)
|
return this.byTrail(trailId).filter((r) => !r.resolved)
|
||||||
|
|
|
||||||
|
|
@ -70,3 +70,19 @@ export function formatDateRange(starts: string | undefined, ends: string | undef
|
||||||
|
|
||||||
return `${from} – ${to}`
|
return `${from} – ${to}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standardfenster für „neu" in Übersichten. Zwei Wochen, weil ein Trail
|
||||||
|
* selten öfter befahren wird — kürzer wäre die Hervorhebung meist leer.
|
||||||
|
*/
|
||||||
|
export const RECENT_DAYS = 14
|
||||||
|
|
||||||
|
/** Liegt der Zeitpunkt innerhalb der letzten `days` Tage? */
|
||||||
|
export function isRecent(value: string | undefined, days = RECENT_DAYS): boolean {
|
||||||
|
if (!value) return false
|
||||||
|
|
||||||
|
const then = new Date(value).getTime()
|
||||||
|
if (!Number.isFinite(then)) return false
|
||||||
|
|
||||||
|
return Date.now() - then <= days * 24 * 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
import { setTrailVersionContext } from '$lib/stores/trailVersions.svelte'
|
import { setTrailVersionContext } from '$lib/stores/trailVersions.svelte'
|
||||||
import { setTrailFlagContext } from '$lib/stores/trailFlags.svelte'
|
import { setTrailFlagContext } from '$lib/stores/trailFlags.svelte'
|
||||||
import { setTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
import { setTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
||||||
|
import { setTrailCommentContext } from '$lib/stores/trailComments.svelte'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import Logo from '$lib/components/Logo.svelte'
|
import Logo from '$lib/components/Logo.svelte'
|
||||||
|
|
@ -34,6 +35,7 @@
|
||||||
const trailVersions = setTrailVersionContext()
|
const trailVersions = setTrailVersionContext()
|
||||||
const trailFlags = setTrailFlagContext()
|
const trailFlags = setTrailFlagContext()
|
||||||
const trailMarkers = setTrailMarkerContext()
|
const trailMarkers = setTrailMarkerContext()
|
||||||
|
const trailComments = setTrailCommentContext()
|
||||||
|
|
||||||
let isMobileMenuOpen = $state(false)
|
let isMobileMenuOpen = $state(false)
|
||||||
|
|
||||||
|
|
@ -48,6 +50,7 @@
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
events.load(), runs.load(), riders.load(), times.load(),
|
events.load(), runs.load(), riders.load(), times.load(),
|
||||||
trails.load(), trailVersions.load(), trailFlags.load(), trailMarkers.load(),
|
trails.load(), trailVersions.load(), trailFlags.load(), trailMarkers.load(),
|
||||||
|
trailComments.load(),
|
||||||
])
|
])
|
||||||
events.subscribe()
|
events.subscribe()
|
||||||
runs.subscribe()
|
runs.subscribe()
|
||||||
|
|
@ -57,6 +60,7 @@
|
||||||
trailVersions.subscribe()
|
trailVersions.subscribe()
|
||||||
trailFlags.subscribe()
|
trailFlags.subscribe()
|
||||||
trailMarkers.subscribe()
|
trailMarkers.subscribe()
|
||||||
|
trailComments.subscribe()
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
|
|
@ -69,6 +73,7 @@
|
||||||
trailVersions.destroy()
|
trailVersions.destroy()
|
||||||
trailFlags.destroy()
|
trailFlags.destroy()
|
||||||
trailMarkers.destroy()
|
trailMarkers.destroy()
|
||||||
|
trailComments.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,23 @@
|
||||||
import { getTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
import { getTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
||||||
import TrailMap from '$lib/components/TrailMap.svelte'
|
import TrailMap from '$lib/components/TrailMap.svelte'
|
||||||
import { trailStatus } from '$lib/trailStatus'
|
import { trailStatus } from '$lib/trailStatus'
|
||||||
|
import { getTrailCommentContext } from '$lib/stores/trailComments.svelte'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import * as Dialog from '@/components/ui/dialog'
|
import * as Dialog from '@/components/ui/dialog'
|
||||||
import { Plus, Route, AlertTriangle } from 'lucide-svelte'
|
import {
|
||||||
|
Plus, Route, AlertTriangle, Ruler, TrendingUp, MapPin, MessageSquare,
|
||||||
|
} from 'lucide-svelte'
|
||||||
|
import { RECENT_DAYS } from '$lib/time'
|
||||||
import type { TrailVersionsResponse } from '$lib/types'
|
import type { TrailVersionsResponse } from '$lib/types'
|
||||||
|
|
||||||
const trails = getTrailContext()
|
const trails = getTrailContext()
|
||||||
const versions = getTrailVersionContext()
|
const versions = getTrailVersionContext()
|
||||||
const markers = getTrailMarkerContext()
|
const markers = getTrailMarkerContext()
|
||||||
|
const comments = getTrailCommentContext()
|
||||||
|
|
||||||
let dialog = $state(false)
|
let dialog = $state(false)
|
||||||
let form = $state({ name: '', description: '' })
|
let form = $state({ name: '', description: '' })
|
||||||
|
|
@ -109,6 +114,10 @@
|
||||||
{@const version = currentVersion(trail.id)}
|
{@const version = currentVersion(trail.id)}
|
||||||
{@const open = markers.openByTrail(trail.id).length}
|
{@const open = markers.openByTrail(trail.id).length}
|
||||||
{@const status = trailStatus(trail.status)}
|
{@const status = trailStatus(trail.status)}
|
||||||
|
{@const markerCount = markers.byTrail(trail.id).length}
|
||||||
|
{@const commentCount = comments.byTrail(trail.id).length}
|
||||||
|
{@const freshMarkers = markers.hasRecent(trail.id)}
|
||||||
|
{@const freshComments = comments.hasRecent(trail.id)}
|
||||||
<a href="/dashboard/trails/{trail.id}" class="block">
|
<a href="/dashboard/trails/{trail.id}" class="block">
|
||||||
<Card class="h-full hover:border-primary transition-colors">
|
<Card class="h-full hover:border-primary transition-colors">
|
||||||
<CardHeader class="pb-3">
|
<CardHeader class="pb-3">
|
||||||
|
|
@ -139,15 +148,60 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex items-center gap-4 text-sm text-muted-foreground">
|
<div class="flex items-center gap-4 text-sm text-muted-foreground">
|
||||||
<span>{formatKm(version?.distance_m)}</span>
|
<!-- Dieselben Icons wie im Kopf der Trail-Seite. -->
|
||||||
<span>{version?.ascent_m ? `${version.ascent_m} hm` : '–'}</span>
|
<span class="flex items-center gap-1.5">
|
||||||
|
<Ruler class="size-4" />
|
||||||
|
{formatKm(version?.distance_m)}
|
||||||
|
</span>
|
||||||
|
<span class="flex items-center gap-1.5">
|
||||||
|
<TrendingUp class="size-4" />
|
||||||
|
{version?.ascent_m ? `${version.ascent_m} hm` : '–'}
|
||||||
|
</span>
|
||||||
{#if open > 0}
|
{#if open > 0}
|
||||||
<span class="flex items-center gap-1 text-amber-600 ml-auto">
|
<span
|
||||||
|
class="flex items-center gap-1 text-amber-600 ml-auto"
|
||||||
|
title="{open} offene Meldung{open === 1 ? '' : 'en'}"
|
||||||
|
>
|
||||||
<AlertTriangle class="size-4" />
|
<AlertTriangle class="size-4" />
|
||||||
{open}
|
{open}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Ein Punkt markiert Bewegung der letzten zwei Wochen. Die
|
||||||
|
Zahl allein sagt nichts darüber, ob sich gerade etwas tut.
|
||||||
|
-->
|
||||||
|
<div class="flex items-center gap-4 text-sm border-t pt-3">
|
||||||
|
<span
|
||||||
|
class="flex items-center gap-1.5"
|
||||||
|
class:text-muted-foreground={!freshMarkers}
|
||||||
|
class:font-medium={freshMarkers}
|
||||||
|
title={freshMarkers
|
||||||
|
? `Marker, neue in den letzten ${RECENT_DAYS} Tagen`
|
||||||
|
: 'Marker'}
|
||||||
|
>
|
||||||
|
<MapPin class="size-4" />
|
||||||
|
{markerCount}
|
||||||
|
{#if freshMarkers}
|
||||||
|
<span class="size-1.5 rounded-full bg-primary"></span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="flex items-center gap-1.5"
|
||||||
|
class:text-muted-foreground={!freshComments}
|
||||||
|
class:font-medium={freshComments}
|
||||||
|
title={freshComments
|
||||||
|
? `Kommentare, neue in den letzten ${RECENT_DAYS} Tagen`
|
||||||
|
: 'Kommentare'}
|
||||||
|
>
|
||||||
|
<MessageSquare class="size-4" />
|
||||||
|
{commentCount}
|
||||||
|
{#if freshComments}
|
||||||
|
<span class="size-1.5 rounded-full bg-primary"></span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
import { getTrailVersionContext } from '$lib/stores/trailVersions.svelte'
|
import { getTrailVersionContext } from '$lib/stores/trailVersions.svelte'
|
||||||
import { getTrailFlagContext } from '$lib/stores/trailFlags.svelte'
|
import { getTrailFlagContext } from '$lib/stores/trailFlags.svelte'
|
||||||
import { getTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
import { getTrailMarkerContext } from '$lib/stores/trailMarkers.svelte'
|
||||||
|
import { getTrailCommentContext } from '$lib/stores/trailComments.svelte'
|
||||||
import { getTeamContext } from '$lib/stores/teams.svelte'
|
import { getTeamContext } from '$lib/stores/teams.svelte'
|
||||||
import TrailMap from '$lib/components/TrailMap.svelte'
|
import TrailMap from '$lib/components/TrailMap.svelte'
|
||||||
import ElevationProfile from '$lib/components/ElevationProfile.svelte'
|
import ElevationProfile from '$lib/components/ElevationProfile.svelte'
|
||||||
|
|
@ -30,7 +31,7 @@
|
||||||
} from 'lucide-svelte'
|
} from 'lucide-svelte'
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
import { browser } from '$app/environment'
|
import { browser } from '$app/environment'
|
||||||
import type { TrailCommentsResponse, TrailVersionsResponse, UsersResponse } from '$lib/types'
|
import type { TrailVersionsResponse } from '$lib/types'
|
||||||
|
|
||||||
// Generics der JSON-Felder, wie in trails/+page.svelte — die
|
// Generics der JSON-Felder, wie in trails/+page.svelte — die
|
||||||
// Rohdaten kommen ungetypt aus PocketBase zurück.
|
// Rohdaten kommen ungetypt aus PocketBase zurück.
|
||||||
|
|
@ -47,6 +48,7 @@
|
||||||
const versions = getTrailVersionContext()
|
const versions = getTrailVersionContext()
|
||||||
const flags = getTrailFlagContext()
|
const flags = getTrailFlagContext()
|
||||||
const markers = getTrailMarkerContext()
|
const markers = getTrailMarkerContext()
|
||||||
|
const commentStore = getTrailCommentContext()
|
||||||
const teams = getTeamContext()
|
const teams = getTeamContext()
|
||||||
|
|
||||||
const trail = $derived(page.params.id ? trails.getById(page.params.id) : undefined)
|
const trail = $derived(page.params.id ? trails.getById(page.params.id) : undefined)
|
||||||
|
|
@ -256,69 +258,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Kommentare ------------------------------------------------------
|
// --- Kommentare ------------------------------------------------------
|
||||||
// Eigener Store wäre Overkill: Kommentare werden nur hier gebraucht.
|
const comments = $derived(trail ? commentStore.byTrail(trail.id) : [])
|
||||||
type Comment = TrailCommentsResponse<{ created_by?: UsersResponse }>
|
|
||||||
|
|
||||||
let comments = $state<Comment[]>([])
|
|
||||||
let commentText = $state('')
|
let commentText = $state('')
|
||||||
let commentError = $state<string | null>(null)
|
let commentError = $state<string | null>(null)
|
||||||
let unsubComments: (() => void) | null = null
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
const id = trail?.id
|
|
||||||
if (!id) return
|
|
||||||
|
|
||||||
let cancelled = false
|
|
||||||
|
|
||||||
api.collection('trail_comments')
|
|
||||||
.getFullList({
|
|
||||||
filter: `trail="${id}"`,
|
|
||||||
sort: '-created',
|
|
||||||
expand: 'created_by',
|
|
||||||
requestKey: null,
|
|
||||||
})
|
|
||||||
.then((r) => {
|
|
||||||
if (!cancelled) comments = r as Comment[]
|
|
||||||
})
|
|
||||||
.catch((e) => console.error('Kommentare laden fehlgeschlagen:', e))
|
|
||||||
|
|
||||||
api.collection('trail_comments')
|
|
||||||
.subscribe(
|
|
||||||
'*',
|
|
||||||
(e) => {
|
|
||||||
if (e.record.trail !== id) return
|
|
||||||
const record = e.record as Comment
|
|
||||||
const idx = comments.findIndex((c) => c.id === record.id)
|
|
||||||
if (e.action === 'create' && idx === -1) comments = [record, ...comments]
|
|
||||||
else if (e.action === 'update' && idx !== -1) comments[idx] = record
|
|
||||||
else if (e.action === 'delete' && idx !== -1) comments = comments.filter((c) => c.id !== record.id)
|
|
||||||
},
|
|
||||||
{ expand: 'created_by' },
|
|
||||||
)
|
|
||||||
.then((u) => {
|
|
||||||
if (cancelled) u()
|
|
||||||
else unsubComments = u
|
|
||||||
})
|
|
||||||
.catch((e) => console.warn('trail_comments subscribe failed:', e))
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
cancelled = true
|
|
||||||
unsubComments?.()
|
|
||||||
unsubComments = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function addComment() {
|
async function addComment() {
|
||||||
if (!trail || !commentText.trim()) return
|
if (!trail || !commentText.trim()) return
|
||||||
|
|
||||||
commentError = null
|
commentError = null
|
||||||
try {
|
try {
|
||||||
await api.collection('trail_comments').create({
|
await commentStore.create(trail.id, trail.team, commentText)
|
||||||
trail: trail.id,
|
|
||||||
team: trail.team,
|
|
||||||
text: commentText.trim(),
|
|
||||||
created_by: auth.user?.id,
|
|
||||||
})
|
|
||||||
commentText = ''
|
commentText = ''
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
commentError = e.message ?? 'Der Kommentar konnte nicht gespeichert werden.'
|
commentError = e.message ?? 'Der Kommentar konnte nicht gespeichert werden.'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue