kin: KIN-UI-026-frontend_dev

This commit is contained in:
Gros Frumos 2026-03-21 09:24:13 +02:00
parent 79ff03ab3d
commit ca47f9c18b

View file

@ -22,6 +22,33 @@ const filteredProjects = computed(() => {
)
})
const SORT_OPTIONS = ['name', 'priority', 'active', 'cost'] as const
type SortOption = typeof SORT_OPTIONS[number]
const selectedSort = ref<SortOption>('name')
const showSortMenu = ref(false)
const sortedProjects = computed(() => {
const list = [...filteredProjects.value]
switch (selectedSort.value) {
case 'priority': return list.sort((a, b) => b.priority - a.priority)
case 'active': return list.sort((a, b) => b.active_tasks - a.active_tasks)
case 'cost': return list.sort((a, b) => (costMap.value[b.id] || 0) - (costMap.value[a.id] || 0))
default: return list.sort((a, b) => a.name.localeCompare(b.name))
}
})
const GROUP_KEYS = ['development', 'operations', 'research'] as const
const groupedProjects = computed(() => {
const groups: Record<string, Project[]> = { development: [], operations: [], research: [] }
for (const p of sortedProjects.value) {
const ptype = p.project_type || 'development'
const key = ptype in groups ? ptype : 'development'
groups[key].push(p)
}
return groups
})
// Add project modal
const showAdd = ref(false)
const form = ref({
@ -227,17 +254,36 @@ async function createNewProject() {
</div>
</div>
<div v-if="!loading && !error" class="mb-3">
<div v-if="!loading && !error" class="mb-3 flex gap-2">
<input v-model="projectSearch"
:placeholder="t('dashboard.search_placeholder')"
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-1.5 text-sm text-gray-300 placeholder-gray-600 focus:border-gray-500 outline-none" />
class="flex-1 bg-gray-800 border border-gray-700 rounded px-3 py-1.5 text-sm text-gray-300 placeholder-gray-600 focus:border-gray-500 outline-none" />
<div class="relative">
<div v-if="showSortMenu" class="fixed inset-0 z-[5]" @click="showSortMenu = false"></div>
<button @click="showSortMenu = !showSortMenu"
class="px-3 py-1.5 text-xs bg-gray-800 text-gray-400 border border-gray-700 rounded hover:bg-gray-700 flex items-center gap-1 whitespace-nowrap">
{{ selectedSort }}
</button>
<div v-if="showSortMenu" class="absolute right-0 top-full mt-1 bg-gray-900 border border-gray-700 rounded shadow-lg z-10 min-w-[100px]">
<button v-for="opt in SORT_OPTIONS" :key="opt"
@click="selectedSort = opt; showSortMenu = false"
class="w-full text-left px-3 py-2 text-xs hover:bg-gray-800"
:class="selectedSort === opt ? 'text-blue-400' : 'text-gray-400'">
{{ opt }}
</button>
</div>
</div>
</div>
<p v-if="loading" class="text-gray-500 text-sm">{{ t('dashboard.loading') }}</p>
<p v-else-if="error" class="text-red-400 text-sm">{{ error }}</p>
<div v-else class="grid gap-3">
<div v-for="p in filteredProjects" :key="p.id">
<div v-else class="space-y-4">
<template v-for="groupKey in GROUP_KEYS" :key="groupKey">
<div v-if="groupedProjects[groupKey].length > 0">
<p class="text-xs text-gray-600 mb-2 text-center tracking-wider"> {{ groupKey }} ({{ groupedProjects[groupKey].length }}) </p>
<div class="grid gap-3">
<div v-for="p in groupedProjects[groupKey]" :key="p.id">
<!-- Inline delete confirmation -->
<div v-if="confirmDeleteId === p.id"
class="border border-red-800 rounded-lg p-4 bg-red-950/20">
@ -304,6 +350,9 @@ async function createNewProject() {
</div>
</router-link>
</div>
</div>
</div>
</template>
</div>
<!-- Add Project Modal -->