guardia-messenger/components/Comment.tsx

125 lines
4.9 KiB
TypeScript

import { useEffect, useState } from 'react'
import {
View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, Alert,
} from 'react-native'
import { COLORS } from '../constants/Config'
import { getSRComments, addSRComment } from '../services/api'
interface CommentItem {
id: number | string
content: string
author?: string
is_internal?: boolean
created_at?: string
}
interface Props {
srId: number
}
/**
* 기능 #13 — 내부/외부 코멘트 구분 컴포넌트
* 내부: 자물쇠(담당자 전용), 외부: 지구본(기관 공개)
* POST /api/tasks/{id}/comments { content, is_internal }
*/
export default function Comment({ srId }: Props) {
const [items, setItems] = useState<CommentItem[]>([])
const [loading, setLoading] = useState(true)
const [text, setText] = useState('')
const [isInternal, setIsInternal] = useState(true)
const [sending, setSending] = useState(false)
const load = async () => {
try {
const res = await getSRComments(srId)
setItems(res.data?.content ?? res.data?.items ?? res.data ?? [])
} catch { setItems([]) }
finally { setLoading(false) }
}
useEffect(() => { load() }, [srId])
const submit = async () => {
if (!text.trim()) return
setSending(true)
try {
await addSRComment(srId, text.trim(), isInternal)
setText('')
await load()
} catch (e: any) {
Alert.alert('오류', e.response?.data?.detail ?? '코멘트 등록 실패')
} finally { setSending(false) }
}
return (
<View>
{loading ? (
<ActivityIndicator color={COLORS.accent} style={{ marginVertical: 12 }} />
) : items.length === 0 ? (
<Text style={s.empty}> .</Text>
) : (
items.map(c => (
<View key={c.id} style={[s.bubble, c.is_internal ? s.internal : s.external]}>
<View style={s.bubbleHead}>
<Text style={s.tag}>{c.is_internal ? '🔒 내부' : '🌐 외부'}</Text>
<Text style={s.author}>{c.author ?? '담당자'}</Text>
</View>
<Text style={s.content}>{c.content}</Text>
{!!c.created_at && <Text style={s.time}>{c.created_at.slice(0, 16).replace('T', ' ')}</Text>}
</View>
))
)}
{/* 입력 영역 */}
<View style={s.toggleRow}>
<TouchableOpacity
style={[s.toggle, isInternal && s.toggleActive]}
onPress={() => setIsInternal(true)}
>
<Text style={[s.toggleText, isInternal && s.toggleTextActive]}>🔒 </Text>
</TouchableOpacity>
<TouchableOpacity
style={[s.toggle, !isInternal && s.toggleActive]}
onPress={() => setIsInternal(false)}
>
<Text style={[s.toggleText, !isInternal && s.toggleTextActive]}>🌐 </Text>
</TouchableOpacity>
</View>
<View style={s.inputRow}>
<TextInput
style={s.input}
value={text}
onChangeText={setText}
placeholder={isInternal ? '내부 코멘트 (담당자만 열람)' : '외부 코멘트 (기관 공개)'}
placeholderTextColor={COLORS.muted}
multiline
/>
<TouchableOpacity style={[s.sendBtn, sending && { opacity: 0.6 }]} onPress={submit} disabled={sending}>
{sending ? <ActivityIndicator color="#fff" size="small" /> : <Text style={s.sendText}></Text>}
</TouchableOpacity>
</View>
</View>
)
}
const s = StyleSheet.create({
empty: { color: COLORS.muted, fontSize: 12, paddingVertical: 8 },
bubble: { borderRadius: 10, padding: 12, marginBottom: 8, borderLeftWidth: 3 },
internal: { backgroundColor: '#FFF7ED', borderLeftColor: COLORS.warning },
external: { backgroundColor: COLORS.light, borderLeftColor: COLORS.accent },
bubbleHead: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 4 },
tag: { fontSize: 11, fontWeight: '700', color: COLORS.text },
author: { fontSize: 11, color: COLORS.muted },
content: { fontSize: 13, color: COLORS.text, lineHeight: 18 },
time: { fontSize: 10, color: COLORS.muted, marginTop: 4 },
toggleRow: { flexDirection: 'row', gap: 8, marginTop: 12, marginBottom: 8 },
toggle: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 16, borderWidth: 1, borderColor: COLORS.border },
toggleActive: { backgroundColor: COLORS.primary, borderColor: COLORS.primary },
toggleText: { fontSize: 12, color: COLORS.text },
toggleTextActive: { color: '#fff', fontWeight: '700' },
inputRow: { flexDirection: 'row', alignItems: 'flex-end', gap: 8 },
input: { flex: 1, borderWidth: 1.5, borderColor: COLORS.border, borderRadius: 9, padding: 10, fontSize: 13, color: COLORS.text, maxHeight: 100 },
sendBtn: { backgroundColor: COLORS.accent, borderRadius: 9, paddingHorizontal: 16, paddingVertical: 11 },
sendText: { color: '#fff', fontSize: 13, fontWeight: '700' },
})