feat(login): campo de dirección del servidor configurable
- config.ts: BASE_URL/ORIGIN dejan de ser constantes; ahora hay setServerOrigin/getBaseUrl/getServerOrigin con normalizeServerUrl (añade http:// si falta, quita barras finales y el sufijo /api/v1). - client.ts: construye las URLs con getBaseUrl() en cada petición. - session: signIn recibe la URL, la fija antes del login y la persiste en SecureStore (clave server_url); se rehidrata al arrancar junto al token. - LoginScreen: campo "Servidor" precargado con el último valor usado. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,8 @@ import { useSession } from '../auth/session';
|
||||
const DEVICE_NAME = `${Platform.OS}-avante`;
|
||||
|
||||
export function LoginScreen() {
|
||||
const { signIn } = useSession();
|
||||
const { signIn, serverUrl } = useSession();
|
||||
const [server, setServer] = useState(serverUrl);
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -25,7 +26,7 @@ export function LoginScreen() {
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
try {
|
||||
await signIn(email.trim(), password, DEVICE_NAME);
|
||||
await signIn(email.trim(), password, DEVICE_NAME, server);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError && e.status === 422) {
|
||||
setError('Credenciales inválidas.');
|
||||
@@ -37,6 +38,8 @@ export function LoginScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const canSubmit = !loading && !!email && !!password && !!server.trim();
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
@@ -44,6 +47,17 @@ export function LoginScreen() {
|
||||
>
|
||||
<Text style={styles.title}>Avante · Seguimiento de obra</Text>
|
||||
|
||||
<Text style={styles.fieldLabel}>Servidor</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="https://servidor o IP (p.ej. 192.168.1.50/construprogress/public)"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
keyboardType="url"
|
||||
value={server}
|
||||
onChangeText={setServer}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Email"
|
||||
@@ -63,9 +77,9 @@ export function LoginScreen() {
|
||||
{error && <Text style={styles.error}>{error}</Text>}
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.button, (loading || !email || !password) && styles.buttonDisabled]}
|
||||
style={[styles.button, !canSubmit && styles.buttonDisabled]}
|
||||
onPress={onSubmit}
|
||||
disabled={loading || !email || !password}
|
||||
disabled={!canSubmit}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
@@ -80,6 +94,7 @@ export function LoginScreen() {
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, justifyContent: 'center', padding: 24, gap: 12 },
|
||||
title: { fontSize: 22, fontWeight: '700', textAlign: 'center', marginBottom: 16 },
|
||||
fieldLabel: { fontSize: 12, color: '#666', marginBottom: -6, fontWeight: '600' },
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
|
||||
Reference in New Issue
Block a user