132 lines
5.9 KiB
TypeScript
132 lines
5.9 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
|
import {
|
|
View, Text, TextInput, TouchableOpacity, StyleSheet,
|
|
ScrollView, Alert, ActivityIndicator,
|
|
} from 'react-native'
|
|
import { COLORS } from '../../constants/Config'
|
|
import { getDelegation, setDelegation, cancelDelegation, searchUsers } from '../../services/api'
|
|
|
|
export default function DelegationScreen() {
|
|
const [current, setCurrent] = useState<any>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [query, setQuery] = useState('')
|
|
const [users, setUsers] = useState<any[]>([])
|
|
const [selected, setSelected] = useState<any>(null)
|
|
const [startDate, setStartDate] = useState('')
|
|
const [endDate, setEndDate] = useState('')
|
|
const [reason, setReason] = useState('')
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
useEffect(() => {
|
|
getDelegation().then(r => setCurrent(r.data)).catch(() => {}).finally(() => setLoading(false))
|
|
}, [])
|
|
|
|
const search = async () => {
|
|
if (!query.trim()) return
|
|
try {
|
|
const r = await searchUsers(query)
|
|
setUsers(r.data?.items ?? r.data ?? [])
|
|
} catch { setUsers([]) }
|
|
}
|
|
|
|
const save = async () => {
|
|
if (!selected || !startDate || !endDate || !reason.trim()) {
|
|
Alert.alert('입력 오류', '모든 항목을 입력해주세요.')
|
|
return
|
|
}
|
|
setSaving(true)
|
|
try {
|
|
await setDelegation({ delegate_to: selected.id, start_date: startDate, end_date: endDate, reason })
|
|
Alert.alert('완료', '대리결재가 설정됐습니다.')
|
|
const r = await getDelegation()
|
|
setCurrent(r.data)
|
|
} catch { Alert.alert('오류', '저장 중 오류가 발생했습니다.') }
|
|
finally { setSaving(false) }
|
|
}
|
|
|
|
const cancel = async (id: number) => {
|
|
Alert.alert('취소 확인', '대리결재를 해제하시겠습니까?', [
|
|
{ text: '아니오', style: 'cancel' },
|
|
{ text: '해제', style: 'destructive', onPress: async () => {
|
|
await cancelDelegation(id)
|
|
setCurrent(null)
|
|
}},
|
|
])
|
|
}
|
|
|
|
if (loading) return <ActivityIndicator style={{ flex: 1 }} color={COLORS.accent} />
|
|
|
|
return (
|
|
<ScrollView style={s.container} contentContainerStyle={{ padding: 16 }}>
|
|
{current && (
|
|
<View style={s.card}>
|
|
<Text style={s.label}>현재 대리결재</Text>
|
|
<Text style={s.value}>대리인: {current.delegate_name ?? current.delegate_to}</Text>
|
|
<Text style={s.value}>기간: {current.start_date} ~ {current.end_date}</Text>
|
|
<Text style={s.value}>사유: {current.reason}</Text>
|
|
<TouchableOpacity style={s.cancelBtn} onPress={() => cancel(current.id)}>
|
|
<Text style={s.cancelBtnText}>해제</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
)}
|
|
|
|
<Text style={s.sectionTitle}>대리인 설정</Text>
|
|
|
|
<View style={s.row}>
|
|
<TextInput
|
|
style={[s.input, { flex: 1 }]}
|
|
value={query}
|
|
onChangeText={setQuery}
|
|
placeholder="사용자 검색..."
|
|
onSubmitEditing={search}
|
|
/>
|
|
<TouchableOpacity style={s.searchBtn} onPress={search}>
|
|
<Text style={s.searchBtnText}>검색</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{users.map((u: any) => (
|
|
<TouchableOpacity
|
|
key={u.id ?? u.username}
|
|
style={[s.userItem, selected?.id === u.id && s.userItemSelected]}
|
|
onPress={() => setSelected(u)}
|
|
>
|
|
<Text style={s.userName}>{u.display_name ?? u.username}</Text>
|
|
<Text style={s.userRole}>{u.role}</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
|
|
{selected && <Text style={s.selectedText}>선택: {selected.display_name ?? selected.username}</Text>}
|
|
|
|
<TextInput style={s.input} value={startDate} onChangeText={setStartDate} placeholder="시작일 (YYYY-MM-DD)" />
|
|
<TextInput style={s.input} value={endDate} onChangeText={setEndDate} placeholder="종료일 (YYYY-MM-DD)" />
|
|
<TextInput style={[s.input, { height: 80 }]} value={reason} onChangeText={setReason} placeholder="사유" multiline />
|
|
|
|
<TouchableOpacity style={s.saveBtn} onPress={save} disabled={saving}>
|
|
<Text style={s.saveBtnText}>{saving ? '저장 중...' : '대리결재 설정'}</Text>
|
|
</TouchableOpacity>
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: COLORS.bg },
|
|
card: { backgroundColor: '#fff', borderRadius: 10, padding: 14, marginBottom: 16, elevation: 1 },
|
|
label: { fontSize: 12, color: COLORS.muted, marginBottom: 4 },
|
|
value: { fontSize: 14, color: COLORS.text, marginBottom: 2 },
|
|
cancelBtn: { marginTop: 8, backgroundColor: COLORS.danger, borderRadius: 6, padding: 8, alignItems: 'center' },
|
|
cancelBtnText: { color: '#fff', fontWeight: '700' },
|
|
sectionTitle: { fontSize: 16, fontWeight: '700', color: COLORS.text, marginBottom: 12 },
|
|
row: { flexDirection: 'row', gap: 8, marginBottom: 8 },
|
|
input: { backgroundColor: '#fff', borderRadius: 8, borderWidth: 1, borderColor: COLORS.border, paddingHorizontal: 12, paddingVertical: 10, marginBottom: 10, fontSize: 14, color: COLORS.text },
|
|
searchBtn: { backgroundColor: COLORS.accent, borderRadius: 8, paddingHorizontal: 16, justifyContent: 'center' },
|
|
searchBtnText: { color: '#fff', fontWeight: '700' },
|
|
userItem: { backgroundColor: '#fff', borderRadius: 8, padding: 10, marginBottom: 4, flexDirection: 'row', justifyContent: 'space-between', borderWidth: 1, borderColor: COLORS.border },
|
|
userItemSelected: { borderColor: COLORS.accent, backgroundColor: COLORS.light },
|
|
userName: { fontSize: 14, fontWeight: '600', color: COLORS.text },
|
|
userRole: { fontSize: 12, color: COLORS.muted },
|
|
selectedText: { fontSize: 13, color: COLORS.accent, marginBottom: 8 },
|
|
saveBtn: { backgroundColor: COLORS.accent, borderRadius: 10, padding: 14, alignItems: 'center', marginTop: 8 },
|
|
saveBtnText: { color: '#fff', fontWeight: '800', fontSize: 15 },
|
|
})
|