├── .eslintrc.json ├── .gitignore ├── README.md ├── components ├── Footer.js ├── Header.js ├── Layout.js └── Todo.js ├── context └── AuthContext.js ├── firebase.js ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── _document.js ├── api │ └── hello.js ├── index.js ├── login.js └── userDashboard.js ├── postcss.config.js ├── public └── images │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── dog.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ └── site.webmanifest ├── styles ├── Home.module.css └── globals.css └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /components/Footer.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | 4 | const Footer = () => { 5 | return ( 6 |
7 | 11 | {" "} 12 | 13 | 14 | 15 | 16 | 17 | {" "} 18 | 19 |
20 | ); 21 | }; 22 | 23 | export default Footer; 24 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React, { useState } from "react"; 3 | import { useAuth } from "../context/AuthContext"; 4 | 5 | const Header = () => { 6 | const { currentUser } = useAuth(); 7 | return ( 8 |
9 |
10 | 11 |
12 | 13 | RTTDA 14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default Header; 25 | -------------------------------------------------------------------------------- /components/Layout.js: -------------------------------------------------------------------------------- 1 | import Footer from "./Footer"; 2 | import Header from "./Header"; 3 | 4 | export default function Layout(props) { 5 | const { children } = props; 6 | return ( 7 | <> 8 |
9 |
10 |
{children}
11 |
13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/Todo.js: -------------------------------------------------------------------------------- 1 | import { deleteDoc, doc, serverTimestamp, setDoc } from "firebase/firestore"; 2 | import React, { useRef, useState } from "react"; 3 | import { db } from "../firebase"; 4 | 5 | const Todo = ({ todo, id }) => { 6 | const [edit, setEdit] = useState(false); 7 | const [edittedTodo, setEdditedTodo] = useState(todo); 8 | const inputRef = useRef(null); 9 | 10 | function setWritingCursor() { 11 | setEdit(true); 12 | // ? setting the writing cursor to the last word of the input tag 13 | setTimeout(() => { 14 | if (inputRef.current) { 15 | inputRef.current.focus(); 16 | inputRef.current.selectionStart = inputRef.current.value.length; 17 | inputRef.current.selectionEnd = inputRef.current.value.length; 18 | } 19 | }, 0); // add a delay of 1 second 20 | } 21 | 22 | async function editTodo() { 23 | // ? logic to make changes to the firestore db 24 | await setDoc( 25 | doc(db, "todos", id), 26 | { 27 | task: edittedTodo, 28 | }, 29 | { merge: true } 30 | ); 31 | setEdit(false); 32 | } 33 | 34 | async function deleteTodo() { 35 | try { 36 | await deleteDoc(doc(db, "todos", id)); 37 | } catch (error) { 38 | console("error while deleting: ", error); 39 | } 40 | } 41 | 42 | return ( 43 | <> 44 |
45 | {edit ? ( 46 | setEdditedTodo(e.target.value)} 54 | /> 55 | ) : ( 56 | 62 | {todo} 63 | 64 | )} 65 | 66 | {/* tools div */} 67 |
68 | {edit ? ( 69 | 73 | ) : ( 74 | 78 | )}{" "} 79 | 83 |
84 |
85 | 86 | ); 87 | }; 88 | 89 | export default Todo; 90 | -------------------------------------------------------------------------------- /context/AuthContext.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect, useRef } from "react"; 2 | import { auth, db } from "../firebase"; 3 | import { 4 | signInWithEmailAndPassword, 5 | createUserWithEmailAndPassword, 6 | signOut, 7 | onAuthStateChanged, 8 | } from "firebase/auth"; 9 | import { doc, getDoc } from "firebase/firestore"; 10 | 11 | const AuthContext = React.createContext(); 12 | 13 | export function useAuth() { 14 | return useContext(AuthContext); 15 | } 16 | 17 | export function AuthProvider({ children }) { 18 | const [currentUser, setCurrentUser] = useState(null); 19 | const [loading, setLoading] = useState(true); 20 | const userInfo = useRef(); 21 | 22 | async function signUp(email, password) { 23 | await createUserWithEmailAndPassword(auth, email, password); 24 | return; 25 | } 26 | async function login(email, password) { 27 | await signInWithEmailAndPassword(auth, email, password); 28 | return; 29 | } 30 | async function logout() { 31 | await signOut(auth); 32 | return; 33 | } 34 | 35 | useEffect(() => { 36 | const unsubscribe = onAuthStateChanged(auth, async (user) => { 37 | setCurrentUser(user); 38 | setLoading(false); 39 | }); 40 | return unsubscribe; // * this is the cleanup function of the useeffect 41 | }, []); 42 | 43 | const value = { 44 | currentUser, 45 | login, 46 | signUp, 47 | logout, 48 | userInfo, 49 | }; 50 | 51 | return ( 52 | 53 | {/* Below code means that return the children iff the loading state is false */} 54 | {!loading && children} 55 | 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /firebase.js: -------------------------------------------------------------------------------- 1 | import { initializeApp } from "firebase/app"; 2 | import { getAuth } from "firebase/auth"; 3 | import { getFirestore } from "firebase/firestore"; 4 | 5 | const firebaseConfig = { 6 | apiKey: process.env.NEXT_PUBLIC_APIKEY, 7 | authDomain: process.env.NEXT_PUBLIC_AUTHDOMAIN, 8 | projectId: process.env.NEXT_PUBLIC_PROJECTID, 9 | storageBucket: process.env.NEXT_PUBLIC_STORAGEBUCKET, 10 | messagingSenderId: process.env.NEXT_PUBLIC_MESSAGINGSENDERID, 11 | appId: process.env.NEXT_PUBLIC_APPID, 12 | }; 13 | 14 | const app = initializeApp(firebaseConfig); 15 | export const auth = getAuth(app); 16 | export const db = getFirestore(app); 17 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | eslint: { 6 | ignoreDuringBuilds: true, 7 | }, 8 | }; 9 | 10 | module.exports = nextConfig; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rttda", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "firebase": "^9.17.1", 13 | "next": "13.1.6", 14 | "react": "18.2.0", 15 | "react-beautiful-dnd": "^13.1.1", 16 | "react-dom": "18.2.0", 17 | "react-icons": "^4.7.1" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^10.4.13", 21 | "eslint": "8.34.0", 22 | "eslint-config-next": "13.1.6", 23 | "postcss": "^8.4.21", 24 | "tailwind-scrollbar": "^2.1.0", 25 | "tailwindcss": "^3.2.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import Layout from "../components/Layout"; 2 | import { AuthProvider } from "../context/AuthContext"; 3 | import "../styles/globals.css"; 4 | 5 | function MyApp({ Component, pageProps }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default MyApp; 16 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | {/* GOOGLE FONTS MONSERRAT FAMILY IMPORT */} 8 | 9 | 10 | 14 | {/* FAVICON *** Not working right now*/} 15 | 20 | 26 | 32 | 33 | {/* FONT AWESOME ICONS CDN IMPORT */} 34 | 41 | 42 | 43 |
44 | 45 |
46 |
47 | 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { useEffect, useState } from "react"; 3 | import Todo from "../components/Todo"; 4 | import { db } from "../firebase"; 5 | import { 6 | collection, 7 | addDoc, 8 | serverTimestamp, 9 | onSnapshot, 10 | query, 11 | where, 12 | orderBy, 13 | } from "firebase/firestore"; 14 | import { useAuth } from "../context/AuthContext"; 15 | import { useRouter } from "next/router"; 16 | import { AiOutlineLoading3Quarters } from "react-icons/ai"; 17 | import { VscLoading } from "react-icons/vsc"; 18 | import thinkDog from "../public/images/dog.png"; 19 | import Image from "next/image"; 20 | 21 | export default function Home() { 22 | const [todo, setTodo] = useState(""); 23 | const [todos, setTodos] = useState([]); 24 | const { currentUser } = useAuth(); 25 | const [loading, setLoading] = useState(false); 26 | const router = useRouter(); 27 | const [todoLoading, setTodoLoading] = useState(false); 28 | 29 | async function handleToDoAdd() { 30 | setLoading(true); 31 | if (!todo) { 32 | return; 33 | } 34 | try { 35 | await addDoc(collection(db, "todos"), { 36 | task: todo, 37 | uid: currentUser.uid, 38 | timestamp: serverTimestamp(), 39 | }); 40 | setLoading(false); 41 | } catch (error) { 42 | console.log("error in adding doc: ", error); 43 | } 44 | } 45 | 46 | { 47 | currentUser && 48 | useEffect(() => { 49 | setTodoLoading(true); 50 | const unsubscribe = onSnapshot( 51 | query( 52 | collection(db, "todos"), 53 | where("uid", "==", currentUser?.uid), 54 | orderBy("timestamp", "desc") 55 | ), 56 | (snapshot) => { 57 | setTodos(snapshot.docs); 58 | setTodoLoading(false); 59 | } 60 | ); 61 | return unsubscribe; // ? clean up function 62 | }, [db]); 63 | } 64 | 65 | // ! chat GPT code ---> helped a lot really(firebase indexing was causing issues) 66 | // useEffect(() => { 67 | // const unsubscribe = onSnapshot(collection(db, "todos"), (snapshot) => { 68 | // console.log(snapshot.docs); 69 | // setTodos( 70 | // snapshot.docs 71 | // .filter((doc) => doc.data().uid === currentUser.uid) 72 | // .sort((a, b) => b.data().timestamp - a.data().timestamp) 73 | // ); 74 | // }); 75 | // return unsubscribe; // ? clean up function 76 | // }, [db, currentUser.uid]); 77 | 78 | return ( 79 | <> 80 | 81 | RTTDA 82 | 83 | 84 | 85 |
86 | {/* BELOW IS ADD TODO DIV */} 87 |
88 | setTodo(e.target.value)} 94 | /> 95 | 109 |
110 |
117 | {/* TODOS DIV */} 118 | {todoLoading ? ( 119 |
120 | 121 |
122 | ) : todos.length > 0 ? ( 123 | todos.map((item) => { 124 | return ( 125 | 126 | ); 127 | }) 128 | ) : ( 129 |
130 | thinking dog pic 131 | No Todos Yet 132 |
133 | )} 134 |
135 |
136 | 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /pages/login.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { useRouter } from "next/router"; 3 | import React, { useState } from "react"; 4 | import { useAuth } from "../context/AuthContext"; 5 | 6 | const Login = () => { 7 | const [email, setEmail] = useState(""); 8 | const [password, setPassword] = useState(""); 9 | const [error, setError] = useState(null); 10 | const [isLoggingIn, setIsLoggingIn] = useState(true); 11 | const { login, signUp } = useAuth(); // ? using firebase login functions via the auth context 12 | const router = useRouter(); 13 | 14 | async function handleSubmit(e) { 15 | e.preventDefault(); 16 | if (!email || !password) { 17 | setError( 18 | !email && !password 19 | ? "Please enter email & password" 20 | : !email 21 | ? "Please enter email" 22 | : "Please enter password" 23 | ); 24 | return; 25 | } 26 | if (isLoggingIn) { 27 | try { 28 | await login(email, password); 29 | setEmail(""); 30 | setPassword(""); 31 | router.push("/"); 32 | } catch (error) { 33 | // ? the user will only reach this state if he or she is filling the wrong credentials 34 | setError("Incorrect email or password"); 35 | } 36 | return; 37 | } 38 | // ? if user is not logging in it means that he is registering for the first time so he will signUp 39 | try { 40 | await signUp(email, password); 41 | setEmail(""); 42 | setPassword(""); 43 | router.push("/"); 44 | } catch (error) { 45 | if (error.code == "auth/weak-password") { 46 | setError("Password length should be atleast 6"); 47 | } 48 | } 49 | return; 50 | } 51 | return ( 52 | <> 53 | 54 | RTTDA Login 55 | 56 |
57 |
58 | 59 | 60 |
61 | 62 | {isLoggingIn ? "Login" : "Sign Up"} 63 | 64 | {error && ( 65 |
66 | {error} 67 |
68 | )} 69 |
70 | {/* LOGIN WITH CREDENTIALS DIV */} 71 |
75 | { 81 | setError(null); 82 | setEmail(e.target.value); 83 | }} 84 | /> 85 | { 91 | setError(null); 92 | setPassword(e.target.value); 93 | }} 94 | /> 95 | 101 |
102 | {/* LOGIN WITH GOOGLE DIV */} 103 | {/*
104 | OR 105 | 108 |
*/} 109 | setIsLoggingIn(!isLoggingIn)} 111 | className="active:scale-105 duration-200 transition transform ease-in-out" 112 | > 113 | {isLoggingIn ? "Sign Up" : "Login"} 114 | 115 |
116 |
117 | 118 | ); 119 | }; 120 | 121 | export default Login; 122 | -------------------------------------------------------------------------------- /pages/userDashboard.js: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import { useRouter } from "next/router"; 3 | import React from "react"; 4 | import { useAuth } from "../context/AuthContext"; 5 | 6 | const UserDashboard = () => { 7 | const router = useRouter(); 8 | const { currentUser, logout } = useAuth(); 9 | const email = currentUser?.email; // ? did this so as to remove flicker of the email when the user logs out 10 | return ( 11 | <> 12 | 13 | RTTDA User Profile 14 | 15 |
16 | 17 | Signed in as:  {email} 18 | 19 | 29 |
30 | 31 | ); 32 | }; 33 | 34 | export default UserDashboard; 35 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/images/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/images/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/apple-touch-icon.png -------------------------------------------------------------------------------- /public/images/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/dog.png -------------------------------------------------------------------------------- /public/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/public/images/favicon.ico -------------------------------------------------------------------------------- /public/images/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-ansh-007/RTTDA/43ca6d5d01e9c99c6b8d4c465aa05fb81b5bbc81/styles/Home.module.css -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | font-family: "Montserrat", sans-serif; 7 | color: #a5a5a5; 8 | } 9 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx}", 5 | "./pages/**/*.{js,ts,jsx,tsx}", 6 | "./components/**/*.{js,ts,jsx,tsx}", 7 | 8 | // Or if using `src` directory: 9 | "./src/**/*.{js,ts,jsx,tsx}", 10 | ], 11 | theme: { 12 | extend: {}, 13 | }, 14 | plugins: [require("tailwind-scrollbar")], 15 | }; 16 | --------------------------------------------------------------------------------