95 lines
4.4 KiB
TypeScript
95 lines
4.4 KiB
TypeScript
import React, { useState, useRef, useCallback } from 'react'
|
|
import { View, Text, TouchableOpacity, StyleSheet, Alert, ScrollView, TextInput } from 'react-native'
|
|
import { useFocusEffect } from 'expo-router'
|
|
import { COLORS } from '../../constants/Config'
|
|
import client from '../../services/api'
|
|
|
|
export default function ESignatureScreen() {
|
|
const [docs, setDocs] = useState<any[]>([])
|
|
const [selected, setSelected] = useState<any>(null)
|
|
const [pin, setPin] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const load = useCallback(async () => {
|
|
setLoading(true)
|
|
try { const r = await client.get('/api/approvals/pending-docs'); setDocs(r.data?.docs ?? r.data?.items ?? []) }
|
|
catch { setDocs([]) } finally { setLoading(false) }
|
|
}, [])
|
|
|
|
useFocusEffect(useCallback(() => { load() }, [load]))
|
|
|
|
const sign = async () => {
|
|
if (!selected) return
|
|
if (!pin || pin.length < 4) { Alert.alert('오류', 'PIN 4자리 이상 입력하세요.'); return }
|
|
try {
|
|
await client.post(`/api/approvals/${selected.id}/sign`, { pin_hash: pin })
|
|
Alert.alert('완료', '전자서명이 완료됐습니다.')
|
|
setSelected(null)
|
|
setPin('')
|
|
load()
|
|
} catch { Alert.alert('오류', '서명에 실패했습니다.') }
|
|
}
|
|
|
|
if (selected) {
|
|
return (
|
|
<View style={s.container}>
|
|
<View style={s.docCard}>
|
|
<Text style={s.docTitle}>{selected.title}</Text>
|
|
<Text style={s.docMeta}>요청자: {selected.requester_name ?? '-'} · {selected.created_at?.slice(0, 10) ?? ''}</Text>
|
|
<Text style={s.docDesc} numberOfLines={4}>{selected.content ?? selected.description ?? ''}</Text>
|
|
</View>
|
|
<Text style={s.pinLabel}>전자서명 PIN 입력</Text>
|
|
<TextInput
|
|
style={s.pinInput}
|
|
value={pin}
|
|
onChangeText={setPin}
|
|
placeholder="PIN (4자리 이상)"
|
|
secureTextEntry
|
|
keyboardType="numeric"
|
|
maxLength={8}
|
|
/>
|
|
<TouchableOpacity style={s.signBtn} onPress={sign}>
|
|
<Text style={s.signText}>전자서명 완료</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={s.cancelBtn} onPress={() => { setSelected(null); setPin('') }}>
|
|
<Text style={s.cancelText}>취소</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<ScrollView style={s.container} contentContainerStyle={{ padding: 12 }}>
|
|
<Text style={s.header}>전자서명 대기 문서</Text>
|
|
{docs.length === 0 && <Text style={s.empty}>서명 대기 문서가 없습니다.</Text>}
|
|
{docs.map((doc, i) => (
|
|
<TouchableOpacity key={i} style={s.card} onPress={() => setSelected(doc)}>
|
|
<Text style={s.cardTitle}>{doc.title}</Text>
|
|
<Text style={s.cardMeta}>{doc.requester_name ?? '-'} · {doc.created_at?.slice(0, 10) ?? ''}</Text>
|
|
<Text style={s.cardAction}>서명하기 →</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: COLORS.bg },
|
|
header: { fontSize: 16, fontWeight: '800', color: COLORS.text, marginBottom: 12 },
|
|
card: { backgroundColor: '#fff', borderRadius: 10, padding: 14, marginBottom: 8, elevation: 1 },
|
|
cardTitle: { fontSize: 14, fontWeight: '700', color: COLORS.text, marginBottom: 4 },
|
|
cardMeta: { fontSize: 11, color: COLORS.muted, marginBottom: 8 },
|
|
cardAction: { color: COLORS.accent, fontSize: 13, fontWeight: '700' },
|
|
docCard: { backgroundColor: '#fff', borderRadius: 12, padding: 16, margin: 12, elevation: 1 },
|
|
docTitle: { fontSize: 16, fontWeight: '800', color: COLORS.text, marginBottom: 4 },
|
|
docMeta: { fontSize: 12, color: COLORS.muted, marginBottom: 10 },
|
|
docDesc: { fontSize: 13, color: COLORS.text, lineHeight: 20 },
|
|
pinLabel: { fontSize: 13, fontWeight: '700', color: COLORS.text, marginHorizontal: 12, marginTop: 8 },
|
|
pinInput: { backgroundColor: '#fff', borderRadius: 10, padding: 14, margin: 12, fontSize: 18, letterSpacing: 4, elevation: 1, color: COLORS.text },
|
|
signBtn: { backgroundColor: COLORS.success, borderRadius: 10, padding: 14, margin: 12, alignItems: 'center' },
|
|
signText: { color: '#fff', fontSize: 15, fontWeight: '800' },
|
|
cancelBtn: { borderRadius: 10, padding: 12, marginHorizontal: 12, alignItems: 'center' },
|
|
cancelText: { color: COLORS.muted, fontSize: 14 },
|
|
empty: { textAlign: 'center', color: COLORS.muted, marginTop: 40 },
|
|
})
|