From 7f79a1c8198cce49148bcdc2d0fec60b9450d1d4 Mon Sep 17 00:00:00 2001 From: ivic00 <102467664+ivic00@users.noreply.github.com> Date: Wed, 30 Oct 2024 00:02:25 +0100 Subject: [PATCH] added confirmation dialogs, fixed scroll wheels ui tweaks --- app/(auth)/_layout.tsx | 7 + assets/svgs/OutlookIcon.tsx | 4 +- .../pages/brain_dump/BrainDumpDialog.tsx | 82 ++ components/pages/brain_dump/MoveBrainDump.tsx | 23 +- .../pages/calendar/DeleteEventDialog.tsx | 81 ++ components/pages/calendar/EventCalendar.tsx | 11 +- .../pages/calendar/ManuallyAddEventModal.tsx | 1158 +++++++++-------- components/pages/calendar/atoms.ts | 1 + components/pages/grocery/AddGroceryItem.tsx | 2 +- components/pages/grocery/GroceryWrapper.tsx | 60 +- .../pages/settings/CalendarSettingsPage.tsx | 43 +- .../CalendarSettingsDialog.tsx | 86 ++ components/pages/todos/ToDoItem.tsx | 36 +- components/pages/todos/ToDosPage.tsx | 137 +- 14 files changed, 1077 insertions(+), 654 deletions(-) create mode 100644 components/pages/brain_dump/BrainDumpDialog.tsx create mode 100644 components/pages/calendar/DeleteEventDialog.tsx create mode 100644 components/pages/settings/calendar_components/CalendarSettingsDialog.tsx diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index 3f470fe..a735503 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -26,6 +26,7 @@ import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon"; import { useAtom } from "jotai"; import { settingsPageIndex, + toDosPageIndex, userSettingsView, } from "@/components/pages/calendar/atoms"; @@ -33,6 +34,7 @@ export default function TabLayout() { const { mutateAsync: signOut } = useSignOut(); const [pageIndex, setPageIndex] = useAtom(settingsPageIndex); const [userView, setUserView] = useAtom(userSettingsView); + const [toDosIndex, setToDosIndex] = useAtom(toDosPageIndex); return ( { props.navigation.navigate("calendar"); setPageIndex(0); + setToDosIndex(0); setUserView(true); }} icon={} @@ -87,6 +90,7 @@ export default function TabLayout() { pressFunc={() => { props.navigation.navigate("grocery"); setPageIndex(0); + setToDosIndex(0); setUserView(true); }} icon={} @@ -113,6 +117,7 @@ export default function TabLayout() { pressFunc={() => { props.navigation.navigate("todos"); setPageIndex(0); + setToDosIndex(0); setUserView(true); }} icon={} @@ -124,6 +129,7 @@ export default function TabLayout() { pressFunc={() => { props.navigation.navigate("brain_dump"); setPageIndex(0); + setToDosIndex(0); setUserView(true); }} icon={} @@ -135,6 +141,7 @@ export default function TabLayout() { onPress={() => { props.navigation.navigate("settings"); setPageIndex(0); + setToDosIndex(0); setUserView(true); }} label={"Manage Settings"} diff --git a/assets/svgs/OutlookIcon.tsx b/assets/svgs/OutlookIcon.tsx index 719d0de..1e0626a 100644 --- a/assets/svgs/OutlookIcon.tsx +++ b/assets/svgs/OutlookIcon.tsx @@ -3,8 +3,8 @@ import Svg, { Path, LinearGradient, Stop, SvgProps } from "react-native-svg"; const OutlookIcon: React.FC = (props) => ( diff --git a/components/pages/brain_dump/BrainDumpDialog.tsx b/components/pages/brain_dump/BrainDumpDialog.tsx new file mode 100644 index 0000000..bffe876 --- /dev/null +++ b/components/pages/brain_dump/BrainDumpDialog.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { Dialog, Button, Text, View } from "react-native-ui-lib"; +import { StyleSheet } from "react-native"; + +interface BrainDumpDialogProps { + visible: boolean; + title: string; + onDismiss: () => void; + onConfirm: () => void; +} + +const BrainDumpDialog: React.FC = ({ + visible, + title, + onDismiss, + onConfirm, +}) => { + return ( + + + Delete Note + + + + Are you sure you want to delete the {"\n"} + + {title} + {" "} + Note? + + + + + + + + ); +}; + +// Empty stylesheet for future styles +const styles = StyleSheet.create({ + confirmBtn: { + backgroundColor: "#ea156d", + }, + cancelBtn: { + backgroundColor: "white", + }, + dialog: { + backgroundColor: "white", + paddingHorizontal: 25, + paddingTop: 35, + paddingBottom: 17, + borderRadius: 20, + }, + title: { + fontFamily: "Manrope_600SemiBold", + fontSize: 22, + marginBottom: 20, + }, + text: { + fontFamily: "PlusJakartaSans_400Regular", + fontSize: 16, + marginBottom: 25, + }, +}); + +export default BrainDumpDialog; diff --git a/components/pages/brain_dump/MoveBrainDump.tsx b/components/pages/brain_dump/MoveBrainDump.tsx index d576f41..7480767 100644 --- a/components/pages/brain_dump/MoveBrainDump.tsx +++ b/components/pages/brain_dump/MoveBrainDump.tsx @@ -18,6 +18,7 @@ import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon"; import NavToDosIcon from "@/assets/svgs/NavToDosIcon"; import RemindersIcon from "@/assets/svgs/RemindersIcon"; import MenuIcon from "@/assets/svgs/MenuIcon"; +import BrainDumpDialog from "./BrainDumpDialog"; const MoveBrainDump = (props: { item: IBrainDump; @@ -28,12 +29,26 @@ const MoveBrainDump = (props: { const [description, setDescription] = useState( props.item.description ); + const [modalVisible, setModalVisible] = useState(false); + const { width } = Dimensions.get("screen"); useEffect(() => { updateBrainDumpItem(props.item.id, { description: description }); }, [description]); + const showConfirmationDialog = () => { + setModalVisible(true); + }; + + const handleDeleteNote = () =>{ + deleteBrainDump(props.item.id); + } + + const hideConfirmationDialog = () => { + setModalVisible(false); + } + return ( } onPress={() => { - deleteBrainDump(props.item.id); - props.setIsVisible(false); + showConfirmationDialog(); }} /> @@ -145,6 +159,7 @@ const MoveBrainDump = (props: { + ); }; @@ -191,10 +206,10 @@ const styles = StyleSheet.create({ fontSize: 22, fontFamily: "Manrope_500Medium", }, - description:{ + description: { fontFamily: "Manrope_400Regular", fontSize: 14, - } + }, }); export default MoveBrainDump; diff --git a/components/pages/calendar/DeleteEventDialog.tsx b/components/pages/calendar/DeleteEventDialog.tsx new file mode 100644 index 0000000..33bd2d9 --- /dev/null +++ b/components/pages/calendar/DeleteEventDialog.tsx @@ -0,0 +1,81 @@ +import React from "react"; +import { Dialog, Button, Text, View } from "react-native-ui-lib"; +import { StyleSheet } from "react-native"; + +interface DeleteEventDialogProps { + visible: boolean; + title: string; + onDismiss: () => void; + onConfirm: () => void; +} + +const DeleteEventDialog: React.FC = ({ + visible, + title, + onDismiss, + onConfirm, +}) => { + return ( + + + Delete Event + + + + Are you sure you want to delete the event:{" "} + + {title} + {" "} + + + + + + + + ); +}; + +// Empty stylesheet for future styles +const styles = StyleSheet.create({ + confirmBtn: { + backgroundColor: "#ea156d", + }, + cancelBtn: { + backgroundColor: "white", + }, + dialog: { + backgroundColor: "white", + paddingHorizontal: 25, + paddingTop: 35, + paddingBottom: 17, + borderRadius: 20, + }, + title: { + fontFamily: "Manrope_600SemiBold", + fontSize: 22, + marginBottom: 20, + }, + text: { + fontFamily: "PlusJakartaSans_400Regular", + fontSize: 16, + marginBottom: 25, + }, +}); + +export default DeleteEventDialog; diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx index 43fc1c3..da7500b 100644 --- a/components/pages/calendar/EventCalendar.tsx +++ b/components/pages/calendar/EventCalendar.tsx @@ -207,7 +207,7 @@ export const EventCalendar: React.FC = React.memo( scrollOffsetMinutes={offsetMinutes} theme={{ palette: { - nowIndicator: "#fd1575", + nowIndicator: profileData?.eventColor || "#fd1575", gray: { "100": "#e8eaed", "200": "#e8eaed", @@ -223,13 +223,15 @@ export const EventCalendar: React.FC = React.memo( fontSize: 16, }, moreLabel: {}, - xs:{fontSize: 10} + xs: { fontSize: 10 }, }, }} dayHeaderStyle={dateStyle} dayHeaderHighlightColor={"white"} renderCustomDateForMonth={renderCustomDateForMonth} showAdjacentMonths + hourStyle={styles.hourStyle} + ampm /> ); } @@ -273,4 +275,9 @@ const styles = StyleSheet.create({ alignItems: "center", justifyContent: "center", }, + hourStyle: { + color: "#5f6368", + fontSize: 12, + fontFamily: "Manrope_500Medium", + }, }); diff --git a/components/pages/calendar/ManuallyAddEventModal.tsx b/components/pages/calendar/ManuallyAddEventModal.tsx index 468d76f..888ec2f 100644 --- a/components/pages/calendar/ManuallyAddEventModal.tsx +++ b/components/pages/calendar/ManuallyAddEventModal.tsx @@ -1,569 +1,655 @@ import { - Button, - ButtonSize, - Colors, - DateTimePicker, - LoaderScreen, - Modal, - Picker, - PickerModes, - Switch, - Text, - TextField, - TextFieldRef, - TouchableOpacity, - View, + Button, + ButtonSize, + Colors, + DateTimePicker, + LoaderScreen, + Modal, + Picker, + PickerModes, + Switch, + Text, + TextField, + TextFieldRef, + TouchableOpacity, + View, } from "react-native-ui-lib"; -import {ScrollView} from "react-native-gesture-handler"; -import {useSafeAreaInsets} from "react-native-safe-area-context"; -import {useEffect, useRef, useState} from "react"; -import {AntDesign, Feather, Ionicons,} from "@expo/vector-icons"; -import {PickerMultiValue} from "react-native-ui-lib/src/components/picker/types"; -import {useCreateEvent} from "@/hooks/firebase/useCreateEvent"; -import {EventData} from "@/hooks/firebase/types/eventData"; -import {addHours} from "date-fns"; +import { ScrollView } from "react-native-gesture-handler"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useEffect, useRef, useState } from "react"; +import { AntDesign, Feather, Ionicons } from "@expo/vector-icons"; +import { PickerMultiValue } from "react-native-ui-lib/src/components/picker/types"; +import { useCreateEvent } from "@/hooks/firebase/useCreateEvent"; +import { EventData } from "@/hooks/firebase/types/eventData"; +import { addHours } from "date-fns"; import DropModalIcon from "@/assets/svgs/DropModalIcon"; -import {StyleSheet} from "react-native"; +import { StyleSheet } from "react-native"; import ClockIcon from "@/assets/svgs/ClockIcon"; import LockIcon from "@/assets/svgs/LockIcon"; import MenuIcon from "@/assets/svgs/MenuIcon"; import CameraIcon from "@/assets/svgs/CameraIcon"; import AssigneesDisplay from "@/components/shared/AssigneesDisplay"; -import {useAtom} from "jotai"; -import {eventForEditAtom, selectedNewEventDateAtom} from "@/components/pages/calendar/atoms"; -import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; +import { useAtom } from "jotai"; +import { + eventForEditAtom, + selectedNewEventDateAtom, +} from "@/components/pages/calendar/atoms"; +import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; +import BinIcon from "@/assets/svgs/BinIcon"; +import CloseXIcon from "@/assets/svgs/CloseXIcon"; +import PenIcon from "@/assets/svgs/PenIcon"; +import DeleteEventDialog from "./DeleteEventDialog"; const daysOfWeek = [ - {label: "Monday", value: "monday"}, - {label: "Tuesday", value: "tuesday"}, - {label: "Wednesday", value: "wednesday"}, - {label: "Thursday", value: "thursday"}, - {label: "Friday", value: "friday"}, - {label: "Saturday", value: "saturday"}, - {label: "Sunday", value: "sunday"}, + { label: "Monday", value: "monday" }, + { label: "Tuesday", value: "tuesday" }, + { label: "Wednesday", value: "wednesday" }, + { label: "Thursday", value: "thursday" }, + { label: "Friday", value: "friday" }, + { label: "Saturday", value: "saturday" }, + { label: "Sunday", value: "sunday" }, ]; export const ManuallyAddEventModal = () => { - const insets = useSafeAreaInsets(); + const insets = useSafeAreaInsets(); - const [selectedNewEventDate, setSelectedNewEndDate] = useAtom(selectedNewEventDateAtom) - const [editEvent, setEditEvent] = useAtom(eventForEditAtom) + const [selectedNewEventDate, setSelectedNewEndDate] = useAtom( + selectedNewEventDateAtom + ); + const [editEvent, setEditEvent] = useAtom(eventForEditAtom); + const [deleteModalVisible, setDeleteModalVisible] = useState(false); - const {show, close, initialDate} = { - show: !!selectedNewEventDate || !!editEvent, - close: () => { - setSelectedNewEndDate(undefined) - setEditEvent(undefined) - }, - initialDate: selectedNewEventDate || editEvent?.start + const showDeleteEventModal = () => { + setDeleteModalVisible(true); + }; + + const handleDeleteEvent = () => {}; + + const hideDeleteEventModal = () => { + setDeleteModalVisible(false); + }; + + const { show, close, initialDate } = { + show: !!selectedNewEventDate || !!editEvent, + close: () => { + setSelectedNewEndDate(undefined); + setEditEvent(undefined); + }, + initialDate: selectedNewEventDate || editEvent?.start, + }; + + const detailsRef = useRef(null); + + const [title, setTitle] = useState(editEvent?.title || ""); + const [details, setDetails] = useState(editEvent?.notes || ""); + const [isAllDay, setIsAllDay] = useState(editEvent?.allDay || false); + const [isPrivate, setIsPrivate] = useState( + editEvent?.private || false + ); + const [startTime, setStartTime] = useState(() => { + const date = initialDate ?? new Date(); + date.setSeconds(0, 0); + return date; + }); + const [endTime, setEndTime] = useState(() => { + if (editEvent?.end) { + const date = new Date(editEvent.end); + date.setSeconds(0, 0); + return date; } + const date = + editEvent?.end ?? initialDate + ? addHours(editEvent?.end ?? initialDate!, 1) + : addHours(new Date(), 1); + date.setSeconds(0, 0); + return date; + }); + const [startDate, setStartDate] = useState(initialDate ?? new Date()); + const [endDate, setEndDate] = useState( + editEvent?.end ?? initialDate ?? new Date() + ); + const [selectedAttendees, setSelectedAttendees] = useState( + editEvent?.participants ?? [] + ); + const [repeatInterval, setRepeatInterval] = useState([]); - const detailsRef = useRef(null) + const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent(); + const { data: members } = useGetFamilyMembers(true); - const [title, setTitle] = useState(editEvent?.title || ""); - const [details, setDetails] = useState(editEvent?.notes || ""); - const [isAllDay, setIsAllDay] = useState(editEvent?.allDay || false); - const [isPrivate, setIsPrivate] = useState(editEvent?.private || false); - const [startTime, setStartTime] = useState(() => { - const date = initialDate ?? new Date(); + useEffect(() => { + setTitle(editEvent?.title || ""); + setDetails(editEvent?.notes || ""); + setIsAllDay(editEvent?.allDay || false); + setIsPrivate(editEvent?.private || false); + setStartTime(() => { + const date = initialDate ?? new Date(); + date.setSeconds(0, 0); + return date; + }); + setEndTime(() => { + if (editEvent?.end) { + const date = new Date(editEvent.end); date.setSeconds(0, 0); return date; + } + const date = + editEvent?.end ?? initialDate + ? addHours(editEvent?.end ?? initialDate!, 1) + : addHours(new Date(), 1); + date.setSeconds(0, 0); + return date; }); - const [endTime, setEndTime] = useState(() => { - if (editEvent?.end) { - const date = new Date(editEvent.end); - date.setSeconds(0, 0); - return date; - } - const date = (editEvent?.end ?? initialDate) - ? addHours((editEvent?.end ?? initialDate!), 1) - : addHours(new Date(), 1); - date.setSeconds(0, 0); - return date; + setStartDate(initialDate ?? new Date()); + setEndDate(editEvent?.end ?? initialDate ?? new Date()); + setSelectedAttendees(editEvent?.participants ?? []); + setRepeatInterval([]); + }, [editEvent, selectedNewEventDate]); + + if (!show) return null; + + const formatDateTime = (date?: Date | string) => { + if (!date) return undefined; + return new Date(date).toLocaleDateString("en-US", { + weekday: "long", + month: "short", + day: "numeric", }); - const [startDate, setStartDate] = useState(initialDate ?? new Date()); - const [endDate, setEndDate] = useState(editEvent?.end ?? initialDate ?? new Date()); - const [selectedAttendees, setSelectedAttendees] = useState(editEvent?.participants ?? []); - const [repeatInterval, setRepeatInterval] = useState([]); + }; - const {mutateAsync: createEvent, isLoading, isError} = useCreateEvent(); - const {data: members} = useGetFamilyMembers(true); + const combineDateAndTime = (date: Date, time: Date): Date => { + const combined = new Date(date); + combined.setHours(time.getHours()); + combined.setMinutes(time.getMinutes()); + combined.setSeconds(0); + combined.setMilliseconds(0); + return combined; + }; - useEffect(() => { - setTitle(editEvent?.title || ""); - setDetails(editEvent?.notes || ""); - setIsAllDay(editEvent?.allDay || false); - setIsPrivate(editEvent?.private || false); - setStartTime(() => { - const date = initialDate ?? new Date(); - date.setSeconds(0, 0); - return date; - }); - setEndTime(() => { - if (editEvent?.end) { - const date = new Date(editEvent.end); - date.setSeconds(0, 0); - return date; - } - const date = (editEvent?.end ?? initialDate) - ? addHours((editEvent?.end ?? initialDate!), 1) - : addHours(new Date(), 1); - date.setSeconds(0, 0); - return date; - }); - setStartDate(initialDate ?? new Date()); - setEndDate(editEvent?.end ?? initialDate ?? new Date()); - setSelectedAttendees(editEvent?.participants ?? []); - setRepeatInterval([]); - }, [editEvent, selectedNewEventDate]); + const handleSave = async () => { + let finalStartDate: Date; + let finalEndDate: Date; - if (!show) return null; - - const formatDateTime = (date?: Date | string) => { - if (!date) return undefined; - return new Date(date).toLocaleDateString("en-US", { - weekday: "long", - month: "short", - day: "numeric", - }); - }; - - const combineDateAndTime = (date: Date, time: Date): Date => { - const combined = new Date(date); - combined.setHours(time.getHours()); - combined.setMinutes(time.getMinutes()); - combined.setSeconds(0); - combined.setMilliseconds(0); - return combined; - }; - - const handleSave = async () => { - let finalStartDate: Date; - let finalEndDate: Date; - - if (isAllDay) { - finalStartDate = new Date(startDate.setHours(0, 0, 0, 0)); - finalEndDate = new Date(startDate.setHours(0, 0, 0, 0)); - } else { - finalStartDate = combineDateAndTime(startDate, startTime); - finalEndDate = combineDateAndTime(endDate, endTime); - } - - const eventData: Partial = { - title: title, - startDate: finalStartDate, - endDate: finalEndDate, - allDay: isAllDay, - attendees: selectedAttendees, - notes: details - }; - - if (editEvent?.id) eventData.id = editEvent?.id - - await createEvent(eventData); - setEditEvent(undefined) - - close(); - }; - - const getRepeatLabel = () => { - const selectedDays = repeatInterval; - const allDays = [ - "sunday", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", - ]; - const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"]; - - const isEveryWorkDay = workDays.every((day) => selectedDays.includes(day)); - - const isEveryDay = allDays.every((day) => selectedDays.includes(day)); - - if (isEveryDay) { - return "Every day"; - } else if ( - isEveryWorkDay && - !selectedDays.includes("saturday") && - !selectedDays.includes("sunday") - ) { - return "Every work day"; - } else { - return selectedDays - .map((item) => daysOfWeek.find((day) => day.value === item)?.label) - .join(", "); - } - }; - - if (isLoading && !isError) { - return ( - - - - ); + if (isAllDay) { + finalStartDate = new Date(startDate.setHours(0, 0, 0, 0)); + finalEndDate = new Date(startDate.setHours(0, 0, 0, 0)); + } else { + finalStartDate = combineDateAndTime(startDate, startTime); + finalEndDate = combineDateAndTime(endDate, endTime); } + const eventData: Partial = { + title: title, + startDate: finalStartDate, + endDate: finalEndDate, + allDay: isAllDay, + attendees: selectedAttendees, + notes: details, + }; + + if (editEvent?.id) eventData.id = editEvent?.id; + + await createEvent(eventData); + setEditEvent(undefined); + + close(); + }; + + const getRepeatLabel = () => { + const selectedDays = repeatInterval; + const allDays = [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + ]; + const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"]; + + const isEveryWorkDay = workDays.every((day) => selectedDays.includes(day)); + + const isEveryDay = allDays.every((day) => selectedDays.includes(day)); + + if (isEveryDay) { + return "Every day"; + } else if ( + isEveryWorkDay && + !selectedDays.includes("saturday") && + !selectedDays.includes("sunday") + ) { + return "Every work day"; + } else { + return selectedDays + .map((item) => daysOfWeek.find((day) => day.value === item)?.label) + .join(", "); + } + }; + + if (isLoading && !isError) { return ( - - - - - - Cancel - - - - - - Save - - - - - { - setTitle(text); - }} - placeholderTextColor="#2d2d30" - style={{fontFamily: "Manrope_500Medium", fontSize: 22}} - paddingT-15 - paddingL-30 - returnKeyType="next" - /> - - - - - - - All day - - - - setIsAllDay(value)} - /> - - - - - - { - setStartDate(date); - }} - maximumDate={endDate} - style={{ - fontFamily: "PlusJakartaSans_500Medium", - fontSize: 16, - }} - marginL-8 - /> - - { - if (time <= endTime) { - setStartTime(time) - } else { - const currentTime = new Date(); - currentTime.setSeconds(0, 0); - setStartTime(currentTime); - } - }} - minuteInterval={5} - dateTimeFormatter={(date, mode) => - date.toLocaleTimeString("en-us", { - hour: "numeric", - minute: "numeric", - }) - } - mode="time" - style={{ - fontFamily: "PlusJakartaSans_500Medium", - fontSize: 16, - }} - marginR-30 - /> - - - {!isAllDay && ( - - - - { - setEndDate(date); - }} - style={{ - fontFamily: "PlusJakartaSans_500Medium", - fontSize: 16, - }} - /> - - { - if (time >= endTime) { - setEndTime(time); - } else { - const currentTime = new Date(); - currentTime.setSeconds(0, 0); - setEndTime(currentTime); - } - }} - minimumDate={startTime} - minuteInterval={5} - dateTimeFormatter={(date, mode) => - date.toLocaleTimeString("en-us", { - hour: "numeric", - minute: "numeric", - }) - } - mode="time" - style={{ - fontFamily: "PlusJakartaSans_500Medium", - fontSize: 16, - }} - marginR-30 - /> - - )} - - - - - - - - Attendees - - - setSelectedAttendees(value as string[] ?? [])} - style={{marginLeft: "auto"}} - mode={PickerModes.MULTI} - renderInput={() => - - - - - - - - - - - - - Reminders - - - - - - - - - - - Mark as Private - - - - setIsPrivate(value)} - /> - - - - - detailsRef?.current?.focus()}> - - - - Add Details - - - - - - - - - + + + ); + } + + return ( + + + {editEvent ? ( + <> + + + + + + + + + ) : ( + + + + Cancel + + + + + + Save + + + + )} + + { + setTitle(text); + }} + placeholderTextColor="#2d2d30" + style={{ fontFamily: "Manrope_500Medium", fontSize: 22 }} + paddingT-15 + paddingL-30 + returnKeyType="next" + /> + + + + + + + All day + + + + setIsAllDay(value)} + /> + + + + + + { + setStartDate(date); + }} + //maximumDate={endDate} + style={{ + fontFamily: "PlusJakartaSans_500Medium", + fontSize: 16, + }} + marginL-8 + /> + + { + if (time <= endTime) { + setStartTime(time); + } else { + const currentTime = new Date(); + currentTime.setSeconds(0, 0); + setStartTime(currentTime); + } + }} + minuteInterval={5} + dateTimeFormatter={(date, mode) => + date.toLocaleTimeString("en-us", { + hour: "numeric", + minute: "numeric", + }) + } + mode="time" + style={{ + fontFamily: "PlusJakartaSans_500Medium", + fontSize: 16, + }} + marginR-30 + /> + + + {!isAllDay && ( + + + + { + setEndDate(date); + }} + style={{ + fontFamily: "PlusJakartaSans_500Medium", + fontSize: 16, + }} + /> + + { + if (time >= endTime) { + setEndTime(time); + } else { + const currentTime = new Date(); + currentTime.setSeconds(0, 0); + setEndTime(currentTime); + } + }} + minimumDate={startTime} + minuteInterval={5} + dateTimeFormatter={(date, mode) => + date.toLocaleTimeString("en-us", { + hour: "numeric", + minute: "numeric", + }) + } + mode="time" + style={{ + fontFamily: "PlusJakartaSans_500Medium", + fontSize: 16, + }} + marginR-30 + /> + + )} + + + + + + + + Attendees + + + + setSelectedAttendees((value as string[]) ?? []) + } + style={{ marginLeft: "auto" }} + mode={PickerModes.MULTI} + renderInput={() => ( + + + + + + + + + + + + + Reminders + + + + + + + + + + + Mark as Private + + + + setIsPrivate(value)} + /> + + + + + detailsRef?.current?.focus()}> + + + + Add Details + + + + + + + + + {editEvent && ( + + )} + + ); }; const styles = StyleSheet.create({ - divider: {height: 1, backgroundColor: "#e4e4e4", marginVertical: 15}, - gradient: { - height: "25%", - position: "absolute", - bottom: 0, - width: "100%", - }, - buttonContainer: { - position: "absolute", - bottom: 25, - width: "100%", - }, - button: { - backgroundColor: "rgb(253, 23, 117)", - paddingVertical: 20, - }, - topBtn: { - backgroundColor: "white", - color: "#05a8b6", - }, - rotateSwitch: { - marginLeft: 35, - marginBottom: 10, - marginTop: 25, - }, + divider: { height: 1, backgroundColor: "#e4e4e4", marginVertical: 15 }, + gradient: { + height: "25%", + position: "absolute", + bottom: 0, + width: "100%", + }, + buttonContainer: { + position: "absolute", + bottom: 25, + width: "100%", + }, + button: { + backgroundColor: "rgb(253, 23, 117)", + paddingVertical: 20, + }, + topBtn: { + backgroundColor: "white", + color: "#05a8b6", + }, + rotateSwitch: { + marginLeft: 35, + marginBottom: 10, + marginTop: 25, + }, }); diff --git a/components/pages/calendar/atoms.ts b/components/pages/calendar/atoms.ts index 894c2fa..6d8fed7 100644 --- a/components/pages/calendar/atoms.ts +++ b/components/pages/calendar/atoms.ts @@ -9,3 +9,4 @@ export const selectedDateAtom = atom(new Date()); export const selectedNewEventDateAtom = atom(undefined); export const settingsPageIndex = atom(0); export const userSettingsView = atom(true); +export const toDosPageIndex = atom(0); diff --git a/components/pages/grocery/AddGroceryItem.tsx b/components/pages/grocery/AddGroceryItem.tsx index 1d5e21f..5304a7a 100644 --- a/components/pages/grocery/AddGroceryItem.tsx +++ b/components/pages/grocery/AddGroceryItem.tsx @@ -14,7 +14,7 @@ const AddGroceryItem = () => { paddingH-25 style={{ position: "absolute", - bottom: 20, + bottom: 65, width: "100%", height: 60, }} diff --git a/components/pages/grocery/GroceryWrapper.tsx b/components/pages/grocery/GroceryWrapper.tsx index 86c0399..fd91bd3 100644 --- a/components/pages/grocery/GroceryWrapper.tsx +++ b/components/pages/grocery/GroceryWrapper.tsx @@ -1,38 +1,42 @@ -import {ScrollView} from "react-native"; -import {View} from "react-native-ui-lib"; -import React, {useEffect, useRef} from "react"; +import { Dimensions, ScrollView } from "react-native"; +import { View } from "react-native-ui-lib"; +import React, { useEffect, useRef } from "react"; import AddGroceryItem from "./AddGroceryItem"; import GroceryList from "./GroceryList"; -import {useGroceryContext} from "@/contexts/GroceryContext"; +import { useGroceryContext } from "@/contexts/GroceryContext"; const GroceryWrapper = () => { - const {isAddingGrocery} = useGroceryContext(); - const scrollViewRef = useRef(null); // Reference to the ScrollView + const { isAddingGrocery } = useGroceryContext(); + const scrollViewRef = useRef(null); // Reference to the ScrollView - useEffect(() => { - if (isAddingGrocery && scrollViewRef.current) { - scrollViewRef.current.scrollTo({ - y: 400, // Adjust this value to scroll a bit down (100 is an example) - animated: true, - }); - } - }, [isAddingGrocery]); + useEffect(() => { + if (isAddingGrocery && scrollViewRef.current) { + scrollViewRef.current.scrollTo({ + y: 400, // Adjust this value to scroll a bit down (100 is an example) + animated: true, + }); + } + }, [isAddingGrocery]); - return ( - - - - - - - - {!isAddingGrocery && } + return ( + + + + + + - - ); + + + + {!isAddingGrocery && } + + ); }; export default GroceryWrapper; diff --git a/components/pages/settings/CalendarSettingsPage.tsx b/components/pages/settings/CalendarSettingsPage.tsx index 87fd675..809d877 100644 --- a/components/pages/settings/CalendarSettingsPage.tsx +++ b/components/pages/settings/CalendarSettingsPage.tsx @@ -1,7 +1,7 @@ import { AntDesign, Ionicons } from "@expo/vector-icons"; import React, { useCallback, useEffect, useState } from "react"; import { Button, Checkbox, Text, View } from "react-native-ui-lib"; -import { ActivityIndicator, ScrollView, StyleSheet } from "react-native"; +import { ActivityIndicator, Alert, ScrollView, StyleSheet } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; import { useAuthContext } from "@/contexts/AuthContext"; import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData"; @@ -21,6 +21,7 @@ import ExpoLocalization from "expo-localization/src/ExpoLocalization"; import { colorMap } from "@/constants/colorMap"; import { useAtom } from "jotai"; import { settingsPageIndex } from "../calendar/atoms"; +import CalendarSettingsDialog from "./calendar_components/CalendarSettingsDialog"; const googleConfig = { androidClientId: @@ -61,6 +62,29 @@ const CalendarSettingsPage = () => { ? "Mondays" : "Sundays" ); + const [isModalVisible, setModalVisible] = useState(false); + const [selectedService, setSelectedService] = useState< + "google" | "outlook" | "apple" + >("google"); + const [selectedEmail, setSelectedEmail] = useState(""); + + const showConfirmationDialog = ( + serviceName: "google" | "outlook" | "apple", + email: string + ) => { + setSelectedService(serviceName); + setSelectedEmail(email); + setModalVisible(true); + }; + + const handleConfirm = () => { + clearToken(selectedService, selectedEmail); + setModalVisible(false); + }; + + const handleCancel = () => { + setModalVisible(false); + }; const [selectedColor, setSelectedColor] = useState( profileData?.eventColor ?? colorMap.pink @@ -477,7 +501,9 @@ const CalendarSettingsPage = () => { googleToken && ( ); }; diff --git a/components/pages/settings/calendar_components/CalendarSettingsDialog.tsx b/components/pages/settings/calendar_components/CalendarSettingsDialog.tsx new file mode 100644 index 0000000..12eea93 --- /dev/null +++ b/components/pages/settings/calendar_components/CalendarSettingsDialog.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import { Dialog, Button, Text, View } from "react-native-ui-lib"; +import { StyleSheet } from "react-native"; + +interface ConfirmationDialogProps { + visible: boolean; + serviceName: "google" | "outlook" | "apple"; + email: string; + onDismiss: () => void; + onConfirm: () => void; +} + +const CalendarSettingsDialog: React.FC = ({ + visible, + serviceName, + email, + onDismiss, + onConfirm, +}) => { + return ( + + + Disconnect {serviceName} + + + + Are you sure you want to disconnect this {"\n"} + + {serviceName} + {" "} + calendar + {"\n"} + for {email}? + + + + + + + + ); +}; + +// Empty stylesheet for future styles +const styles = StyleSheet.create({ + confirmBtn: { + backgroundColor: "#ea156d", + }, + cancelBtn: { + backgroundColor: "white", + }, + dialog: { + backgroundColor: "white", + paddingHorizontal: 25, + paddingTop: 35, + paddingBottom: 17, + borderRadius: 20, + }, + title: { + fontFamily: "Manrope_600SemiBold", + fontSize: 22, + marginBottom: 20, + }, + text: { + fontFamily: "PlusJakartaSans_400Regular", + fontSize: 16, + marginBottom: 25, + }, +}); + +export default CalendarSettingsDialog; diff --git a/components/pages/todos/ToDoItem.tsx b/components/pages/todos/ToDoItem.tsx index 2207c77..7bc1446 100644 --- a/components/pages/todos/ToDoItem.tsx +++ b/components/pages/todos/ToDoItem.tsx @@ -12,11 +12,11 @@ import { useToDosContext } from "@/contexts/ToDosContext"; import { Ionicons } from "@expo/vector-icons"; import PointsSlider from "@/components/shared/PointsSlider"; import { IToDo } from "@/hooks/firebase/types/todoData"; -import { ImageBackground } from "react-native"; +import { ImageBackground, StyleSheet } from "react-native"; import AddChoreDialog from "@/components/pages/todos/AddChoreDialog"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import RepeatIcon from "@/assets/svgs/RepeatIcon"; -import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; +import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { const { updateToDo } = useToDosContext(); @@ -48,9 +48,9 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { let isTodoEditable; if (isParent) { - isTodoEditable = true + isTodoEditable = true; } else { - isTodoEditable = props.item.creatorId === profileData?.uid; + isTodoEditable = props.item.creatorId === profileData?.uid; } return ( @@ -81,20 +81,16 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { fontSize: 15, }} onPress={() => { - isTodoEditable ? setVisible(true) : null + isTodoEditable ? setVisible(true) : null; }} > {props.item.title} { updateToDo({ id: props.item.id, done: !props.item.done }); @@ -160,7 +156,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { })()} - )} + )} {selectedMembers?.map((member) => { @@ -258,3 +254,17 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { }; export default ToDoItem; + +const styles = StyleSheet.create({ + checkbox: { + borderRadius: 50, + borderWidth: 0.7, + color: "#bfbfbf", + borderColor: "#bfbfbf", + width: 24.64, + aspectRatio: 1, + }, + checked: { + borderRadius: 50, + }, +}); diff --git a/components/pages/todos/ToDosPage.tsx b/components/pages/todos/ToDosPage.tsx index 6ceb916..b8ab4c6 100644 --- a/components/pages/todos/ToDosPage.tsx +++ b/components/pages/todos/ToDosPage.tsx @@ -1,81 +1,90 @@ -import {Button, Text, View} from "react-native-ui-lib"; -import React, {useState} from "react"; +import { Button, Text, View } from "react-native-ui-lib"; +import React, { useState } from "react"; import HeaderTemplate from "@/components/shared/HeaderTemplate"; import AddChore from "./AddChore"; import ProgressCard from "./ProgressCard"; import ToDosList from "./ToDosList"; -import {Dimensions, ScrollView, StyleSheet} from "react-native"; -import {TouchableOpacity} from "react-native-gesture-handler"; -import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; +import { Dimensions, ScrollView, StyleSheet } from "react-native"; +import { TouchableOpacity } from "react-native-gesture-handler"; +import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import FamilyChoresProgress from "./family-chores/FamilyChoresProgress"; import UserChoresProgress from "./user-chores/UserChoresProgress"; +import { useAtom } from "jotai"; +import { toDosPageIndex } from "../calendar/atoms"; const ToDosPage = () => { - const [pageIndex, setPageIndex] = useState(0); - const {profileData} = useAuthContext(); - const {width, height} = Dimensions.get("screen"); - const pageLink = ( - setPageIndex(1)}> - - View family progress - - - ); - return ( - <> - - {pageIndex == 0 && ( - - setPageIndex(1)}> + + View family progress + + + ); + return ( + <> + + + {pageIndex == 0 && ( + + + + {profileData?.userType == ProfileType.CHILD && ( + + setPageIndex(2)} > - - - {profileData?.userType == ProfileType.CHILD && ( - - setPageIndex(2)} - > - - View your full progress report here - - - } - /> - - )} - - - - + + View your full progress report here + + + } + /> + )} - {pageIndex == 1 && } - {pageIndex == 2 && } + + - - - ) - ; + )} + {pageIndex == 1 && ( + + )} + {pageIndex == 2 && } + + + + + ); }; const styles = StyleSheet.create({ - linkBtn: { - backgroundColor: "transparent", - padding: 0, - }, + linkBtn: { + backgroundColor: "transparent", + padding: 0, + }, }); export default ToDosPage;