diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index 2edaa4e..dceffc1 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -7,7 +7,7 @@ import { DrawerItemList, } from "@react-navigation/drawer"; import { Button, View, Text, ButtonSize } from "react-native-ui-lib"; -import { ImageBackground, StyleSheet } from "react-native"; +import { Dimensions, ImageBackground, StyleSheet } from "react-native"; import Feather from "@expo/vector-icons/Feather"; import DrawerButton from "@/components/shared/DrawerButton"; import { @@ -30,6 +30,7 @@ import { toDosPageIndex, userSettingsView, } from "@/components/pages/calendar/atoms"; +import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon"; export default function TabLayout() { const { mutateAsync: signOut } = useSignOut(); @@ -52,8 +53,8 @@ export default function TabLayout() { }} drawerContent={(props) => { return ( - - + + } /> + { + props.navigation.navigate("feedback"); + setPageIndex(0); + setToDosIndex(0); + setUserView(true); + setIsFamilyView(false); + }} + icon={} + /> {/* + ); } diff --git a/app/(auth)/feedback/_layout.tsx b/app/(auth)/feedback/_layout.tsx new file mode 100644 index 0000000..0990eb8 --- /dev/null +++ b/app/(auth)/feedback/_layout.tsx @@ -0,0 +1,5 @@ +import {Stack} from "expo-router"; + +export default function StackLayout () { + return +} \ No newline at end of file diff --git a/app/(auth)/feedback/index.tsx b/app/(auth)/feedback/index.tsx new file mode 100644 index 0000000..feb51f7 --- /dev/null +++ b/app/(auth)/feedback/index.tsx @@ -0,0 +1,13 @@ +import FeedbackPage from "@/components/pages/feedback/FeedbackPage"; +import { FeedbackProvider } from "@/contexts/FeedbackContext"; +import { View } from "react-native-ui-lib"; + +export default function Screen() { + return ( + + + + + + ); +} diff --git a/assets/svgs/FeedbackNavIcon.tsx b/assets/svgs/FeedbackNavIcon.tsx new file mode 100644 index 0000000..0f4d67b --- /dev/null +++ b/assets/svgs/FeedbackNavIcon.tsx @@ -0,0 +1,20 @@ +import * as React from "react" +import Svg, { SvgProps, Path } from "react-native-svg" +const FeedbackNavIcon = (props: SvgProps) => ( + + + +) +export default FeedbackNavIcon diff --git a/components/pages/feedback/AddFeedback.tsx b/components/pages/feedback/AddFeedback.tsx new file mode 100644 index 0000000..9b2c816 --- /dev/null +++ b/components/pages/feedback/AddFeedback.tsx @@ -0,0 +1,157 @@ +import { + Button, + Dialog, + TextField, + TextFieldRef, + TouchableOpacity, + View, +} from "react-native-ui-lib"; +import React, { useEffect, useRef, useState } from "react"; +import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView"; +import { Dimensions, Platform, StyleSheet } from "react-native"; + +import DropModalIcon from "@/assets/svgs/DropModalIcon"; +import { useBrainDumpContext } from "@/contexts/DumpContext"; +import KeyboardManager from "react-native-keyboard-manager"; +import { useFeedbackContext } from "@/contexts/FeedbackContext"; + +interface IAddFeedbackProps { + isVisible: boolean; + setIsVisible: (value: boolean) => void; +} + +const AddFeedback = ({ + addFeedbackProps, +}: { + addFeedbackProps: IAddFeedbackProps; +}) => { + const { addFeedback } = useFeedbackContext(); + const [feedback, setFeedback] = useState(""); + const [feedbackTitle, setFeedbackTitle] = useState(""); + const { width } = Dimensions.get("screen"); + + const descriptionRef = useRef(null); + const titleRef = useRef(null); + + useEffect(() => { + setFeedback(""); + }, [addFeedbackProps.isVisible]); + + useEffect(() => { + if (addFeedbackProps.isVisible) { + setTimeout(() => { + titleRef?.current?.focus(); + }, 500); + } + }, [addFeedbackProps.isVisible]); + + useEffect(() => { + if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(false); + + setFeedbackTitle(""); + setFeedback(""); + }, []); + + return ( + addFeedbackProps.setIsVisible(false)} + containerStyle={styles.dialogContainer} + visible={addFeedbackProps.isVisible} + > + + + ); +}; + +const styles = StyleSheet.create({ + dialogContainer: { + borderTopRightRadius: 15, + borderTopLeftRadius: 15, + backgroundColor: "white", + padding: 0, + paddingTop: 3, + margin: 0, + }, + topBtns: {}, + topBtn: { + backgroundColor: "white", + color: "#05a8b6", + }, + title: { + fontSize: 22, + fontFamily: "Manrope_500Medium", + }, + description: { + fontFamily: "Manrope_400Regular", + fontSize: 14, + textAlignVertical: "top", + }, +}); + +export default AddFeedback; diff --git a/components/pages/feedback/EditFeedback.tsx b/components/pages/feedback/EditFeedback.tsx new file mode 100644 index 0000000..385f659 --- /dev/null +++ b/components/pages/feedback/EditFeedback.tsx @@ -0,0 +1,196 @@ +import React, { useEffect, useRef, useState } from "react"; +import { + Button, + Dialog, + View, + Text, + TextField, + TouchableOpacity, + TextFieldRef, +} from "react-native-ui-lib"; +import { Dimensions, StyleSheet } from "react-native"; +import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView"; +import PenIcon from "@/assets/svgs/PenIcon"; +import BinIcon from "@/assets/svgs/BinIcon"; +import DropModalIcon from "@/assets/svgs/DropModalIcon"; +import CloseXIcon from "@/assets/svgs/CloseXIcon"; +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 { IFeedback, useFeedbackContext } from "@/contexts/FeedbackContext"; +import FeedbackDialog from "./FeedbackDialog"; + +const EditFeedback = (props: { + item: IFeedback; + isVisible: boolean; + setIsVisible: (value: boolean) => void; +}) => { + const { updateFeedback, deleteFeedback } = useFeedbackContext(); + const [text, setText] = useState(props.item.text); + const [modalVisible, setModalVisible] = useState(false); + const textRef = useRef(null); + + const { width } = Dimensions.get("screen"); + + useEffect(() => { + setText(props.item.text); + }, []); + + useEffect(() => { + if (props.isVisible) { + setTimeout(() => { + textRef?.current?.focus(); + }, 500); + } + }, [props.isVisible]); + + const showConfirmationDialog = () => { + setModalVisible(true); + }; + + const handleDeleteNote = () => { + deleteFeedback(props.item.id); + }; + + const hideConfirmationDialog = () => { + setModalVisible(false); + }; + + return ( + props.setIsVisible(false)} + containerStyle={{ + borderRadius: 15, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + backgroundColor: "white", + width: "100%", + alignSelf: "stretch", + padding: 10, + paddingTop: 3, + margin: 0, + }} + visible={props.isVisible} + > + + props.setIsVisible(false)}> + + + + + + ); +}; + +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", + marginTop: -3, + }, + rotateSwitch: { + marginLeft: 35, + marginBottom: 10, + marginTop: 25, + }, + optionsReg: { + fontSize: 16, + fontFamily: "PlusJakartaSans_400Regular", + }, + optionsBold: { + fontSize: 16, + fontFamily: "PlusJakartaSans_600SemiBold", + }, + optionsIcon: { + marginRight: 10, + }, + title: { + fontSize: 22, + fontFamily: "Manrope_500Medium", + }, + description: { + fontFamily: "Manrope_400Regular", + fontSize: 14, + }, +}); + +export default EditFeedback; diff --git a/components/pages/feedback/Feedback.tsx b/components/pages/feedback/Feedback.tsx new file mode 100644 index 0000000..49df7b8 --- /dev/null +++ b/components/pages/feedback/Feedback.tsx @@ -0,0 +1,50 @@ +import { View, Text } from "react-native-ui-lib"; +import React, { useState } from "react"; +import { + TouchableWithoutFeedback, +} from "react-native-gesture-handler"; +import { IFeedback } from "@/contexts/FeedbackContext"; +import EditFeedback from "./EditFeedback"; + +const Feedback = (props: { item: IFeedback }) => { + const [isVisible, setIsVisible] = useState(false); + + return ( + + setIsVisible(true)}> + + + {props.item.title} + + + {props.item.text} + + + + + + ); +}; + +export default Feedback; diff --git a/components/pages/feedback/FeedbackDialog.tsx b/components/pages/feedback/FeedbackDialog.tsx new file mode 100644 index 0000000..421c395 --- /dev/null +++ b/components/pages/feedback/FeedbackDialog.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 FeedbackDialogProps { + visible: boolean; + title: string; + onDismiss: () => void; + onConfirm: () => void; +} + +const FeedbackDialog: React.FC = ({ + visible, + title, + onDismiss, + onConfirm, +}) => { + return ( + + + Delete Note + + + + Are you sure you want to delete this feedback? {"\n\n"} + + {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 FeedbackDialog; diff --git a/components/pages/feedback/FeedbackList.tsx b/components/pages/feedback/FeedbackList.tsx new file mode 100644 index 0000000..c5b5bdf --- /dev/null +++ b/components/pages/feedback/FeedbackList.tsx @@ -0,0 +1,35 @@ +import { View } from "react-native-ui-lib"; +import React from "react"; +import { FlatList } from "react-native"; +import { useFeedbackContext } from "@/contexts/FeedbackContext"; +import Feedback from "./Feedback"; + +const FeedbackList = (props: { searchText: string }) => { + const { feedbacks } = useFeedbackContext(); + + const filteredBrainDumps = + props.searchText.trim() === "" + ? feedbacks + : feedbacks.filter( + (item) => + item.title.toLowerCase().includes(props.searchText.toLowerCase()) || + item.text + .toLowerCase() + .includes(props.searchText.toLowerCase()) + ); + + return ( + + item.title} + renderItem={({ item }) => ( + + )} + /> + + ); +}; + +export default FeedbackList; diff --git a/components/pages/feedback/FeedbackPage.tsx b/components/pages/feedback/FeedbackPage.tsx new file mode 100644 index 0000000..464a91f --- /dev/null +++ b/components/pages/feedback/FeedbackPage.tsx @@ -0,0 +1,120 @@ +import {Dimensions, ScrollView, StyleSheet} from "react-native"; +import React, {useState} from "react"; +import {Button, Text, TextField, View} from "react-native-ui-lib"; +import HeaderTemplate from "@/components/shared/HeaderTemplate"; +import {Feather, MaterialIcons} from "@expo/vector-icons"; +import LinearGradient from "react-native-linear-gradient"; +import PlusIcon from "@/assets/svgs/PlusIcon"; +import AddFeedback from "./AddFeedback"; +import FeedbackList from "./FeedbackList"; + +const FeedbackPage = () => { + const [searchText, setSearchText] = useState(""); + const [isAddVisible, setIsAddVisible] = useState(false); + + return ( + + + + + + Drop your feedback here, and{"\n"}organize it later. + + } + /> + + + { + setSearchText(value); + }} + leadingAccessory={ + + } + style={{ + fontFamily: "Manrope_500Medium", + fontSize: 15, + }} + placeholder="Search your feedbacks..." + /> + + + + + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + searchField: { + borderWidth: 0.7, + borderColor: "#9b9b9b", + borderRadius: 15, + height: 42, + paddingLeft: 10, + marginVertical: 20, + }, +}); + +export default FeedbackPage; diff --git a/components/pages/main/SignInPage.tsx b/components/pages/main/SignInPage.tsx index b3b0718..8f926b2 100644 --- a/components/pages/main/SignInPage.tsx +++ b/components/pages/main/SignInPage.tsx @@ -1,183 +1,193 @@ import { - Button, - ButtonSize, - Colors, - KeyboardAwareScrollView, - LoaderScreen, - Text, - TextField, - TextFieldRef, - View, + Button, + ButtonSize, + Colors, + KeyboardAwareScrollView, + LoaderScreen, + Text, + TextField, + TextFieldRef, + View, } from "react-native-ui-lib"; -import React, {useRef, useState} from "react"; -import {useSignIn} from "@/hooks/firebase/useSignIn"; -import {KeyboardAvoidingView, Platform, StyleSheet} from "react-native"; +import React, { useRef, useState } from "react"; +import { useSignIn } from "@/hooks/firebase/useSignIn"; +import { KeyboardAvoidingView, Platform, StyleSheet } from "react-native"; import Toast from "react-native-toast-message"; import KeyboardManager from "react-native-keyboard-manager"; -import {SafeAreaView} from "react-native-safe-area-context"; -import {useRouter} from "expo-router"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { useRouter } from "expo-router"; -KeyboardManager.setEnableAutoToolbar(true); +if (Platform.OS === "ios") KeyboardManager.setEnableAutoToolbar(true); const SignInPage = () => { - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const passwordRef = useRef(null); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const passwordRef = useRef(null); - const {mutateAsync: signIn, error, isError, isLoading} = useSignIn(); + const { mutateAsync: signIn, error, isError, isLoading } = useSignIn(); - const router = useRouter() + const router = useRouter(); - const handleSignIn = async () => { - await signIn({email, password}); - if (!isError) { - Toast.show({ - type: "success", - text1: "Login successful!", - }); - } else { - Toast.show({ - type: "error", - text1: "Error logging in", - text2: `${error}`, - }); - } - }; + const handleSignIn = async () => { + await signIn({ email, password }); + if (!isError) { + Toast.show({ + type: "success", + text1: "Login successful!", + }); + } else { + Toast.show({ + type: "error", + text1: "Error logging in", + text2: `${error}`, + }); + } + }; - return ( - - - - - - Jump back into Cally - - - Please enter your details. - - + return ( + + + + + + Jump back into Cally + + + Please enter your details. + + - - { - // Move focus to the description field - passwordRef.current?.focus(); - }} - /> - - + + { + // Move focus to the description field + passwordRef.current?.focus(); + }} + /> + + - + -