big refactor
This commit is contained in:
153
app/components/back-office/KanbanColumn.vue
Normal file
153
app/components/back-office/KanbanColumn.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
import KanbanTaskCard from './KanbanTaskCard.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
status: string
|
||||
tasks: any[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function navigateToTask(task: any) {
|
||||
router.push(`/back-office/workload/${encodeURIComponent(task.id)}`)
|
||||
}
|
||||
|
||||
// Stage configuration for colors
|
||||
const stageConfig: Record<string, {
|
||||
color: string;
|
||||
dot: string;
|
||||
headerBg: string;
|
||||
}> = {
|
||||
created: {
|
||||
color: 'text-[var(--text-muted)]',
|
||||
dot: 'bg-[var(--text-muted)]',
|
||||
headerBg: 'bg-[var(--surface)] border-[var(--card-border)]'
|
||||
},
|
||||
draft: {
|
||||
color: 'text-[var(--brand)]',
|
||||
dot: 'bg-[var(--brand)]',
|
||||
headerBg: 'bg-[var(--brand-faint)] border-[var(--brand-soft)]'
|
||||
},
|
||||
approved: {
|
||||
color: 'text-emerald-700',
|
||||
dot: 'bg-emerald-500',
|
||||
headerBg: 'bg-emerald-50 border-emerald-200'
|
||||
},
|
||||
completed: {
|
||||
color: 'text-gray-500',
|
||||
dot: 'bg-gray-400',
|
||||
headerBg: 'bg-gray-50 border-gray-200'
|
||||
}
|
||||
}
|
||||
|
||||
function daysInStage(createdAt: string): string {
|
||||
const days = Math.floor((Date.now() - new Date(createdAt).getTime()) / (1000 * 60 * 60 * 24))
|
||||
return days === 0 ? 'Today' : `${days}d`
|
||||
}
|
||||
|
||||
function taskUrgent(task: any): boolean {
|
||||
// Placeholder - will be based on priority/due_date from backend
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="kanban-column">
|
||||
<div class="column-header" :class="stageConfig[status].headerBg">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="h-2 w-2 rounded-full" :class="stageConfig[status].dot" />
|
||||
<span class="text-[13px] font-semibold" :class="stageConfig[status].color">
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="flex h-5 min-w-[20px] items-center justify-center rounded-full bg-[var(--surface)]/70 px-1.5 text-[11px] font-semibold text-[var(--text-muted)] ring-1 ring-[var(--card-border)]/60">
|
||||
{{ tasks.length }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-list">
|
||||
<KanbanTaskCard
|
||||
v-for="task in tasks"
|
||||
:key="task.id"
|
||||
:task="task"
|
||||
:urgent="taskUrgent(task)"
|
||||
@click="navigateToTask(task)"
|
||||
/>
|
||||
|
||||
<div v-if="tasks.length === 0 && !loading" class="empty-state">
|
||||
<UIcon name="i-heroicons-inbox" class="w-8 h-8" />
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading-state">
|
||||
<div v-for="n in 3" :key="n" class="skeleton-card" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.kanban-column {
|
||||
flex: 0 0 240px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--card-border);
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--card-border);
|
||||
background: var(--surface);
|
||||
border-radius: 8px 8px 0 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.task-list {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32px 16px;
|
||||
color: var(--text-muted);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
height: 100px;
|
||||
background: var(--card-border);
|
||||
border-radius: 6px;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 0.6; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user