[V1.0] Working application, added notifications.
Ready to upload to store.
This commit is contained in:
@@ -1,52 +1,99 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Button, Checkbox, Text, ActivityIndicator, Snackbar } from 'react-native-paper';
|
||||
import { Button, Checkbox, Text, ActivityIndicator, Snackbar, TextInput, Divider, useTheme } from 'react-native-paper'; // Added TextInput, Divider, useTheme
|
||||
import { clearDatabase } from '../api/admin';
|
||||
// Remove useNavigation import if no longer needed elsewhere in this file
|
||||
// import { useNavigation } from '@react-navigation/native';
|
||||
import { useAuth } from '../contexts/AuthContext'; // Import useAuth
|
||||
import apiClient from '../api/client'; // Import apiClient
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
const AdminScreen = () => {
|
||||
const [isHardClear, setIsHardClear] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [snackbarVisible, setSnackbarVisible] = useState(false);
|
||||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||||
// const navigation = useNavigation(); // Remove if not used elsewhere
|
||||
const { logout } = useAuth(); // Get the logout function from context
|
||||
const theme = useTheme(); // Get theme for styling if needed
|
||||
|
||||
// --- State for Clear DB ---
|
||||
const [isHardClear, setIsHardClear] = useState(false);
|
||||
const [isClearingDb, setIsClearingDb] = useState(false); // Renamed from isLoading
|
||||
const [clearDbSnackbarVisible, setClearDbSnackbarVisible] = useState(false); // Renamed
|
||||
const [clearDbSnackbarMessage, setClearDbSnackbarMessage] = useState(''); // Renamed
|
||||
|
||||
// --- State for Send Notification ---
|
||||
const [username, setUsername] = useState('');
|
||||
const [title, setTitle] = useState('');
|
||||
const [body, setBody] = useState('');
|
||||
const [isSendingNotification, setIsSendingNotification] = useState(false); // New loading state
|
||||
const [notificationError, setNotificationError] = useState<string | null>(null); // New error state
|
||||
const [notificationSuccess, setNotificationSuccess] = useState<string | null>(null); // New success state
|
||||
|
||||
const { logout } = useAuth();
|
||||
|
||||
// --- Clear DB Handler ---
|
||||
const handleClearDb = async () => {
|
||||
setIsLoading(true);
|
||||
setSnackbarVisible(false);
|
||||
setIsClearingDb(true); // Use renamed state
|
||||
setClearDbSnackbarVisible(false);
|
||||
try {
|
||||
const response = await clearDatabase(isHardClear);
|
||||
setSnackbarMessage(response.message || 'Database cleared successfully.');
|
||||
setSnackbarVisible(true);
|
||||
setClearDbSnackbarMessage(response.message || 'Database cleared successfully.');
|
||||
setClearDbSnackbarVisible(true);
|
||||
|
||||
// If hard clear was successful, trigger the logout process from AuthContext
|
||||
if (isHardClear) {
|
||||
console.log('Hard clear successful, calling logout...');
|
||||
await logout(); // Call the logout function from AuthContext
|
||||
// The RootNavigator will automatically switch to the AuthFlow
|
||||
// No need to manually navigate or set loading to false here
|
||||
return; // Exit early
|
||||
await logout();
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error("Error clearing database:", error);
|
||||
setSnackbarMessage(error.response?.data?.detail || 'Failed to clear database.');
|
||||
setSnackbarVisible(true);
|
||||
setClearDbSnackbarMessage(error.response?.data?.detail || 'Failed to clear database.');
|
||||
setClearDbSnackbarVisible(true);
|
||||
} finally {
|
||||
// Only set loading to false if it wasn't a hard clear (as logout handles navigation)
|
||||
if (!isHardClear) {
|
||||
setIsLoading(false);
|
||||
setIsClearingDb(false); // Use renamed state
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- Send Notification Handler ---
|
||||
const handleSendNotification = async () => {
|
||||
if (!username || !title || !body) {
|
||||
setNotificationError('Username, Title, and Body are required.');
|
||||
setNotificationSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSendingNotification(true);
|
||||
setNotificationError(null);
|
||||
setNotificationSuccess(null);
|
||||
|
||||
try {
|
||||
const response = await apiClient.post('/admin/send-notification', {
|
||||
username,
|
||||
title,
|
||||
body,
|
||||
// data: {} // Add optional data payload if needed
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
setNotificationSuccess(response.data.message || 'Notification sent successfully!');
|
||||
// Clear fields after success
|
||||
setUsername('');
|
||||
setTitle('');
|
||||
setBody('');
|
||||
} else {
|
||||
setNotificationError(response.data?.detail || 'Failed to send notification.');
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error("Error sending notification:", err.response?.data || err.message);
|
||||
setNotificationError(err.response?.data?.detail || 'An error occurred while sending the notification.');
|
||||
} finally {
|
||||
setIsSendingNotification(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text variant="headlineMedium" style={styles.title}>Admin Controls</Text>
|
||||
|
||||
{/* --- Clear Database Section --- */}
|
||||
<Text variant="titleMedium" style={styles.sectionTitle}>Clear Database</Text>
|
||||
<View style={styles.checkboxContainer}>
|
||||
<Checkbox
|
||||
status={isHardClear ? 'checked' : 'unchecked'}
|
||||
@@ -54,24 +101,68 @@ const AdminScreen = () => {
|
||||
/>
|
||||
<Text onPress={() => setIsHardClear(!isHardClear)}>Hard Clear (Delete all data)</Text>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleClearDb}
|
||||
disabled={isLoading}
|
||||
disabled={isClearingDb} // Use renamed state
|
||||
style={styles.button}
|
||||
buttonColor="red" // Make it look dangerous
|
||||
buttonColor="red"
|
||||
>
|
||||
{isLoading ? <ActivityIndicator animating={true} color="white" /> : 'Clear Database'}
|
||||
{isClearingDb ? <ActivityIndicator animating={true} color="white" /> : 'Clear Database'}
|
||||
</Button>
|
||||
|
||||
<Snackbar
|
||||
visible={snackbarVisible}
|
||||
onDismiss={() => setSnackbarVisible(false)}
|
||||
visible={clearDbSnackbarVisible} // Use renamed state
|
||||
onDismiss={() => setClearDbSnackbarVisible(false)}
|
||||
duration={Snackbar.DURATION_SHORT}
|
||||
>
|
||||
{snackbarMessage}
|
||||
{clearDbSnackbarMessage} {/* Use renamed state */}
|
||||
</Snackbar>
|
||||
|
||||
<Divider style={styles.divider} />
|
||||
|
||||
{/* --- Send Notification Section --- */}
|
||||
<Text variant="titleMedium" style={styles.sectionTitle}>Send Push Notification</Text>
|
||||
|
||||
{notificationError && <Text style={[styles.message, { color: theme.colors.error }]}>{notificationError}</Text>}
|
||||
{notificationSuccess && <Text style={[styles.message, { color: theme.colors.primary }]}>{notificationSuccess}</Text>}
|
||||
|
||||
<TextInput
|
||||
label="Username"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
disabled={isSendingNotification}
|
||||
/>
|
||||
<TextInput
|
||||
label="Notification Title"
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
disabled={isSendingNotification}
|
||||
/>
|
||||
<TextInput
|
||||
label="Notification Body"
|
||||
value={body}
|
||||
onChangeText={setBody}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
multiline
|
||||
numberOfLines={3}
|
||||
disabled={isSendingNotification}
|
||||
/>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleSendNotification}
|
||||
loading={isSendingNotification}
|
||||
disabled={isSendingNotification}
|
||||
style={styles.button}
|
||||
>
|
||||
{isSendingNotification ? 'Sending...' : 'Send Notification'}
|
||||
</Button>
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -80,19 +171,37 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 20,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
// Removed justifyContent and alignItems to allow scrolling if content overflows
|
||||
},
|
||||
title: {
|
||||
marginBottom: 30,
|
||||
marginBottom: 20, // Reduced margin
|
||||
textAlign: 'center',
|
||||
},
|
||||
sectionTitle: {
|
||||
marginBottom: 15,
|
||||
marginTop: 10, // Add some space before the title
|
||||
textAlign: 'center',
|
||||
},
|
||||
checkboxContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 20,
|
||||
marginBottom: 10, // Reduced margin
|
||||
justifyContent: 'center', // Center checkbox
|
||||
},
|
||||
button: {
|
||||
marginTop: 10,
|
||||
marginBottom: 10, // Add margin below button
|
||||
},
|
||||
input: {
|
||||
marginBottom: 15,
|
||||
},
|
||||
message: {
|
||||
marginBottom: 15,
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
divider: {
|
||||
marginVertical: 30, // Add vertical space around the divider
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -143,13 +143,6 @@ const EventFormScreen = () => {
|
||||
const handleStartDateConfirm = (date: Date) => {
|
||||
setStartDate(date);
|
||||
setWebStartDateInput(formatForWebInput(date)); // Update web input state
|
||||
// Optional: Auto-set end date if it's before start date or null
|
||||
if (!endDate || endDate < date) {
|
||||
const newEndDate = new Date(date);
|
||||
newEndDate.setHours(date.getHours() + 1); // Default to 1 hour later
|
||||
setEndDate(newEndDate);
|
||||
setWebEndDateInput(formatForWebInput(newEndDate)); // Update web input state
|
||||
}
|
||||
validateForm({ start: date }); // Validate after setting
|
||||
hideStartDatePicker();
|
||||
};
|
||||
@@ -189,13 +182,6 @@ const EventFormScreen = () => {
|
||||
if (isValid(parsedDate) && text.length >= 15) { // Basic length check for 'yyyy-MM-dd HH:mm'
|
||||
if (type === 'start') {
|
||||
setStartDate(parsedDate);
|
||||
// Optional: Auto-set end date
|
||||
if (!endDate || endDate < parsedDate) {
|
||||
const newEndDate = new Date(parsedDate);
|
||||
newEndDate.setHours(parsedDate.getHours() + 1);
|
||||
setEndDate(newEndDate);
|
||||
setWebEndDateInput(formatForWebInput(newEndDate)); // Update other web input too
|
||||
}
|
||||
validateForm({ start: parsedDate }); // Validate with the actual Date
|
||||
} else {
|
||||
setEndDate(parsedDate);
|
||||
|
||||
Reference in New Issue
Block a user