import { useEffect, useState } from 'react' import { View, Text, ScrollView, StyleSheet, TouchableOpacity, ActivityIndicator, RefreshControl, Alert, } from 'react-native' import { router } from 'expo-router' import { COLORS, PRIORITY_COLOR } from '../../constants/Config' import { getSRList, patchSR } from '../../services/api' interface SR { id: number sr_id?: string title: string status?: string priority?: string } const COLUMNS: { key: string; label: string; color: string }[] = [ { key: 'RECEIVED', label: '접수', color: '#94a3b8' }, { key: 'IN_PROGRESS', label: '진행중', color: '#4f6ef7' }, { key: 'PENDING_APPROVAL', label: '승인대기', color: '#f59e0b' }, { key: 'COMPLETED', label: '완료', color: '#22c55e' }, ] /** * 기능 #5 — Kanban SR 보드 * 상태별 컬럼. 카드의 ◀ ▶ 버튼으로 이전/다음 상태 이동 (PATCH /api/tasks/{id}). */ export default function KanbanScreen() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(true) const [refresh, setRefresh] = useState(false) const load = async (r = false) => { r ? setRefresh(true) : setLoading(true) try { const res = await getSRList(0, 100) setItems(res.data?.content ?? res.data?.items ?? res.data ?? []) } catch { setItems([]) } finally { setLoading(false); setRefresh(false) } } useEffect(() => { load() }, []) const move = async (sr: SR, dir: -1 | 1) => { const idx = COLUMNS.findIndex(c => c.key === sr.status) const nextIdx = idx + dir if (idx < 0 || nextIdx < 0 || nextIdx >= COLUMNS.length) return const nextStatus = COLUMNS[nextIdx].key setItems(prev => prev.map(i => (i.id === sr.id ? { ...i, status: nextStatus } : i))) try { await patchSR(sr.id, { status: nextStatus }) } catch (e: any) { setItems(prev => prev.map(i => (i.id === sr.id ? { ...i, status: sr.status } : i))) Alert.alert('오류', e.response?.data?.detail ?? '상태 변경 실패') } } if (loading) return return ( load(true)} />} > {COLUMNS.map((col, ci) => { const cards = items.filter(i => i.status === col.key) return ( {col.label} {cards.length} {cards.length === 0 && 비어 있음} {cards.map(sr => ( router.push({ pathname: '/(tabs)/sr_detail', params: { id: String(sr.id) } })}> {sr.sr_id ?? `#${sr.id}`} {sr.title} {!!sr.priority && ( ● {sr.priority} )} move(sr, -1)} disabled={ci === 0} > move(sr, 1)} disabled={ci === COLUMNS.length - 1} > ))} ) })} ) } const s = StyleSheet.create({ column: { width: 240, marginRight: 12, backgroundColor: '#fff', borderRadius: 12, padding: 8, maxHeight: '100%' }, colHead: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderTopWidth: 3, paddingTop: 8, paddingHorizontal: 6, paddingBottom: 8, marginBottom: 6 }, colTitle: { fontSize: 14, fontWeight: '800', color: COLORS.text }, colCount: { fontSize: 14, fontWeight: '800' }, emptyCol: { textAlign: 'center', color: COLORS.muted, fontSize: 12, paddingVertical: 16 }, card: { backgroundColor: COLORS.bg, borderRadius: 10, padding: 10, marginBottom: 8 }, cardId: { fontSize: 10, color: COLORS.accent, fontWeight: '700' }, cardTitle: { fontSize: 13, color: COLORS.text, marginTop: 4, lineHeight: 18 }, cardPri: { fontSize: 10, fontWeight: '700', marginTop: 6 }, moveRow: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 8 }, moveBtn: { backgroundColor: COLORS.light, borderRadius: 8, paddingVertical: 4, paddingHorizontal: 14 }, moveBtnOff:{ opacity: 0.3 }, moveText: { color: COLORS.accent, fontWeight: '700', fontSize: 13 }, })