guardia-messenger/app/(auth)/login.tsx
DESKTOP-TKLFCPRython 278f9639c9 feat(app): Messenger app Variant design applied
Config.ts:
- COLORS: accent #4f6ef7 -> #00A0C8(cyan), primary #003366(navy)
- gnbBg: deeper navy #001530

_layout.tsx:
- TabBar: elevated shadow, cyan active tint, bolder label

index.tsx (Dashboard):
- StatCard: top color bar + icon box (screenshot9 pattern)
- Header: deep navy gradient rounded bottom
- QuickBtn: bg-light card style
- Section: deeper shadow, navy title

login.tsx:
- Background: deep navy #001530
- Card: white + strong shadow
- Button: solid cyan with shadow
- Label: cyan uppercase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:25:47 +09:00

141 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import {
View, Text, TextInput, TouchableOpacity, StyleSheet,
KeyboardAvoidingView, Platform, ActivityIndicator, Alert, ScrollView,
} from 'react-native'
import { useAuth } from '../../hooks/useAuth'
import { COLORS } from '../../constants/Config'
export default function LoginScreen() {
const { login } = useAuth()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const handleLogin = async () => {
if (!username.trim() || !password.trim()) {
Alert.alert('입력 오류', '아이디와 비밀번호를 입력해주세요.')
return
}
setLoading(true)
try {
await login(username.trim(), password)
} catch (e: any) {
const msg = e.response?.data?.detail ?? '로그인에 실패했습니다.'
Alert.alert('로그인 실패', msg)
} finally {
setLoading(false)
}
}
return (
<KeyboardAvoidingView
style={s.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView contentContainerStyle={s.inner} keyboardShouldPersistTaps="handled">
{/* 로고 영역 */}
<View style={s.logoBox}>
<Text style={s.logoIcon}>🛡</Text>
<Text style={s.logoTitle}>GUARDiA</Text>
<Text style={s.logoSub}>AI </Text>
<View style={s.badge}>
<Text style={s.badgeText}>()</Text>
</View>
</View>
{/* 로그인 카드 */}
<View style={s.card}>
<Text style={s.cardTitle}></Text>
<View style={s.field}>
<Text style={s.label}></Text>
<TextInput
style={s.input}
value={username}
onChangeText={setUsername}
placeholder="관리자 아이디"
placeholderTextColor={COLORS.muted}
autoCapitalize="none"
autoCorrect={false}
returnKeyType="next"
/>
</View>
<View style={s.field}>
<Text style={s.label}></Text>
<TextInput
style={s.input}
value={password}
onChangeText={setPassword}
placeholder="비밀번호"
placeholderTextColor={COLORS.muted}
secureTextEntry
returnKeyType="done"
onSubmitEditing={handleLogin}
/>
</View>
<TouchableOpacity
style={[s.btn, loading && s.btnDisabled]}
onPress={handleLogin}
disabled={loading}
>
{loading
? <ActivityIndicator color="#fff" />
: <Text style={s.btnText}></Text>
}
</TouchableOpacity>
<Text style={s.hint}>GUARDiA ITSM </Text>
</View>
<Text style={s.version}>v1.0.0 · zioinfo.co.kr</Text>
</ScrollView>
</KeyboardAvoidingView>
)
}
/* Variant 스타일 로그인 — 딥네이비 배경 + 흰 카드 */
const s = StyleSheet.create({
container: { flex: 1, backgroundColor: '#001530' },
inner: { flexGrow: 1, justifyContent: 'center', padding: 28 },
logoBox: { alignItems: 'center', marginBottom: 36 },
logoIcon: { fontSize: 52, marginBottom: 10 },
logoTitle: { fontSize: 34, fontWeight: '900', color: '#fff', letterSpacing: 1.5 },
logoSub: { fontSize: 13, color: 'rgba(0,160,200,.85)', marginTop: 5 },
badge: {
marginTop: 10,
backgroundColor: 'rgba(0,160,200,.15)',
borderWidth: 1, borderColor: 'rgba(0,160,200,.3)',
paddingHorizontal: 14, paddingVertical: 5, borderRadius: 20,
},
badgeText: { color: '#00A0C8', fontSize: 11, fontWeight: '700' },
card: {
backgroundColor: '#fff', borderRadius: 20, padding: 28,
shadowColor: '#001530', shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.25, shadowRadius: 32, elevation: 10,
},
cardTitle: {
fontSize: 18, fontWeight: '800', color: '#003366',
marginBottom: 22, textAlign: 'center', letterSpacing: -0.3,
},
field: { marginBottom: 16 },
label: { fontSize: 11, fontWeight: '700', color: '#00A0C8',
marginBottom: 6, textTransform: 'uppercase', letterSpacing: .6 },
input: {
borderWidth: 1.5, borderColor: '#E2E8F0', borderRadius: 12,
padding: 14, fontSize: 15, color: '#1E293B', backgroundColor: '#F8FAFC',
},
btn: {
backgroundColor: '#00A0C8', borderRadius: 12, padding: 16,
alignItems: 'center', marginTop: 8,
shadowColor: '#00A0C8', shadowOffset: { width: 0, height: 4 },
shadowOpacity: .3, shadowRadius: 10, elevation: 4,
},
btnDisabled: { opacity: .6 },
btnText: { color: '#fff', fontSize: 16, fontWeight: '800', letterSpacing: 0.3 },
hint: { textAlign: 'center', color: '#64748B', fontSize: 12, marginTop: 16 },
version: { textAlign: 'center', color: 'rgba(0,160,200,.4)', fontSize: 11, marginTop: 24 },
})