Skip to main content
Glama
KanbanColumn.svelte4.41 kB
<script> import { createEventDispatcher } from 'svelte'; export let title; export let tasks = []; export let prdLookup = {}; export let colorClass = 'bg-gray-50 border-gray-200'; const dispatch = createEventDispatcher(); function getPriorityColor(priority) { switch (priority) { case 'critical': return 'border-l-red-800 bg-red-50'; case 'high': return 'border-l-red-500 bg-red-50'; case 'medium': return 'border-l-yellow-500 bg-yellow-50'; case 'low': return 'border-l-green-500 bg-green-50'; default: return 'border-l-gray-300 bg-white'; } } function formatDate(dateString) { if (!dateString) return ''; return new Date(dateString).toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' }); } function handleDragStart(event, task) { dispatch('dragStart', { event, task }); } </script> <div class="h-full"> <!-- Column Header --> <div class="border-2 border-dashed {colorClass} rounded-lg p-4 mb-4"> <div class="flex items-center justify-between"> <h3 class="font-semibold text-gray-900">{title}</h3> <span class="text-sm text-gray-500 bg-gray-200 px-2 py-1 rounded-full"> {tasks.length} </span> </div> </div> <!-- Task Cards --> <div class="space-y-3"> {#each tasks as task (task.id)} <div class="kanban-card card border-l-4 {getPriorityColor(task.priority)}" draggable="true" on:dragstart={(e) => handleDragStart(e, task)} > <div class="p-4"> <div class="flex items-start justify-between mb-2"> <h4 class="text-sm font-medium text-gray-900 line-clamp-2"> {task.title} </h4> <div class="ml-2 flex-shrink-0"> {#if task.priority === 'critical'} <span class="text-red-600">🔥</span> {:else if task.priority === 'high'} <span class="text-red-500">⚠️</span> {:else if task.priority === 'medium'} <span class="text-yellow-500">📋</span> {:else} <span class="text-green-500">📝</span> {/if} </div> </div> {#if task.description} <p class="text-xs text-gray-600 mb-3 line-clamp-2"> {task.description} </p> {/if} <!-- PRD Reference --> {#if task.prd_id && prdLookup[task.prd_id]} <div class="mb-3"> <span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-blue-100 text-blue-800"> 📋 {prdLookup[task.prd_id]} </span> </div> {/if} <!-- Tags --> {#if task.tags} {@const parsedTags = (() => { try { // tags가 이미 배열이면 그대로 사용 if (Array.isArray(task.tags)) return task.tags; // 문자열이면 JSON 파싱 시도 if (typeof task.tags === 'string') { // JSON 배열 형태인지 확인 if (task.tags.startsWith('[') && task.tags.endsWith(']')) { return JSON.parse(task.tags); } // 쉼표로 구분된 문자열인지 확인 return task.tags.split(',').map(tag => tag.trim()).filter(tag => tag); } return []; } catch (e) { // JSON 파싱 실패 시 쉼표로 구분된 문자열로 처리 if (typeof task.tags === 'string') { return task.tags.split(',').map(tag => tag.trim()).filter(tag => tag); } return []; } })()} {#if parsedTags.length > 0} <div class="flex flex-wrap gap-1 mb-3"> {#each parsedTags as tag} <span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-gray-100 text-gray-700"> {tag} </span> {/each} </div> {/if} {/if} <!-- Due Date --> {#if task.due_date} <div class="flex items-center text-xs text-gray-500 mb-2"> <span class="mr-1">📅</span> {formatDate(task.due_date)} </div> {/if} <!-- Task Info --> <div class="flex items-center justify-between text-xs text-gray-500"> <span>#{task.id}</span> <span class="capitalize priority-{task.priority}"> {task.priority === 'critical' ? '긴급' : task.priority === 'high' ? '높음' : task.priority === 'medium' ? '보통' : '낮음'} </span> </div> </div> </div> {/each} </div> </div> <style> .line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } </style>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/foswmine/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server