Add device settings screen
Features: - Device metadata display (name, MAC, location, description) - Edit device name and description - Update WiFi credentials (reconnect flow) - Remove device from beneficiary - Device history and diagnostics UI: - Clean settings form with validation - Delete confirmation dialog - Success/error feedback - Navigation back to equipment list on changes Route: /(tabs)/beneficiaries/[id]/device-settings/[deviceId]
This commit is contained in:
parent
3c3283e424
commit
5092678430
728
app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx
Normal file
728
app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx
Normal file
@ -0,0 +1,728 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { router, useLocalSearchParams } from 'expo-router';
|
||||
import * as Device from 'expo-device';
|
||||
import { useBLE } from '@/contexts/BLEContext';
|
||||
import { api } from '@/services/api';
|
||||
import type { WiFiStatus } from '@/services/ble';
|
||||
import {
|
||||
AppColors,
|
||||
BorderRadius,
|
||||
FontSizes,
|
||||
FontWeights,
|
||||
Spacing,
|
||||
Shadows,
|
||||
} from '@/constants/theme';
|
||||
|
||||
interface SensorInfo {
|
||||
deviceId: string;
|
||||
wellId: number;
|
||||
mac: string;
|
||||
name: string;
|
||||
status: 'online' | 'offline';
|
||||
lastSeen: Date;
|
||||
beneficiaryId: string;
|
||||
deploymentId: number;
|
||||
}
|
||||
|
||||
export default function DeviceSettingsScreen() {
|
||||
const { id, deviceId } = useLocalSearchParams<{ id: string; deviceId: string }>();
|
||||
const {
|
||||
connectedDevices,
|
||||
isBLEAvailable,
|
||||
connectDevice,
|
||||
disconnectDevice,
|
||||
getCurrentWiFi,
|
||||
rebootDevice,
|
||||
} = useBLE();
|
||||
|
||||
const [sensorInfo, setSensorInfo] = useState<SensorInfo | null>(null);
|
||||
const [currentWiFi, setCurrentWiFi] = useState<WiFiStatus | null>(null);
|
||||
const [isLoadingInfo, setIsLoadingInfo] = useState(true);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [isLoadingWiFi, setIsLoadingWiFi] = useState(false);
|
||||
const [isRebooting, setIsRebooting] = useState(false);
|
||||
|
||||
const isConnected = connectedDevices.has(deviceId!);
|
||||
|
||||
useEffect(() => {
|
||||
loadSensorInfo();
|
||||
}, []);
|
||||
|
||||
const loadSensorInfo = async () => {
|
||||
setIsLoadingInfo(true);
|
||||
|
||||
try {
|
||||
// Get sensor info from API
|
||||
const response = await api.getDevicesForBeneficiary(id!);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load sensor info');
|
||||
}
|
||||
|
||||
const sensor = response.data.find((s: any) => s.deviceId === deviceId);
|
||||
|
||||
if (sensor) {
|
||||
setSensorInfo(sensor);
|
||||
} else {
|
||||
throw new Error('Sensor not found');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('[DeviceSettings] Failed to load sensor info:', error);
|
||||
Alert.alert('Error', 'Failed to load sensor information');
|
||||
router.back();
|
||||
} finally {
|
||||
setIsLoadingInfo(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConnect = async () => {
|
||||
if (!sensorInfo) return;
|
||||
|
||||
setIsConnecting(true);
|
||||
|
||||
try {
|
||||
const success = await connectDevice(deviceId!);
|
||||
|
||||
if (!success) {
|
||||
throw new Error('Connection failed');
|
||||
}
|
||||
|
||||
// Load WiFi status after connecting
|
||||
loadWiFiStatus();
|
||||
} catch (error: any) {
|
||||
console.error('[DeviceSettings] Connection failed:', error);
|
||||
Alert.alert('Connection Failed', 'Failed to connect to sensor via Bluetooth.');
|
||||
} finally {
|
||||
setIsConnecting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loadWiFiStatus = async () => {
|
||||
if (!isConnected) return;
|
||||
|
||||
setIsLoadingWiFi(true);
|
||||
|
||||
try {
|
||||
const wifiStatus = await getCurrentWiFi(deviceId!);
|
||||
setCurrentWiFi(wifiStatus);
|
||||
} catch (error: any) {
|
||||
console.error('[DeviceSettings] Failed to get WiFi status:', error);
|
||||
Alert.alert('Error', 'Failed to get WiFi status');
|
||||
} finally {
|
||||
setIsLoadingWiFi(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangeWiFi = () => {
|
||||
if (!isConnected) {
|
||||
Alert.alert('Not Connected', 'Please connect to the sensor via Bluetooth first.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigate to Setup WiFi screen
|
||||
router.push({
|
||||
pathname: `/(tabs)/beneficiaries/${id}/setup-wifi` as any,
|
||||
params: {
|
||||
deviceId: deviceId!,
|
||||
deviceName: sensorInfo?.name || '',
|
||||
wellId: sensorInfo?.wellId.toString() || '',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleReboot = () => {
|
||||
if (!isConnected) {
|
||||
Alert.alert('Not Connected', 'Please connect to the sensor via Bluetooth first.');
|
||||
return;
|
||||
}
|
||||
|
||||
Alert.alert(
|
||||
'Reboot Sensor',
|
||||
'Are you sure you want to reboot this sensor? It will disconnect from Bluetooth and restart.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
text: 'Reboot',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
setIsRebooting(true);
|
||||
|
||||
try {
|
||||
await rebootDevice(deviceId!);
|
||||
Alert.alert('Success', 'Sensor is rebooting. It will be back online in a minute.');
|
||||
router.back();
|
||||
} catch (error: any) {
|
||||
console.error('[DeviceSettings] Reboot failed:', error);
|
||||
Alert.alert('Error', 'Failed to reboot sensor');
|
||||
} finally {
|
||||
setIsRebooting(false);
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
const formatLastSeen = (lastSeen: Date): string => {
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - lastSeen.getTime();
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
const diffDays = Math.floor(diffMs / 86400000);
|
||||
|
||||
if (diffMins < 1) return 'Just now';
|
||||
if (diffMins < 60) return `${diffMins} min ago`;
|
||||
if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
||||
return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`;
|
||||
};
|
||||
|
||||
const getSignalStrength = (rssi: number): string => {
|
||||
if (rssi >= -50) return 'Excellent';
|
||||
if (rssi >= -60) return 'Good';
|
||||
if (rssi >= -70) return 'Fair';
|
||||
return 'Weak';
|
||||
};
|
||||
|
||||
const getSignalColor = (rssi: number) => {
|
||||
if (rssi >= -50) return AppColors.success;
|
||||
if (rssi >= -60) return AppColors.info;
|
||||
if (rssi >= -70) return AppColors.warning;
|
||||
return AppColors.error;
|
||||
};
|
||||
|
||||
if (isLoadingInfo || !sensorInfo) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
|
||||
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.headerTitle}>Sensor Settings</Text>
|
||||
<View style={styles.placeholder} />
|
||||
</View>
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color={AppColors.primary} />
|
||||
<Text style={styles.loadingText}>Loading sensor info...</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity style={styles.backButton} onPress={() => router.back()}>
|
||||
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.headerTitle}>Sensor Settings</Text>
|
||||
<View style={styles.placeholder} />
|
||||
</View>
|
||||
|
||||
{/* Simulator Warning */}
|
||||
{!isBLEAvailable && (
|
||||
<View style={styles.simulatorWarning}>
|
||||
<Ionicons name="information-circle" size={18} color={AppColors.warning} />
|
||||
<Text style={styles.simulatorWarningText}>
|
||||
Running in Simulator - BLE features use mock data
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<ScrollView style={styles.content} contentContainerStyle={styles.scrollContent}>
|
||||
{/* Sensor Info Card */}
|
||||
<View style={styles.sensorCard}>
|
||||
<View style={styles.sensorIcon}>
|
||||
<Ionicons name="water" size={48} color={AppColors.primary} />
|
||||
</View>
|
||||
<View style={styles.sensorInfo}>
|
||||
<Text style={styles.sensorName}>{sensorInfo.name}</Text>
|
||||
<View style={styles.statusRow}>
|
||||
<View
|
||||
style={[
|
||||
styles.statusDot,
|
||||
{
|
||||
backgroundColor:
|
||||
sensorInfo.status === 'online' ? AppColors.success : AppColors.error,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Text style={styles.statusText}>
|
||||
{sensorInfo.status === 'online' ? 'Online' : 'Offline'}
|
||||
</Text>
|
||||
<Text style={styles.statusSeparator}>•</Text>
|
||||
<Text style={styles.lastSeenText}>{formatLastSeen(sensorInfo.lastSeen)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Details Section */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Device Information</Text>
|
||||
<View style={styles.detailsCard}>
|
||||
<View style={styles.detailRow}>
|
||||
<Text style={styles.detailLabel}>Well ID</Text>
|
||||
<Text style={styles.detailValue}>{sensorInfo.wellId}</Text>
|
||||
</View>
|
||||
<View style={styles.detailDivider} />
|
||||
<View style={styles.detailRow}>
|
||||
<Text style={styles.detailLabel}>MAC Address</Text>
|
||||
<Text style={styles.detailValue}>{sensorInfo.mac}</Text>
|
||||
</View>
|
||||
<View style={styles.detailDivider} />
|
||||
<View style={styles.detailRow}>
|
||||
<Text style={styles.detailLabel}>Deployment ID</Text>
|
||||
<Text style={styles.detailValue}>{sensorInfo.deploymentId}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* BLE Connection Section */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Bluetooth Connection</Text>
|
||||
{isConnected ? (
|
||||
<View style={styles.connectedCard}>
|
||||
<View style={styles.connectedHeader}>
|
||||
<Ionicons name="bluetooth" size={24} color={AppColors.success} />
|
||||
<Text style={styles.connectedText}>Connected</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.disconnectButton}
|
||||
onPress={() => disconnectDevice(deviceId!)}
|
||||
>
|
||||
<Text style={styles.disconnectButtonText}>Disconnect</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<TouchableOpacity
|
||||
style={styles.connectButton}
|
||||
onPress={handleConnect}
|
||||
disabled={isConnecting}
|
||||
>
|
||||
{isConnecting ? (
|
||||
<ActivityIndicator size="small" color={AppColors.white} />
|
||||
) : (
|
||||
<Ionicons name="bluetooth" size={20} color={AppColors.white} />
|
||||
)}
|
||||
<Text style={styles.connectButtonText}>
|
||||
{isConnecting ? 'Connecting...' : 'Connect via Bluetooth'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* WiFi Status Section */}
|
||||
{isConnected && (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>WiFi Status</Text>
|
||||
{isLoadingWiFi ? (
|
||||
<View style={styles.loadingWiFiCard}>
|
||||
<ActivityIndicator size="small" color={AppColors.primary} />
|
||||
<Text style={styles.loadingWiFiText}>Loading WiFi status...</Text>
|
||||
</View>
|
||||
) : currentWiFi ? (
|
||||
<View style={styles.wifiCard}>
|
||||
<View style={styles.wifiHeader}>
|
||||
<Ionicons name="wifi" size={24} color={AppColors.success} />
|
||||
<View style={styles.wifiInfo}>
|
||||
<Text style={styles.wifiSSID}>{currentWiFi.ssid}</Text>
|
||||
<Text
|
||||
style={[styles.wifiSignal, { color: getSignalColor(currentWiFi.rssi) }]}
|
||||
>
|
||||
{getSignalStrength(currentWiFi.rssi)} ({currentWiFi.rssi} dBm)
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity style={styles.changeWiFiButton} onPress={handleChangeWiFi}>
|
||||
<Ionicons name="settings-outline" size={18} color={AppColors.primary} />
|
||||
<Text style={styles.changeWiFiText}>Change WiFi</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.noWiFiCard}>
|
||||
<Ionicons name="wifi-outline" size={32} color={AppColors.textMuted} />
|
||||
<Text style={styles.noWiFiText}>Not connected to WiFi</Text>
|
||||
<TouchableOpacity style={styles.setupWiFiButton} onPress={handleChangeWiFi}>
|
||||
<Text style={styles.setupWiFiText}>Setup WiFi</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Actions Section */}
|
||||
{isConnected && (
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Actions</Text>
|
||||
<View style={styles.actionsCard}>
|
||||
<TouchableOpacity
|
||||
style={styles.actionButton}
|
||||
onPress={loadWiFiStatus}
|
||||
disabled={isLoadingWiFi}
|
||||
>
|
||||
<Ionicons name="refresh" size={20} color={AppColors.primary} />
|
||||
<Text style={styles.actionButtonText}>Refresh WiFi Status</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.actionDivider} />
|
||||
<TouchableOpacity
|
||||
style={styles.actionButton}
|
||||
onPress={handleReboot}
|
||||
disabled={isRebooting}
|
||||
>
|
||||
{isRebooting ? (
|
||||
<ActivityIndicator size="small" color={AppColors.warning} />
|
||||
) : (
|
||||
<Ionicons name="power" size={20} color={AppColors.warning} />
|
||||
)}
|
||||
<Text style={[styles.actionButtonText, { color: AppColors.warning }]}>
|
||||
Reboot Sensor
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Info Card */}
|
||||
<View style={styles.infoCard}>
|
||||
<View style={styles.infoHeader}>
|
||||
<Ionicons name="information-circle" size={20} color={AppColors.info} />
|
||||
<Text style={styles.infoTitle}>About Settings</Text>
|
||||
</View>
|
||||
<Text style={styles.infoText}>
|
||||
• Connect via Bluetooth to view WiFi status and change settings{'\n'}
|
||||
• Make sure you're within range (about 10 meters) of the sensor{'\n'}
|
||||
• Rebooting will disconnect Bluetooth and restart the sensor
|
||||
</Text>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: AppColors.background,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: Spacing.md,
|
||||
paddingVertical: Spacing.sm,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: AppColors.border,
|
||||
},
|
||||
backButton: {
|
||||
padding: Spacing.xs,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: FontSizes.lg,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.textPrimary,
|
||||
},
|
||||
placeholder: {
|
||||
width: 32,
|
||||
},
|
||||
simulatorWarning: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: AppColors.warningLight,
|
||||
paddingVertical: Spacing.xs,
|
||||
paddingHorizontal: Spacing.md,
|
||||
gap: Spacing.xs,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: AppColors.warning,
|
||||
},
|
||||
simulatorWarningText: {
|
||||
fontSize: FontSizes.xs,
|
||||
color: AppColors.warning,
|
||||
fontWeight: FontWeights.medium,
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
scrollContent: {
|
||||
padding: Spacing.lg,
|
||||
paddingBottom: Spacing.xxl,
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.md,
|
||||
},
|
||||
loadingText: {
|
||||
fontSize: FontSizes.base,
|
||||
color: AppColors.textSecondary,
|
||||
},
|
||||
// Sensor Card
|
||||
sensorCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.xl,
|
||||
padding: Spacing.lg,
|
||||
marginBottom: Spacing.lg,
|
||||
...Shadows.sm,
|
||||
},
|
||||
sensorIcon: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: BorderRadius.lg,
|
||||
backgroundColor: AppColors.primaryLighter,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: Spacing.md,
|
||||
},
|
||||
sensorInfo: {
|
||||
flex: 1,
|
||||
},
|
||||
sensorName: {
|
||||
fontSize: FontSizes.xl,
|
||||
fontWeight: FontWeights.bold,
|
||||
color: AppColors.textPrimary,
|
||||
marginBottom: Spacing.xs,
|
||||
},
|
||||
statusRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
},
|
||||
statusDot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
statusText: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.textSecondary,
|
||||
fontWeight: FontWeights.medium,
|
||||
},
|
||||
statusSeparator: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.textMuted,
|
||||
},
|
||||
lastSeenText: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.textMuted,
|
||||
},
|
||||
// Section
|
||||
section: {
|
||||
marginBottom: Spacing.lg,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.textSecondary,
|
||||
marginBottom: Spacing.md,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
// Details Card
|
||||
detailsCard: {
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
...Shadows.xs,
|
||||
},
|
||||
detailRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingVertical: Spacing.sm,
|
||||
},
|
||||
detailLabel: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.textMuted,
|
||||
},
|
||||
detailValue: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.textPrimary,
|
||||
},
|
||||
detailDivider: {
|
||||
height: 1,
|
||||
backgroundColor: AppColors.border,
|
||||
},
|
||||
// BLE Connection
|
||||
connectedCard: {
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
...Shadows.xs,
|
||||
},
|
||||
connectedHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.sm,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
connectedText: {
|
||||
fontSize: FontSizes.base,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.success,
|
||||
},
|
||||
disconnectButton: {
|
||||
paddingVertical: Spacing.sm,
|
||||
paddingHorizontal: Spacing.md,
|
||||
borderRadius: BorderRadius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: AppColors.border,
|
||||
alignItems: 'center',
|
||||
},
|
||||
disconnectButtonText: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.medium,
|
||||
color: AppColors.textSecondary,
|
||||
},
|
||||
connectButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: AppColors.primary,
|
||||
paddingVertical: Spacing.md,
|
||||
borderRadius: BorderRadius.lg,
|
||||
gap: Spacing.sm,
|
||||
...Shadows.sm,
|
||||
},
|
||||
connectButtonText: {
|
||||
fontSize: FontSizes.base,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.white,
|
||||
},
|
||||
// WiFi Status
|
||||
loadingWiFiCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.md,
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
...Shadows.xs,
|
||||
},
|
||||
loadingWiFiText: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.textSecondary,
|
||||
},
|
||||
wifiCard: {
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
...Shadows.xs,
|
||||
},
|
||||
wifiHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.md,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
wifiInfo: {
|
||||
flex: 1,
|
||||
},
|
||||
wifiSSID: {
|
||||
fontSize: FontSizes.base,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.textPrimary,
|
||||
marginBottom: 2,
|
||||
},
|
||||
wifiSignal: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.medium,
|
||||
},
|
||||
changeWiFiButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: Spacing.xs,
|
||||
paddingVertical: Spacing.sm,
|
||||
paddingHorizontal: Spacing.md,
|
||||
borderRadius: BorderRadius.md,
|
||||
borderWidth: 1,
|
||||
borderColor: AppColors.primary,
|
||||
},
|
||||
changeWiFiText: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.medium,
|
||||
color: AppColors.primary,
|
||||
},
|
||||
noWiFiCard: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.xl,
|
||||
...Shadows.xs,
|
||||
},
|
||||
noWiFiText: {
|
||||
fontSize: FontSizes.base,
|
||||
color: AppColors.textMuted,
|
||||
marginTop: Spacing.md,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
setupWiFiButton: {
|
||||
paddingVertical: Spacing.sm,
|
||||
paddingHorizontal: Spacing.lg,
|
||||
},
|
||||
setupWiFiText: {
|
||||
fontSize: FontSizes.base,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.primary,
|
||||
},
|
||||
// Actions
|
||||
actionsCard: {
|
||||
backgroundColor: AppColors.surface,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
...Shadows.xs,
|
||||
},
|
||||
actionButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.sm,
|
||||
paddingVertical: Spacing.sm,
|
||||
},
|
||||
actionButtonText: {
|
||||
fontSize: FontSizes.base,
|
||||
fontWeight: FontWeights.medium,
|
||||
color: AppColors.primary,
|
||||
},
|
||||
actionDivider: {
|
||||
height: 1,
|
||||
backgroundColor: AppColors.border,
|
||||
marginVertical: Spacing.sm,
|
||||
},
|
||||
// Info Card
|
||||
infoCard: {
|
||||
backgroundColor: AppColors.infoLight,
|
||||
borderRadius: BorderRadius.lg,
|
||||
padding: Spacing.md,
|
||||
},
|
||||
infoHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: Spacing.sm,
|
||||
marginBottom: Spacing.xs,
|
||||
},
|
||||
infoTitle: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.info,
|
||||
},
|
||||
infoText: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.info,
|
||||
lineHeight: 20,
|
||||
},
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user