2026-09-05 00:46:17 +01:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, ref } from 'vue'
|
|
|
|
|
import CardRow from '../components/CardRow.vue'
|
|
|
|
|
import { ApiError } from '../lib/api'
|
|
|
|
|
import { useCardsStore } from '../stores/cards'
|
|
|
|
|
|
|
|
|
|
// The project's cards, flat and sorted by name -- ProjectView (the parent
|
|
|
|
|
// layout) has already loaded them into this store, keyed to the current
|
|
|
|
|
// project, so there's nothing to fetch here.
|
|
|
|
|
const cards = useCardsStore()
|
|
|
|
|
|
|
|
|
|
const actionError = ref<string | null>(null)
|
|
|
|
|
const newText = ref('')
|
|
|
|
|
const submitting = ref(false)
|
|
|
|
|
|
|
|
|
|
const summary = computed(() => {
|
|
|
|
|
const total = cards.cards.length
|
|
|
|
|
if (total === 0) return 'No cards yet.'
|
|
|
|
|
return `${total} card${total === 1 ? '' : 's'}.`
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// No manual order here -- sort by name, case-insensitively.
|
|
|
|
|
const sortedCards = computed(() =>
|
|
|
|
|
[...cards.cards].sort((a, b) => a.text.localeCompare(b.text, undefined, { sensitivity: 'base' })),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async function onCreate() {
|
|
|
|
|
submitting.value = true
|
|
|
|
|
actionError.value = null
|
|
|
|
|
try {
|
|
|
|
|
await cards.add(newText.value)
|
|
|
|
|
newText.value = ''
|
|
|
|
|
} catch (e) {
|
|
|
|
|
actionError.value = e instanceof ApiError ? e.message : 'Could not add the card.'
|
|
|
|
|
} finally {
|
|
|
|
|
submitting.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<p v-if="actionError" class="form-error">{{ actionError }}</p>
|
|
|
|
|
|
|
|
|
|
<p class="muted">{{ summary }}</p>
|
|
|
|
|
|
|
|
|
|
<p v-if="cards.loading && !cards.loaded" class="muted">Loading…</p>
|
|
|
|
|
|
|
|
|
|
<ul v-else-if="sortedCards.length" class="cards">
|
2026-09-05 00:54:36 +01:00
|
|
|
<CardRow v-for="card in sortedCards" :key="card.id" :card="card" />
|
2026-09-05 00:46:17 +01:00
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<form class="kanban__new form--new-card" @submit.prevent="onCreate">
|
|
|
|
|
<input
|
|
|
|
|
v-model="newText"
|
|
|
|
|
class="field field--compact"
|
|
|
|
|
type="text"
|
|
|
|
|
maxlength="1000"
|
|
|
|
|
required
|
|
|
|
|
placeholder="New card"
|
|
|
|
|
aria-label="New card"
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
class="btn-icon"
|
|
|
|
|
:disabled="submitting"
|
|
|
|
|
:title="submitting ? 'Adding…' : 'Add card'"
|
|
|
|
|
:aria-label="submitting ? 'Adding…' : 'Add card'"
|
|
|
|
|
>+</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|