add allday to calendar model

This commit is contained in:
c-d-p
2025-04-23 19:25:50 +02:00
parent 1f8b81e69c
commit 4d41aa9136
5 changed files with 103 additions and 42 deletions

View File

@@ -75,12 +75,15 @@ const EventItem: React.FC<EventItemProps> = ({ event, showTime = true }) => {
navigation.navigate('EventForm', { eventId: event.id });
};
const timeString = showTime ? format(startDate, 'p') : ''; // Format time like 1:00 PM
// Determine if time should be shown: only if showTime is true AND the event is NOT all_day
const shouldDisplayTime = showTime && !event.all_day;
const timeString = shouldDisplayTime ? format(startDate, 'p') : ''; // Format time like 1:00 PM
return (
<TouchableOpacity onPress={handlePress} style={styles.container}>
<Text style={styles.text} numberOfLines={2} ellipsizeMode='clip'>
{showTime && <Text style={styles.timeText}>{timeString} </Text>}
{/* Conditionally render time based on shouldDisplayTime */}
{shouldDisplayTime && <Text style={styles.timeText}>{timeString} </Text>}
{event.title}
</Text>
{/* Optional: Display tags if they exist */}

View File

@@ -2,8 +2,8 @@
import React, { useState, useEffect, useCallback } from 'react';
// Add Platform import
import { View, StyleSheet, ScrollView, Alert, TouchableOpacity, Platform } from 'react-native';
// Add Chip
import { TextInput, Button, useTheme, Text, ActivityIndicator, HelperText, Chip } from 'react-native-paper';
// Add Chip and Switch
import { TextInput, Button, useTheme, Text, ActivityIndicator, HelperText, Chip, Switch } from 'react-native-paper';
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
// Conditionally import DateTimePickerModal only if not on web
// Note: This dynamic import might not work as expected depending on the bundler setup.
@@ -35,6 +35,7 @@ const EventFormScreen = () => {
const [endDate, setEndDate] = useState<Date | null>(null);
const [color, setColor] = useState(''); // Basic color input for now
const [location, setLocation] = useState(''); // Add location state
const [isAllDay, setIsAllDay] = useState(false); // Add all_day state
const [tags, setTags] = useState<string[]>([]); // Add tags state
const [currentTagInput, setCurrentTagInput] = useState(''); // State for tag input field
@@ -63,6 +64,7 @@ const EventFormScreen = () => {
setDescription(event.description || '');
setColor(event.color || ''); // Use optional color
setLocation(event.location || ''); // Set location state
setIsAllDay(event.all_day ?? false); // Set all_day state, default to false if null/undefined
setTags(event.tags || []); // Load tags or default to empty array
// Ensure dates are Date objects
if (event.start && isValid(parseISO(event.start))) {
@@ -116,6 +118,7 @@ const EventFormScreen = () => {
setEndDate(null);
setWebEndDateInput('');
}
setIsAllDay(false); // Default all_day to false for new events
setTags([]); // Ensure tags start empty for new event
} else {
// Default start date to now if creating without a selected date
@@ -124,6 +127,7 @@ const EventFormScreen = () => {
setWebStartDateInput(formatForWebInput(now)); // Init web input
setEndDate(null);
setWebEndDateInput('');
setIsAllDay(false); // Default all_day to false for new events
setTags([]); // Ensure tags start empty for new event
}
}, [eventId, selectedDate]);
@@ -281,6 +285,7 @@ const EventFormScreen = () => {
end: endDate ? endDate.toISOString() : null,
location: location.trim() || null, // Include location
color: color.trim() || null, // Include color
all_day: isAllDay, // Include all_day state
tags: tags.length > 0 ? tags : null, // Include tags, send null if empty
};
@@ -334,8 +339,9 @@ const EventFormScreen = () => {
const formatDateTime = (date: Date | null): string => {
if (!date) return '';
try {
// Native uses 'MMM d, yyyy, p', web input is handled separately
return format(date, "MMM d, yyyy, p");
// If all_day, only show the date part
const formatString = isAllDay ? "MMM d, yyyy" : "MMM d, yyyy, p";
return format(date, formatString);
} catch {
return "Invalid Date";
}
@@ -345,7 +351,9 @@ const EventFormScreen = () => {
const formatForWebInput = (date: Date | null): string => {
if (!date || !isValid(date)) return '';
try {
return format(date, "yyyy-MM-dd HH:mm");
// If all_day, only format the date part for the input
const formatString = isAllDay ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm";
return format(date, formatString);
} catch {
return ''; // Return empty if formatting fails
}
@@ -364,6 +372,32 @@ const EventFormScreen = () => {
setTags(tags.filter(tag => tag !== tagToRemove));
};
// --- All Day Toggle Logic ---
const onToggleAllDay = () => {
const newAllDay = !isAllDay;
setIsAllDay(newAllDay);
// Adjust dates when toggling all_day
if (startDate) {
const newStartDate = new Date(startDate);
if (newAllDay) {
// Set time to beginning of the day
newStartDate.setHours(0, 0, 0, 0);
// Optionally clear end date or set it to end of the same day
setEndDate(null);
setWebEndDateInput('');
} else {
// Restore a default time (e.g., 9 AM) or keep the previous time if stored
newStartDate.setHours(9, 0, 0, 0);
}
setStartDate(newStartDate);
setWebStartDateInput(formatForWebInput(newStartDate)); // Update web input
// Re-validate if end date was cleared or start date changed
validateForm({ start: newStartDate, end: newAllDay ? null : endDate });
}
};
if (isLoading && !title) { // Show loading indicator only during initial fetch
return <ActivityIndicator animating={true} style={styles.loading} />;
}
@@ -387,44 +421,54 @@ const EventFormScreen = () => {
{formErrors.title}
</HelperText>
{/* All Day Switch */}
<View style={styles.switchContainer}>
<Text>All-day event</Text>
<Switch value={isAllDay} onValueChange={onToggleAllDay} />
</View>
{/* Start Date Input - Conditional Logic */}
<TouchableOpacity onPress={showStartDatePicker} disabled={Platform.OS === 'web'}>
<TouchableOpacity onPress={showStartDatePicker} disabled={Platform.OS === 'web' || isAllDay}>
<TextInput
label="Start Date & Time *"
label={isAllDay ? "Date *" : "Start Date & Time *"}
// Use raw web input state for value on web, formatted Date otherwise
value={Platform.OS === 'web' ? webStartDateInput : formatDateTime(startDate)}
editable={Platform.OS === 'web'}
onChangeText={Platform.OS === 'web' ? (text) => handleWebDateInputChange(text, 'start') : undefined}
placeholder={Platform.OS === 'web' ? 'YYYY-MM-DD HH:mm' : ''}
editable={Platform.OS === 'web' && !isAllDay}
onChangeText={Platform.OS === 'web' && !isAllDay ? (text) => handleWebDateInputChange(text, 'start') : undefined}
placeholder={Platform.OS === 'web' ? (isAllDay ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm') : ''}
mode="outlined"
style={styles.input}
right={Platform.OS !== 'web' ? <TextInput.Icon icon="calendar-clock" onPress={showStartDatePicker} /> : null}
right={Platform.OS !== 'web' && !isAllDay ? <TextInput.Icon icon="calendar-clock" onPress={showStartDatePicker} /> : <TextInput.Icon icon="calendar" onPress={showStartDatePicker} />}
error={!!formErrors.start}
disabled={isAllDay && Platform.OS !== 'web'} // Disable native text input editing for all-day
/>
</TouchableOpacity>
<HelperText type="error" visible={!!formErrors.start}>
{formErrors.start}
</HelperText>
{/* End Date Input - Conditional Logic */}
<TouchableOpacity onPress={showEndDatePicker} disabled={Platform.OS === 'web'}>
<TextInput
label="End Date & Time"
// Use raw web input state for value on web, formatted Date otherwise
value={Platform.OS === 'web' ? webEndDateInput : formatDateTime(endDate)}
editable={Platform.OS === 'web'}
onChangeText={Platform.OS === 'web' ? (text) => handleWebDateInputChange(text, 'end') : undefined}
placeholder={Platform.OS === 'web' ? 'YYYY-MM-DD HH:mm' : ''}
mode="outlined"
style={styles.input}
right={Platform.OS !== 'web' ? <TextInput.Icon icon="calendar-clock" onPress={showEndDatePicker} /> : null}
error={!!formErrors.end}
/>
</TouchableOpacity>
<HelperText type="error" visible={!!formErrors.end}>
{formErrors.end}
</HelperText>
{/* End Date Input - Conditionally render based on all_day */}
{!isAllDay && (
<>
<TouchableOpacity onPress={showEndDatePicker} disabled={Platform.OS === 'web'}>
<TextInput
label="End Date & Time"
// Use raw web input state for value on web, formatted Date otherwise
value={Platform.OS === 'web' ? webEndDateInput : formatDateTime(endDate)}
editable={Platform.OS === 'web'}
onChangeText={Platform.OS === 'web' ? (text) => handleWebDateInputChange(text, 'end') : undefined}
placeholder={Platform.OS === 'web' ? 'YYYY-MM-DD HH:mm' : ''}
mode="outlined"
style={styles.input}
right={Platform.OS !== 'web' ? <TextInput.Icon icon="calendar-clock" onPress={showEndDatePicker} /> : null}
error={!!formErrors.end}
/>
</TouchableOpacity>
<HelperText type="error" visible={!!formErrors.end}>
{formErrors.end}
</HelperText>
</>
)}
{/* Add Location Input */}
<TextInput
@@ -511,21 +555,24 @@ const EventFormScreen = () => {
<>
<DateTimePickerModal
isVisible={isStartDatePickerVisible}
mode="datetime"
mode={isAllDay ? "date" : "datetime"} // Use 'date' mode if all_day
date={startDate || new Date()} // Default picker to current start date or now
onConfirm={handleStartDateConfirm}
onCancel={hideStartDatePicker}
is24Hour={false} // Adjust based on preference
/>
<DateTimePickerModal
isVisible={isEndDatePickerVisible}
mode="datetime"
date={endDate || startDate || new Date()} // Default picker to end, start, or now
minimumDate={startDate || undefined} // Prevent end date being before start date
onConfirm={handleEndDateConfirm}
onCancel={hideEndDatePicker}
is24Hour={false}
/>
{/* Only show end date picker if not all_day */}
{!isAllDay && (
<DateTimePickerModal
isVisible={isEndDatePickerVisible}
mode="datetime"
date={endDate || startDate || new Date()} // Default picker to end, start, or now
minimumDate={startDate || undefined} // Prevent end date being before start date
onConfirm={handleEndDateConfirm}
onCancel={hideEndDatePicker}
is24Hour={false}
/>
)}
</>
)}
</View>
@@ -549,6 +596,13 @@ const styles = StyleSheet.create({
input: {
marginBottom: 5, // Reduced margin as HelperText adds space
},
switchContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15, // Add some space below the switch
paddingHorizontal: 4, // Align text with TextInput label
},
button: {
marginTop: 15,
},

View File

@@ -8,6 +8,7 @@ export interface CalendarEvent {
end?: string | null; // ISO string format
location?: string | null; // Make optional
color?: string | null; // Keep optional
all_day?: boolean | null; // Add all_day field
tags?: string[]; // Add optional tags array
user_id: number; // Add user_id as it's in the response
}
@@ -20,6 +21,7 @@ export type CalendarEventCreate = {
end?: string | null; // ISO string format
location?: string | null;
color?: string | null;
all_day?: boolean | null; // Add all_day field
tags?: string[]; // Add optional tags array
};
@@ -31,5 +33,6 @@ export type CalendarEventUpdate = {
end?: string | null; // ISO string format
location?: string | null;
color?: string | null;
all_day?: boolean | null; // Add all_day field
tags?: string[]; // Add optional tags array
};