76 lines
3.6 KiB
TypeScript
76 lines
3.6 KiB
TypeScript
import { useState } from 'react'
|
|
import {
|
|
View, Text, TextInput, TouchableOpacity, StyleSheet, ScrollView, ActivityIndicator, KeyboardAvoidingView, Platform,
|
|
} from 'react-native'
|
|
import { router } from 'expo-router'
|
|
import { useTheme } from '../../constants/ThemeContext'
|
|
import { type ThemeTokens } from '../../constants/ItmsTheme'
|
|
import { itmsLogin, itmsErrorMessage } from '../itmsApi'
|
|
|
|
/*
|
|
* ITMS 로그인 — 레거시 OAuth2 password grant(웹 front 와 동일 계약).
|
|
* userId + password → POST /oauth/token → 토큰/프로필 저장 → /home.
|
|
* 비밀번호는 웹과 동일하게 평문 전송(TLS 보호). 클라 암호화 안 함.
|
|
*/
|
|
export default function Login() {
|
|
const { t: th } = useTheme()
|
|
const s = makeStyles(th)
|
|
const [userId, setUserId] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [busy, setBusy] = useState(false)
|
|
const [err, setErr] = useState('')
|
|
|
|
const doLogin = async () => {
|
|
if (!userId.trim() || !password) { setErr('아이디와 비밀번호를 입력하세요.'); return }
|
|
setBusy(true); setErr('')
|
|
try {
|
|
await itmsLogin(userId.trim(), password)
|
|
router.replace('/home')
|
|
} catch (e) { setErr(itmsErrorMessage(e)) }
|
|
finally { setBusy(false) }
|
|
}
|
|
|
|
return (
|
|
<KeyboardAvoidingView style={{ flex: 1 }} behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
|
|
<ScrollView style={s.wrap} contentContainerStyle={s.content} keyboardShouldPersistTaps="handled">
|
|
<Text style={s.logo}>ITMS</Text>
|
|
<Text style={s.sub}>IT 유지보수관리 시스템</Text>
|
|
|
|
<View style={s.card}>
|
|
<Text style={s.lbl}>아이디</Text>
|
|
<TextInput
|
|
style={s.input} value={userId} onChangeText={setUserId}
|
|
placeholder="사용자 아이디" placeholderTextColor={th.muted}
|
|
autoCapitalize="none" autoCorrect={false}
|
|
/>
|
|
<Text style={[s.lbl, { marginTop: 12 }]}>비밀번호</Text>
|
|
<TextInput
|
|
style={s.input} value={password} onChangeText={setPassword}
|
|
placeholder="비밀번호" placeholderTextColor={th.muted} secureTextEntry
|
|
onSubmitEditing={doLogin} returnKeyType="go"
|
|
/>
|
|
{!!err && <Text style={s.err}>{err}</Text>}
|
|
<TouchableOpacity style={[s.btn, busy && { opacity: 0.5 }]} onPress={doLogin} disabled={busy}>
|
|
{busy ? <ActivityIndicator color={th.onBrand} /> : <Text style={s.btnText}>로그인</Text>}
|
|
</TouchableOpacity>
|
|
</View>
|
|
<Text style={s.foot}>지오정보기술 · WISE Platform</Text>
|
|
</ScrollView>
|
|
</KeyboardAvoidingView>
|
|
)
|
|
}
|
|
|
|
const makeStyles = (th: ThemeTokens) => StyleSheet.create({
|
|
wrap: { flex: 1, backgroundColor: th.bg },
|
|
content: { flexGrow: 1, justifyContent: 'center', padding: 24 },
|
|
logo: { color: th.primary, fontSize: 44, fontWeight: '900', textAlign: 'center', letterSpacing: 3 },
|
|
sub: { color: th.muted, fontSize: 13, textAlign: 'center', marginTop: 6, marginBottom: 28 },
|
|
card: { backgroundColor: th.card, borderRadius: 16, borderWidth: 1, borderColor: th.border, padding: 20 },
|
|
lbl: { color: th.muted, fontSize: 12, marginBottom: 6 },
|
|
input: { backgroundColor: th.cardAlt, borderRadius: 10, borderWidth: 1, borderColor: th.border, color: th.text, padding: 13, fontSize: 15 },
|
|
btn: { backgroundColor: th.brand, borderRadius: 12, paddingVertical: 14, alignItems: 'center', marginTop: 20 },
|
|
btnText: { color: th.onBrand, fontWeight: '800', fontSize: 15 },
|
|
err: { color: th.danger, fontSize: 12, marginTop: 12 },
|
|
foot: { color: th.muted, fontSize: 11, textAlign: 'center', marginTop: 28 },
|
|
})
|