83 lines
4.4 KiB
TypeScript
83 lines
4.4 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { View, Text, StyleSheet, TouchableOpacity, Alert, ScrollView, ActivityIndicator } from 'react-native'
|
|
import { COLORS } from '../../constants/Config'
|
|
import client from '../../services/api'
|
|
|
|
export default function NFCAssetScreen() {
|
|
const [scanning, setScanning] = useState(false)
|
|
const [asset, setAsset] = useState<any>(null)
|
|
|
|
const startScan = async () => {
|
|
setScanning(true)
|
|
setAsset(null)
|
|
try {
|
|
const NFC = (() => { try { return require('react-native-nfc-manager').default } catch { return null } })()
|
|
if (!NFC) { Alert.alert('NFC 미지원', '이 기기는 NFC를 지원하지 않거나 모듈이 설치되지 않았습니다.'); setScanning(false); return }
|
|
|
|
await NFC.start()
|
|
await NFC.requestTechnology(['Ndef'])
|
|
const tag = await NFC.getTag()
|
|
const payload = tag?.ndefMessage?.[0]?.payload
|
|
const assetId = payload ? String.fromCharCode(...payload).replace(/^\x02en/, '') : null
|
|
|
|
if (!assetId) { Alert.alert('오류', 'NFC 태그에서 자산 ID를 읽을 수 없습니다.'); setScanning(false); return }
|
|
|
|
const r = await client.get(`/api/cmdb/assets/${assetId}`)
|
|
setAsset(r.data)
|
|
} catch (e: any) {
|
|
if (!e.message?.includes('cancel')) Alert.alert('스캔 실패', e.message ?? 'NFC 스캔에 실패했습니다.')
|
|
} finally {
|
|
setScanning(false)
|
|
try { const NFC = require('react-native-nfc-manager').default; NFC.cancelTechnologyRequest() } catch {}
|
|
}
|
|
}
|
|
|
|
const checkin = async () => {
|
|
if (!asset) return
|
|
try {
|
|
await client.post('/api/servers/field-checkin', { server_id: asset.id, source: 'nfc', method: 'nfc_tag' })
|
|
Alert.alert('완료', `${asset.hostname ?? asset.name} 실사 체크인 완료!`)
|
|
} catch { Alert.alert('오류', '체크인에 실패했습니다.') }
|
|
}
|
|
|
|
return (
|
|
<ScrollView style={s.container} contentContainerStyle={{ padding: 16 }}>
|
|
<Text style={s.title}>NFC 자산 인식</Text>
|
|
<Text style={s.subtitle}>NFC 태그가 부착된 서버·장비에 폰을 가져다 대세요.</Text>
|
|
|
|
<TouchableOpacity style={[s.scanBtn, { opacity: scanning ? 0.6 : 1 }]} onPress={startScan} disabled={scanning}>
|
|
{scanning ? <ActivityIndicator color="#fff" /> : <Text style={s.scanText}>NFC 스캔 시작</Text>}
|
|
</TouchableOpacity>
|
|
|
|
{asset && (
|
|
<View style={s.assetCard}>
|
|
<Text style={s.assetName}>{asset.hostname ?? asset.name}</Text>
|
|
<View style={s.infoRow}><Text style={s.label}>IP</Text><Text style={s.val}>***.***.***</Text></View>
|
|
<View style={s.infoRow}><Text style={s.label}>OS</Text><Text style={s.val}>{asset.os_name ?? '-'}</Text></View>
|
|
<View style={s.infoRow}><Text style={s.label}>위치</Text><Text style={s.val}>{asset.location ?? '-'}</Text></View>
|
|
<View style={s.infoRow}><Text style={s.label}>기관</Text><Text style={s.val}>{asset.institution_name ?? '-'}</Text></View>
|
|
<View style={s.infoRow}><Text style={s.label}>상태</Text><Text style={[s.val, { color: asset.status === 'active' ? COLORS.success : COLORS.muted }]}>{asset.status ?? '-'}</Text></View>
|
|
<TouchableOpacity style={s.checkinBtn} onPress={checkin}>
|
|
<Text style={s.checkinText}>실사 체크인</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
)
|
|
}
|
|
|
|
const s = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: COLORS.bg },
|
|
title: { fontSize: 22, fontWeight: '800', color: COLORS.text, marginBottom: 8 },
|
|
subtitle: { fontSize: 13, color: COLORS.muted, marginBottom: 24 },
|
|
scanBtn: { backgroundColor: COLORS.accent, borderRadius: 16, padding: 24, alignItems: 'center', marginBottom: 24, elevation: 3 },
|
|
scanText: { color: '#fff', fontSize: 18, fontWeight: '800' },
|
|
assetCard: { backgroundColor: '#fff', borderRadius: 16, padding: 16, elevation: 2 },
|
|
assetName: { fontSize: 20, fontWeight: '800', color: COLORS.text, marginBottom: 16 },
|
|
infoRow: { flexDirection: 'row', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: COLORS.border },
|
|
label: { width: 60, fontSize: 12, color: COLORS.muted, fontWeight: '600' },
|
|
val: { flex: 1, fontSize: 13, color: COLORS.text },
|
|
checkinBtn: { backgroundColor: COLORS.success, borderRadius: 10, padding: 14, alignItems: 'center', marginTop: 16 },
|
|
checkinText:{ color: '#fff', fontSize: 14, fontWeight: '800' },
|
|
})
|