├── styles ├── globals.css └── globalsCSS.css ├── .env.example ├── public ├── plus.png ├── logo500.png ├── logonew.png ├── logo__1_-removebg-preview.png └── log.svg ├── images └── image1.png ├── postcss.config.js ├── .eslintrc.json ├── next.config.js ├── pages ├── api │ ├── checkusername │ │ └── [username].js │ ├── getUserInfo.js │ ├── todo │ │ ├── createone.js │ │ ├── fetchall.js │ │ └── deleteone │ │ │ └── [id].js │ └── auth │ │ ├── check │ │ └── [username].js │ │ ├── login.js │ │ └── createuser.js ├── _app.js ├── index.js ├── Login.js └── Signup.js ├── tailwind.config.js ├── .vscode └── tasks.json ├── mongoconnect.js ├── .gitignore ├── context ├── detail.js └── data.js ├── models ├── notes.js └── user.js ├── package.json ├── lib └── redisConnect.js ├── README.md └── components ├── Messages.js └── SendMessage.js /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | MONGODB_URI=__database_uri__ 2 | JWT_INFO=__JWT_INFO__ 3 | REDIS_URI=__REDIS_URI__ -------------------------------------------------------------------------------- /public/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/innovatorved/LetsDo-with-TODO/HEAD/public/plus.png -------------------------------------------------------------------------------- /images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/innovatorved/LetsDo-with-TODO/HEAD/images/image1.png -------------------------------------------------------------------------------- /public/logo500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/innovatorved/LetsDo-with-TODO/HEAD/public/logo500.png -------------------------------------------------------------------------------- /public/logonew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/innovatorved/LetsDo-with-TODO/HEAD/public/logonew.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "@next/next/no-img-element": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/logo__1_-removebg-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/innovatorved/LetsDo-with-TODO/HEAD/public/logo__1_-removebg-preview.png -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /pages/api/checkusername/[username].js: -------------------------------------------------------------------------------- 1 | 2 | const {nodeRedisGet} = require('../../../lib/redisConnect') 3 | 4 | export default async function handler(req, res) { 5 | 6 | return res.status(400).json({success : false}); 7 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./pages/**/*.{js,ts,jsx,tsx}", 4 | "./components/**/*.{js,ts,jsx,tsx}", 5 | ], 6 | theme: { 7 | extend: {}, 8 | }, 9 | plugins: [], 10 | } 11 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globalsCSS.css' 2 | import States from '../context/data'; 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return ( 6 | 7 | 8 | 9 | ) 10 | } 11 | 12 | export default MyApp; 13 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Run the Nextjs Application in Dev mode", 6 | "type": "shell", 7 | "command": "npm run dev", 8 | "group": "none", 9 | "presentation": { 10 | "reveal": "always", 11 | "panel": "new" 12 | }, 13 | "runOptions": { "runOn": "folderOpen" } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /pages/api/getUserInfo.js: -------------------------------------------------------------------------------- 1 | const { fetchUserDetails } = require("../../context/detail"); 2 | 3 | export default async function handler(req, res) { 4 | const token = req.headers.authtoken; 5 | const data = await fetchUserDetails(token); 6 | 7 | if (data.success){ 8 | return res.status(200).json({ 9 | success: true, 10 | data : data.details 11 | }); 12 | } 13 | return res.status(400).json({success : false}); 14 | } -------------------------------------------------------------------------------- /mongoconnect.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const mongoURI = process.env.MONGODB_URI; 3 | 4 | const connection = {}; 5 | 6 | const connectToMongo = async() => { 7 | if (connection.isConnected){ 8 | return ; 9 | } 10 | 11 | const db = await mongoose.connect(mongoURI, { 12 | useNewUrlParser: true, 13 | useUnifiedTopology: true 14 | }) 15 | 16 | connection.isConnected = db.connections[0].readyState; 17 | } 18 | 19 | module.exports = connectToMongo; -------------------------------------------------------------------------------- /.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 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | -------------------------------------------------------------------------------- /context/detail.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const JWT_KEY = process.env.JWT_INFO; 3 | const connectToMongo = require('../mongoconnect'); 4 | 5 | exports.fetchUserDetails = async function (token){ 6 | if (!token) { 7 | return ({success : false , details : null}); 8 | } 9 | await connectToMongo(); 10 | try { 11 | const userDetail = await jwt.verify(token , JWT_KEY); 12 | return ({success : true, details : userDetail.user}); 13 | } catch (error) { 14 | return ({success : false , details : null}); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /models/notes.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { Schema } = mongoose; 3 | 4 | const NotesSchema = new Schema({ 5 | user : { 6 | type : mongoose.Schema.Types.ObjectId, 7 | ref : "notes" 8 | }, 9 | main : { 10 | type: String, 11 | required: true 12 | }, 13 | flag : { 14 | type : Boolean, 15 | default: false 16 | }, 17 | timestamp : { 18 | type: Date, 19 | default: Date.now 20 | 21 | } 22 | }); 23 | 24 | module.exports = mongoose.models.notes || mongoose.model('notes' , NotesSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo", 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 | "bcryptjs": "^2.4.3", 13 | "jsonwebtoken": "^8.5.1", 14 | "mongoose": "^6.2.7", 15 | "next": "12.1.0", 16 | "react": "17.0.2", 17 | "react-dom": "17.0.2", 18 | "redis": "^4.0.4" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^10.4.4", 22 | "eslint": "8.10.0", 23 | "eslint-config-next": "12.1.0", 24 | "postcss": "^8.4.12", 25 | "tailwindcss": "^3.0.23" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const { Schema } = mongoose; 3 | 4 | const UserSchema = new Schema({ 5 | name : { 6 | type: String, 7 | required: true 8 | }, 9 | username : { 10 | type: String, 11 | required: true, 12 | unique: true 13 | }, 14 | email : { 15 | type: String, 16 | required: true, 17 | unique: true 18 | }, 19 | password : { 20 | type: String, 21 | required: true 22 | }, 23 | date : { 24 | type: Date, 25 | default: Date.now 26 | 27 | } 28 | }); 29 | module.exports = mongoose.models.user || mongoose.model('user' , UserSchema); -------------------------------------------------------------------------------- /pages/api/todo/createone.js: -------------------------------------------------------------------------------- 1 | const { fetchUserDetails } = require("../../../context/detail"); 2 | const notes = require("../../../models/notes"); 3 | 4 | export default async function handler(req, res) { 5 | const { msg } = req.body; 6 | const token = req.headers.authtoken; 7 | 8 | const data = await fetchUserDetails(token); 9 | if (!data.success || msg == ""){ 10 | return res.status(401).json({ 11 | success: false, 12 | message: "Unauthorized" 13 | }); 14 | } 15 | const userId = data.details.id; 16 | const note = new notes({main : msg , user : userId}); 17 | const savenote = await note.save(); 18 | return res.status(200).json({success : true , savenote}); 19 | } -------------------------------------------------------------------------------- /lib/redisConnect.js: -------------------------------------------------------------------------------- 1 | const { createClient } = require('redis'); 2 | 3 | async function nodeRedisSet ( username , msg) { 4 | try { 5 | const client = createClient({ 6 | url : process.env.REDIS_URI 7 | }); 8 | await client.connect(); 9 | const val = await client.set(username , msg); 10 | await client.quit(); 11 | return val; 12 | } catch (error) { 13 | return false; 14 | } 15 | } 16 | 17 | async function nodeRedisGet ( username ) { 18 | try { 19 | const client = createClient({ 20 | url : process.env.REDIS_URI 21 | }); 22 | await client.connect(); 23 | const val = await client.get(username); 24 | await client.quit(); 25 | return val; 26 | } catch (error) { 27 | return false; 28 | } 29 | } 30 | 31 | export { 32 | nodeRedisSet , 33 | nodeRedisGet 34 | }; -------------------------------------------------------------------------------- /pages/api/todo/fetchall.js: -------------------------------------------------------------------------------- 1 | const { fetchUserDetails } = require("../../../context/detail"); 2 | const notes = require("../../../models/notes"); 3 | 4 | export default async function handler(req, res) { 5 | if (req.method !== 'GET') { return res.status(500).json({ success : false, error: "Internal Server try after some time : method Problem" }); } 6 | 7 | const token = req.headers.authtoken; 8 | const data = await fetchUserDetails(token); 9 | if (!data.success){ 10 | return res.status(401).json({ 11 | success: false, 12 | message: "Unauthorized" 13 | }); 14 | } 15 | 16 | try { 17 | const userId = data.details.id; 18 | const note = await notes.find({user : userId} , {user : 0 , __v : 0}); 19 | return res.status(200).json({success:true , note}); 20 | } catch (error) { 21 | return res.status(400).json({success:false}); 22 | } 23 | } -------------------------------------------------------------------------------- /pages/api/auth/check/[username].js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcryptjs'); 2 | const jwt = require('jsonwebtoken'); 3 | 4 | const user = require('../../../../models/user'); 5 | 6 | const connectToMongo = require('../../../../mongoconnect'); 7 | const JWT_key = process.env.JWT_INFO; 8 | 9 | export default async function handler(req, res) { 10 | let success = false; 11 | const { username } = req.query; 12 | 13 | if (req.method !== 'PUT') { return res.status(500).json({ success, error: "Internal Server try after some time : method Problem" }); } 14 | 15 | try { 16 | await connectToMongo(); 17 | const UserDetails = await user.findOne({ username }); 18 | 19 | success = true; 20 | if (UserDetails) { 21 | return res.json({ success, "res": true }); 22 | } 23 | else { 24 | return res.json({ success, "res": false }); 25 | } 26 | 27 | } catch (error) { 28 | success = false; 29 | return res.status(500).json({ success, error: "Some Error" }); 30 | } 31 | } -------------------------------------------------------------------------------- /pages/api/todo/deleteone/[id].js: -------------------------------------------------------------------------------- 1 | const { fetchUserDetails } = require("../../../../context/detail"); 2 | const notes = require("../../../../models/notes"); 3 | 4 | export default async function handler(req, res) { 5 | if (req.method !== 'DELETE') { return res.status(500).json({ success: false, error: "Internal Server try after some time : method Problem" }); } 6 | try { 7 | const token = req.headers.authtoken; 8 | const { id } = req.query; 9 | const data = await fetchUserDetails(token); 10 | if (!data.success) { 11 | return res.status(401).json({ 12 | success: false, 13 | message: "Unauthorized" 14 | }); 15 | } 16 | const noteID = id.toString(); 17 | const userId = data.details.id; 18 | const delNote = await notes.findOneAndDelete({ _id: noteID, user: userId }); 19 | return res.status(200).json({ success:true , msg : delNote }); 20 | } catch (error) { 21 | return res.status(200).json({ success:false , msg : "note deleted" }); 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | todologo 3 |

4 | 5 | # LetsDo-with-TODO 6 | 7 | A todo application built with Next.js and MongoDB provides a simple and efficient way to manage tasks and stay organized. 8 | 9 | ## Demo 10 | 11 | ![lets-do-with-todo.vercel.app/Signup](lets-do-with-todo.vercel.app/Signup) 12 | 13 | ## Deployment 14 | 15 | To deploy this project run 16 | 17 | ### Clone the Repo 18 | 19 | ```bash 20 | git clone https://github.com/innovatorved/LetsDo-with-TODO.git 21 | ``` 22 | 23 | ### Install Dependencies and Run 24 | 25 | ```bash 26 | cd LetsDo-with-TODO 27 | npm install 28 | npm run dev 29 | ``` 30 | 31 | ## Tech Stack 32 | 33 | **Client:** Nextjs , React, react-router-dom 34 | 35 | **Server:** bcryptjs ,express , express-validator , jsonwebtoken ,mongoose 36 | 37 | **Database:** MongoDB 38 | 39 | ## Screenshots 40 | 41 | ![todoapp](https://bbpuefvzkslxkhvduevs.supabase.co/storage/v1/object/public/images/todo-image1.png) 42 | 43 | 44 | ## License 45 | 46 | [MIT](https://choosealicense.com/licenses/mit/) 47 | 48 | ## Authors 49 | 50 | - [Ved Gupta](https://www.github.com/innovatorved) 51 | 52 | ## 🚀 About Me 53 | 54 | I'm a Developer i will feel the code then write . 55 | 56 | ## Support 57 | 58 | For support, email vedgupta@protonmail.com 59 | -------------------------------------------------------------------------------- /public/log.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /components/Messages.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { StateManager } from '../context/data'; 3 | 4 | export default function Messages() { 5 | const { data, changeState } = useContext(StateManager); 6 | return ( 7 |
8 |
9 | { 10 | data.map((item, index) => { 11 | return ( 12 |
13 |
14 | { 15 | item.state ? 16 | {item.msg} 17 | : 18 | 19 | } 20 |
21 | changeState(index) 23 | } readOnly/> 24 |
25 | ) 26 | }) 27 | } 28 |
29 |
30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /pages/api/auth/login.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcryptjs'); 2 | const jwt = require('jsonwebtoken'); 3 | const user = require('../../../models/user'); 4 | 5 | const connectToMongo = require('../../../mongoconnect'); 6 | const JWT_key = process.env.JWT_INFO; 7 | 8 | export default async function handler(req, res) { 9 | let success = false; 10 | if (req.method !== 'POST') { return res.status(500).json({ success, error: "Internal Server try after some time : method Problem" }); } 11 | 12 | try { 13 | await connectToMongo(); 14 | const {email , password} = req.body; 15 | const userDetails = await user.findOne({ email }); 16 | 17 | if (!userDetails) { 18 | success = false; 19 | return res.status(400).json({ success, error: "EmailId not Registered" }); 20 | } 21 | 22 | const passwordCompare = await bcrypt.compare(password, userDetails.password); 23 | if (!passwordCompare) { 24 | success = false; 25 | return res.status(400).json({ success, error: "Please try to login with Correct Credentials" }); 26 | } 27 | const data = { 28 | user: { 29 | id: userDetails.id, 30 | name : userDetails.name, 31 | username : userDetails.username, 32 | date : Date.now(), 33 | } 34 | }; 35 | 36 | const authtoken = jwt.sign(data, JWT_key); 37 | success = true; 38 | return res.json({ success, authtoken , userDetails}); 39 | 40 | } catch (error) { 41 | success = false; 42 | return res.status(500).json({ success, error: "Internal Server try after some time" }); 43 | } 44 | } -------------------------------------------------------------------------------- /pages/api/auth/createuser.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcryptjs'); 2 | const jwt = require('jsonwebtoken'); 3 | const user = require('../../../models/user'); 4 | 5 | const {nodeRedisSet} = require('../../../lib/redisConnect'); 6 | 7 | const connectToMongo = require('../../../mongoconnect'); 8 | const JWT_key = process.env.JWT_INFO; 9 | 10 | export default async function handler(req, res) { 11 | let success = false; 12 | if (req.method !== 'POST') {return res.status(500).json({success , error :"Internal Server try after some time : method Problem"});} 13 | 14 | try { 15 | await connectToMongo(); 16 | const userEmail = await user.findOne({ email: req.body.email }); 17 | const userusername = await user.findOne({ username: req.body.username }); 18 | 19 | if (userEmail) { 20 | success = false; 21 | return res.status(400).json({ success, error: "user with samee email id already Registered" }); 22 | } 23 | if (userusername) { 24 | success = false; 25 | return res.status(400).json({ success, error: "user with samee username already Registered" }); 26 | } 27 | 28 | const salt = await bcrypt.genSaltSync(10); 29 | const secPass = await bcrypt.hashSync(req.body.password, salt); 30 | 31 | const createuser = await user.create({ 32 | name: req.body.name, 33 | username: req.body.username, 34 | email: req.body.email, 35 | password: secPass, 36 | }); 37 | 38 | const data = { 39 | user: { 40 | id: createuser.id, 41 | name : createuser.name, 42 | username : createuser.username, 43 | date : Date.now(), 44 | } 45 | }; 46 | 47 | const authtoken = jwt.sign(data, JWT_key); 48 | success = true; 49 | nodeRedisSet(createuser.username , authtoken); 50 | return res.status(200).json({ success, authtoken }); 51 | } catch (error) { 52 | success = false; 53 | return res.status(500).json({success , error :"Internal Server try after some time"}); 54 | } 55 | } -------------------------------------------------------------------------------- /context/data.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState, useEffect } from 'react'; 2 | 3 | // Createcontext 4 | const StateManager = createContext(); 5 | 6 | 7 | const States = (props) => { 8 | 9 | const host = ''; 10 | 11 | const [userInfo, setUserInfo] = useState({ 12 | name: '', 13 | username: '' 14 | }); 15 | 16 | 17 | const list = []; 18 | 19 | const [data, setData] = useState(list); 20 | 21 | const LogOut = () => { 22 | localStorage.removeItem('token'); 23 | localStorage.removeItem('name'); 24 | localStorage.removeItem('username'); 25 | setUserInfo({ 26 | name: '', 27 | username: '' 28 | }); 29 | setData([]); 30 | }; 31 | 32 | const AddData = (note) => { 33 | const add = { 34 | msg: note.msg, 35 | timestamp: note.time, 36 | state: false 37 | }; 38 | setData(data => [add, ...data]); 39 | fetch(host + "/api/todo/createone", { 40 | method: "POST", 41 | headers: { 42 | "Content-Type": "application/json", 43 | authtoken: localStorage.getItem("token"), 44 | }, 45 | body: JSON.stringify(add), 46 | }) 47 | .then((res) => res.json()) 48 | .then((data) => { 49 | if (data.success) { 50 | console.log(data.success); 51 | } 52 | }) 53 | .catch((err) => console.log(err)); 54 | } 55 | 56 | const changeState = (index) => { 57 | const exactIndex = index; 58 | const newArray = [...data]; 59 | let ele = newArray[exactIndex] 60 | localStorage.setItem(ele.id, !ele.state); 61 | newArray[exactIndex] = { ...ele, state: !ele.state } 62 | setData(newArray); 63 | } 64 | 65 | 66 | return ( 67 | 68 | {props.children} 69 | 70 | ) 71 | } 72 | 73 | export default States; 74 | export { 75 | StateManager 76 | }; -------------------------------------------------------------------------------- /components/SendMessage.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import Image from 'next/image'; 3 | 4 | import { StateManager } from '../context/data'; 5 | import Router from 'next/router'; 6 | 7 | export default function SendMessage() { 8 | const { AddData , userInfo , LogOut} = useContext(StateManager); 9 | 10 | const [Plus, setPlus] = useState({ 11 | height: 27, 12 | weigth: 27 13 | }); 14 | 15 | const messageSubmitted = (e) => { 16 | e.preventDefault(); 17 | const line = e.target.elements.message.value; 18 | if (line.trim() !== "" && line.trim() !== null) { 19 | const data2 = { 20 | name: userInfo.name, 21 | msg: line, 22 | time: new Date().toLocaleTimeString() 23 | } 24 | e.target.elements.message.value = ''; 25 | AddData( 26 | data2 27 | ); 28 | } 29 | } 30 | 31 | return ( 32 |
33 |
34 |
35 |
36 | Hi , {userInfo.name} 37 |
38 |
{ 39 | LogOut(); 40 | Router.push('/Login'); 41 | }}> 42 | logo svg 43 |
44 |
45 |
46 |
47 | 50 |
51 | 59 |
60 |
61 |
62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import SendMessage from '../components/SendMessage'; 3 | import Messages from '../components/Messages'; 4 | import { useEffect, useContext } from 'react'; 5 | import Router from 'next/router'; 6 | import { StateManager } from '../context/data'; 7 | 8 | export default function Home() { 9 | const { setUserInfo, LogOut, host, data, setData } = useContext(StateManager); 10 | 11 | useEffect(() => { 12 | if (!localStorage.getItem('token')) { 13 | LogOut(); 14 | Router.push('/Login'); 15 | } 16 | }) 17 | 18 | useEffect(async() => { 19 | if (data.length === 0) { 20 | fetch(host + "/api/todo/fetchall", { 21 | method: "GET", 22 | headers: { 23 | "Content-Type": "application/json", 24 | authtoken: localStorage.getItem("token"), 25 | }, 26 | }) 27 | .then((res) => res.json()) 28 | .then((dataNotes) => { 29 | const notes = []; 30 | if (dataNotes.success) { 31 | dataNotes.note.map((note) => { 32 | const state = localStorage.getItem(note._id); 33 | const add = { 34 | id : note._id, 35 | msg: note.main, 36 | timestamp: note.timestamp, 37 | state: state===null?false:state==="true"?true:false 38 | }; 39 | notes.push(add); 40 | }); 41 | // reverse notes 42 | notes.reverse(); 43 | setData(notes); 44 | } 45 | }) 46 | .catch((err) => { 47 | console.log(err); 48 | }); 49 | } 50 | }, []); 51 | 52 | 53 | 54 | useEffect(async () => { 55 | if (localStorage.getItem('name') === null) { 56 | const tok = localStorage.getItem("token"); 57 | const response = await fetch(host + '/api/getUserInfo', { 58 | method: 'GET', 59 | headers: { 60 | 'Content-Type': 'application/json', 61 | 'authtoken': tok 62 | } 63 | }); 64 | const data = await response.json(); 65 | if (data.success) { 66 | setUserInfo(data.data); 67 | localStorage.setItem('name', data.data.name); 68 | localStorage.setItem('username', data.data.username); 69 | } 70 | } 71 | else { 72 | setUserInfo({ 73 | name: localStorage.getItem("name"), 74 | username: localStorage.getItem("username") 75 | }) 76 | } 77 | }, []) 78 | 79 | return ( 80 |
81 | 82 | ToDo 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
91 | Contkt Logo 98 | 99 | 100 |
101 |
102 | ) 103 | } 104 | -------------------------------------------------------------------------------- /pages/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from 'react'; 2 | import Router from 'next/router'; 3 | import { StateManager } from '../context/data'; 4 | import Head from 'next/head'; 5 | 6 | 7 | export default function Login() { 8 | const { host } = useContext(StateManager); 9 | useEffect(() => { 10 | if (localStorage.getItem('token')) { 11 | Router.push('/'); 12 | } 13 | }) 14 | const [data, setData] = useState({ 15 | email: "", 16 | password: "" 17 | }) 18 | 19 | const OnChangeData = (e) => { 20 | setData({ 21 | ...data, 22 | [e.target.name]: e.target.value 23 | }) 24 | } 25 | 26 | const LoginSubitted = (e) => { 27 | e.preventDefault(); 28 | fetch(host + '/api/auth/login', { 29 | method: 'POST', 30 | headers: { 31 | 'Content-Type': 'application/json' 32 | }, 33 | body: JSON.stringify(data) 34 | }) 35 | .then(res => res.json()) 36 | .then(res => { 37 | if (res.success) { 38 | localStorage.setItem('token', res.authtoken); 39 | Router.push('/'); 40 | } else { 41 | alert(res.error); 42 | } 43 | }) 44 | } 45 | 46 | return ( 47 | <> 48 | 49 | Login to TODO 50 | 51 | 52 |
53 |
54 |
55 |
56 | Workflow 57 |

Sign in to your TODO

58 |
59 | 60 |
61 | 62 |
63 |
64 | 65 | 69 | 70 |
71 | 72 |

We take privacy issues seriously. You can be sure that your personal data is securely protected.

73 |
74 | 79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 | 87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 |
95 |
96 | 97 | 98 |
99 |
100 | 101 |
102 | 110 |
111 |
112 |
113 |
114 |
115 | 116 | ) 117 | } 118 | -------------------------------------------------------------------------------- /pages/Signup.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from 'react'; 2 | import Router from 'next/router'; 3 | import { StateManager } from '../context/data'; 4 | import Head from 'next/head'; 5 | import Link from 'next/link'; 6 | 7 | 8 | export default function Signup() { 9 | const { host } = useContext(StateManager); 10 | const [data, setData] = useState({ 11 | firstname: '', 12 | lastname: '', 13 | username: '', 14 | email: "" 15 | }) 16 | const [state , setState] = useState(false); 17 | 18 | const [pass, setPass] = useState({ 19 | password: "", 20 | confirmPassword: "" 21 | }) 22 | 23 | useEffect(() => { 24 | if (localStorage.getItem('token')) { 25 | Router.push('/'); 26 | } 27 | }) 28 | 29 | const OnChangeData = (e) => { 30 | setData({ 31 | ...data, 32 | [e.target.name]: e.target.value 33 | }) 34 | } 35 | const ChangePass = (e) => { 36 | setPass({ 37 | ...pass, 38 | [e.target.name]: e.target.value 39 | }) 40 | }; 41 | 42 | const CreateNewUser = (e) => { 43 | e.preventDefault(); 44 | const user = { 45 | name: data.firstname + " " + data.lastname, 46 | username: data.username, 47 | email: data.email, 48 | password: pass.password 49 | } 50 | fetch(host + '/api/auth/createuser', { 51 | method: 'POST', 52 | headers: { 53 | 'Content-Type': 'application/json' 54 | }, 55 | body: JSON.stringify(user) 56 | }) 57 | .then(res => res.json()) 58 | .then(newUser => { 59 | if (newUser.success) { 60 | localStorage.setItem('token', newUser.authtoken); 61 | Router.push('/Login'); 62 | } 63 | else { 64 | alert(newUser.error); 65 | } 66 | } 67 | ) 68 | } 69 | 70 | const runthis= async()=>{ 71 | const username = data.username; 72 | const url = `${host}/api/auth/check/${username}`; 73 | 74 | if(username.length >= 5){ 75 | const response = await fetch(url , { 76 | method : 'PUT', 77 | headers : { 78 | "Content-Type" : "application/json" 79 | } 80 | }); 81 | const jsonRes = await response.json(); 82 | if (jsonRes.res === true){ 83 | setState("yes"); 84 | } 85 | else if (jsonRes.res === false){ 86 | setState("no"); 87 | } 88 | } 89 | 90 | 91 | }; 92 | 93 | 94 | 95 | return ( 96 | <> 97 | 98 | Create Acoount on TODO 99 | 100 | 101 |
102 |
103 |
104 |
105 |
106 | Workflow 107 |

Create Account in TODO

108 |
109 |
110 | 111 |
112 |
113 | 114 | 118 | 119 |
120 | 121 |

We take privacy issues seriously. You can be sure that your personal data is securely protected.

122 |
123 | 128 |
129 |
130 |
131 |
132 |
133 |

Personal Information

134 |
135 |

Information about the section could go here and a brief description of how this might be used.

136 |
137 |
138 |
139 |
140 | 141 | 142 |
143 |
144 | 145 | 146 |
147 |
148 |
149 |
150 | 151 | 152 |
153 |
154 | 155 | 156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |

Security

164 |
165 |

Information about the section could go here and a brief description of how this might be used.

166 |
167 |
168 |
169 |
170 | 171 | 172 |
173 |
174 | 175 | 176 |
177 |
178 |
179 |
180 |
181 |
182 | 183 | 189 | 190 |
191 |
192 |
193 |
194 | 201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | 211 | ) 212 | } -------------------------------------------------------------------------------- /styles/globalsCSS.css: -------------------------------------------------------------------------------- 1 | /* 2 | ! tailwindcss v3.0.23 | MIT License | https://tailwindcss.com 3 | */ 4 | 5 | /* 6 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 7 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 8 | */ 9 | 10 | *, 11 | ::before, 12 | ::after { 13 | box-sizing: border-box; 14 | /* 1 */ 15 | border-width: 0; 16 | /* 2 */ 17 | border-style: solid; 18 | /* 2 */ 19 | border-color: #e5e7eb; 20 | /* 2 */ 21 | } 22 | 23 | ::before, 24 | ::after { 25 | --tw-content: ''; 26 | } 27 | 28 | /* 29 | 1. Use a consistent sensible line-height in all browsers. 30 | 2. Prevent adjustments of font size after orientation changes in iOS. 31 | 3. Use a more readable tab size. 32 | 4. Use the user's configured `sans` font-family by default. 33 | */ 34 | 35 | html { 36 | line-height: 1.5; 37 | /* 1 */ 38 | -webkit-text-size-adjust: 100%; 39 | /* 2 */ 40 | -moz-tab-size: 4; 41 | /* 3 */ 42 | -o-tab-size: 4; 43 | tab-size: 4; 44 | /* 3 */ 45 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 46 | /* 4 */ 47 | } 48 | 49 | /* 50 | 1. Remove the margin in all browsers. 51 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 52 | */ 53 | 54 | body { 55 | margin: 0; 56 | /* 1 */ 57 | line-height: inherit; 58 | /* 2 */ 59 | } 60 | 61 | /* 62 | 1. Add the correct height in Firefox. 63 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 64 | 3. Ensure horizontal rules are visible by default. 65 | */ 66 | 67 | hr { 68 | height: 0; 69 | /* 1 */ 70 | color: inherit; 71 | /* 2 */ 72 | border-top-width: 1px; 73 | /* 3 */ 74 | } 75 | 76 | /* 77 | Add the correct text decoration in Chrome, Edge, and Safari. 78 | */ 79 | 80 | abbr:where([title]) { 81 | -webkit-text-decoration: underline dotted; 82 | text-decoration: underline dotted; 83 | } 84 | 85 | /* 86 | Remove the default font size and weight for headings. 87 | */ 88 | 89 | h1, 90 | h2, 91 | h3, 92 | h4, 93 | h5, 94 | h6 { 95 | font-size: inherit; 96 | font-weight: inherit; 97 | } 98 | 99 | /* 100 | Reset links to optimize for opt-in styling instead of opt-out. 101 | */ 102 | 103 | a { 104 | color: inherit; 105 | text-decoration: inherit; 106 | } 107 | 108 | /* 109 | Add the correct font weight in Edge and Safari. 110 | */ 111 | 112 | b, 113 | strong { 114 | font-weight: bolder; 115 | } 116 | 117 | /* 118 | 1. Use the user's configured `mono` font family by default. 119 | 2. Correct the odd `em` font sizing in all browsers. 120 | */ 121 | 122 | code, 123 | kbd, 124 | samp, 125 | pre { 126 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 127 | /* 1 */ 128 | font-size: 1em; 129 | /* 2 */ 130 | } 131 | 132 | /* 133 | Add the correct font size in all browsers. 134 | */ 135 | 136 | small { 137 | font-size: 80%; 138 | } 139 | 140 | /* 141 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 142 | */ 143 | 144 | sub, 145 | sup { 146 | font-size: 75%; 147 | line-height: 0; 148 | position: relative; 149 | vertical-align: baseline; 150 | } 151 | 152 | sub { 153 | bottom: -0.25em; 154 | } 155 | 156 | sup { 157 | top: -0.5em; 158 | } 159 | 160 | /* 161 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 162 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 163 | 3. Remove gaps between table borders by default. 164 | */ 165 | 166 | table { 167 | text-indent: 0; 168 | /* 1 */ 169 | border-color: inherit; 170 | /* 2 */ 171 | border-collapse: collapse; 172 | /* 3 */ 173 | } 174 | 175 | /* 176 | 1. Change the font styles in all browsers. 177 | 2. Remove the margin in Firefox and Safari. 178 | 3. Remove default padding in all browsers. 179 | */ 180 | 181 | button, 182 | input, 183 | optgroup, 184 | select, 185 | textarea { 186 | font-family: inherit; 187 | /* 1 */ 188 | font-size: 100%; 189 | /* 1 */ 190 | line-height: inherit; 191 | /* 1 */ 192 | color: inherit; 193 | /* 1 */ 194 | margin: 0; 195 | /* 2 */ 196 | padding: 0; 197 | /* 3 */ 198 | } 199 | 200 | /* 201 | Remove the inheritance of text transform in Edge and Firefox. 202 | */ 203 | 204 | button, 205 | select { 206 | text-transform: none; 207 | } 208 | 209 | /* 210 | 1. Correct the inability to style clickable types in iOS and Safari. 211 | 2. Remove default button styles. 212 | */ 213 | 214 | button, 215 | [type='button'], 216 | [type='reset'], 217 | [type='submit'] { 218 | -webkit-appearance: button; 219 | /* 1 */ 220 | background-color: transparent; 221 | /* 2 */ 222 | background-image: none; 223 | /* 2 */ 224 | } 225 | 226 | /* 227 | Use the modern Firefox focus style for all focusable elements. 228 | */ 229 | 230 | :-moz-focusring { 231 | outline: auto; 232 | } 233 | 234 | /* 235 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 236 | */ 237 | 238 | :-moz-ui-invalid { 239 | box-shadow: none; 240 | } 241 | 242 | /* 243 | Add the correct vertical alignment in Chrome and Firefox. 244 | */ 245 | 246 | progress { 247 | vertical-align: baseline; 248 | } 249 | 250 | /* 251 | Correct the cursor style of increment and decrement buttons in Safari. 252 | */ 253 | 254 | ::-webkit-inner-spin-button, 255 | ::-webkit-outer-spin-button { 256 | height: auto; 257 | } 258 | 259 | /* 260 | 1. Correct the odd appearance in Chrome and Safari. 261 | 2. Correct the outline style in Safari. 262 | */ 263 | 264 | [type='search'] { 265 | -webkit-appearance: textfield; 266 | /* 1 */ 267 | outline-offset: -2px; 268 | /* 2 */ 269 | } 270 | 271 | /* 272 | Remove the inner padding in Chrome and Safari on macOS. 273 | */ 274 | 275 | ::-webkit-search-decoration { 276 | -webkit-appearance: none; 277 | } 278 | 279 | /* 280 | 1. Correct the inability to style clickable types in iOS and Safari. 281 | 2. Change font properties to `inherit` in Safari. 282 | */ 283 | 284 | ::-webkit-file-upload-button { 285 | -webkit-appearance: button; 286 | /* 1 */ 287 | font: inherit; 288 | /* 2 */ 289 | } 290 | 291 | /* 292 | Add the correct display in Chrome and Safari. 293 | */ 294 | 295 | summary { 296 | display: list-item; 297 | } 298 | 299 | /* 300 | Removes the default spacing and border for appropriate elements. 301 | */ 302 | 303 | blockquote, 304 | dl, 305 | dd, 306 | h1, 307 | h2, 308 | h3, 309 | h4, 310 | h5, 311 | h6, 312 | hr, 313 | figure, 314 | p, 315 | pre { 316 | margin: 0; 317 | } 318 | 319 | fieldset { 320 | margin: 0; 321 | padding: 0; 322 | } 323 | 324 | legend { 325 | padding: 0; 326 | } 327 | 328 | ol, 329 | ul, 330 | menu { 331 | list-style: none; 332 | margin: 0; 333 | padding: 0; 334 | } 335 | 336 | /* 337 | Prevent resizing textareas horizontally by default. 338 | */ 339 | 340 | textarea { 341 | resize: vertical; 342 | } 343 | 344 | /* 345 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 346 | 2. Set the default placeholder color to the user's configured gray 400 color. 347 | */ 348 | 349 | input::-moz-placeholder, textarea::-moz-placeholder { 350 | opacity: 1; 351 | /* 1 */ 352 | color: #9ca3af; 353 | /* 2 */ 354 | } 355 | 356 | input:-ms-input-placeholder, textarea:-ms-input-placeholder { 357 | opacity: 1; 358 | /* 1 */ 359 | color: #9ca3af; 360 | /* 2 */ 361 | } 362 | 363 | input::placeholder, 364 | textarea::placeholder { 365 | opacity: 1; 366 | /* 1 */ 367 | color: #9ca3af; 368 | /* 2 */ 369 | } 370 | 371 | /* 372 | Set the default cursor for buttons. 373 | */ 374 | 375 | button, 376 | [role="button"] { 377 | cursor: pointer; 378 | } 379 | 380 | /* 381 | Make sure disabled buttons don't get the pointer cursor. 382 | */ 383 | 384 | :disabled { 385 | cursor: default; 386 | } 387 | 388 | /* 389 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 390 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 391 | This can trigger a poorly considered lint error in some tools but is included by design. 392 | */ 393 | 394 | img, 395 | svg, 396 | video, 397 | canvas, 398 | audio, 399 | iframe, 400 | embed, 401 | object { 402 | display: block; 403 | /* 1 */ 404 | vertical-align: middle; 405 | /* 2 */ 406 | } 407 | 408 | /* 409 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 410 | */ 411 | 412 | img, 413 | video { 414 | max-width: 100%; 415 | height: auto; 416 | } 417 | 418 | /* 419 | Ensure the default browser behavior of the `hidden` attribute. 420 | */ 421 | 422 | [hidden] { 423 | display: none; 424 | } 425 | 426 | *, ::before, ::after { 427 | --tw-translate-x: 0; 428 | --tw-translate-y: 0; 429 | --tw-rotate: 0; 430 | --tw-skew-x: 0; 431 | --tw-skew-y: 0; 432 | --tw-scale-x: 1; 433 | --tw-scale-y: 1; 434 | --tw-pan-x: ; 435 | --tw-pan-y: ; 436 | --tw-pinch-zoom: ; 437 | --tw-scroll-snap-strictness: proximity; 438 | --tw-ordinal: ; 439 | --tw-slashed-zero: ; 440 | --tw-numeric-figure: ; 441 | --tw-numeric-spacing: ; 442 | --tw-numeric-fraction: ; 443 | --tw-ring-inset: ; 444 | --tw-ring-offset-width: 0px; 445 | --tw-ring-offset-color: #fff; 446 | --tw-ring-color: rgb(59 130 246 / 0.5); 447 | --tw-ring-offset-shadow: 0 0 #0000; 448 | --tw-ring-shadow: 0 0 #0000; 449 | --tw-shadow: 0 0 #0000; 450 | --tw-shadow-colored: 0 0 #0000; 451 | --tw-blur: ; 452 | --tw-brightness: ; 453 | --tw-contrast: ; 454 | --tw-grayscale: ; 455 | --tw-hue-rotate: ; 456 | --tw-invert: ; 457 | --tw-saturate: ; 458 | --tw-sepia: ; 459 | --tw-drop-shadow: ; 460 | --tw-backdrop-blur: ; 461 | --tw-backdrop-brightness: ; 462 | --tw-backdrop-contrast: ; 463 | --tw-backdrop-grayscale: ; 464 | --tw-backdrop-hue-rotate: ; 465 | --tw-backdrop-invert: ; 466 | --tw-backdrop-opacity: ; 467 | --tw-backdrop-saturate: ; 468 | --tw-backdrop-sepia: ; 469 | } 470 | 471 | .sr-only { 472 | position: absolute; 473 | width: 1px; 474 | height: 1px; 475 | padding: 0; 476 | margin: -1px; 477 | overflow: hidden; 478 | clip: rect(0, 0, 0, 0); 479 | white-space: nowrap; 480 | border-width: 0; 481 | } 482 | 483 | .absolute { 484 | position: absolute; 485 | } 486 | 487 | .relative { 488 | position: relative; 489 | } 490 | 491 | .inset-y-0 { 492 | top: 0px; 493 | bottom: 0px; 494 | } 495 | 496 | .left-0 { 497 | left: 0px; 498 | } 499 | 500 | .m-auto { 501 | margin: auto; 502 | } 503 | 504 | .mx-auto { 505 | margin-left: auto; 506 | margin-right: auto; 507 | } 508 | 509 | .my-10 { 510 | margin-top: 2.5rem; 511 | margin-bottom: 2.5rem; 512 | } 513 | 514 | .mx-2 { 515 | margin-left: 0.5rem; 516 | margin-right: 0.5rem; 517 | } 518 | 519 | .mt-6 { 520 | margin-top: 1.5rem; 521 | } 522 | 523 | .mt-7 { 524 | margin-top: 1.75rem; 525 | } 526 | 527 | .mt-8 { 528 | margin-top: 2rem; 529 | } 530 | 531 | .ml-2 { 532 | margin-left: 0.5rem; 533 | } 534 | 535 | .mt-16 { 536 | margin-top: 4rem; 537 | } 538 | 539 | .mt-4 { 540 | margin-top: 1rem; 541 | } 542 | 543 | .mt-3 { 544 | margin-top: 0.75rem; 545 | } 546 | 547 | .mb-4 { 548 | margin-bottom: 1rem; 549 | } 550 | 551 | .mt-14 { 552 | margin-top: 3.5rem; 553 | } 554 | 555 | .mt-2 { 556 | margin-top: 0.5rem; 557 | } 558 | 559 | .mr-3 { 560 | margin-right: 0.75rem; 561 | } 562 | 563 | .mt-10 { 564 | margin-top: 2.5rem; 565 | } 566 | 567 | .mb-5 { 568 | margin-bottom: 1.25rem; 569 | } 570 | 571 | .ml-3 { 572 | margin-left: 0.75rem; 573 | } 574 | 575 | .mt-1 { 576 | margin-top: 0.25rem; 577 | } 578 | 579 | .block { 580 | display: block; 581 | } 582 | 583 | .inline { 584 | display: inline; 585 | } 586 | 587 | .flex { 588 | display: flex; 589 | } 590 | 591 | .grid { 592 | display: grid; 593 | } 594 | 595 | .hidden { 596 | display: none; 597 | } 598 | 599 | .h-12 { 600 | height: 3rem; 601 | } 602 | 603 | .h-4 { 604 | height: 1rem; 605 | } 606 | 607 | .h-5 { 608 | height: 1.25rem; 609 | } 610 | 611 | .h-auto { 612 | height: auto; 613 | } 614 | 615 | .h-6 { 616 | height: 1.5rem; 617 | } 618 | 619 | .h-10 { 620 | height: 2.5rem; 621 | } 622 | 623 | .min-h-full { 624 | min-height: 100%; 625 | } 626 | 627 | .w-full { 628 | width: 100%; 629 | } 630 | 631 | .w-auto { 632 | width: auto; 633 | } 634 | 635 | .w-4 { 636 | width: 1rem; 637 | } 638 | 639 | .w-5 { 640 | width: 1.25rem; 641 | } 642 | 643 | .w-80 { 644 | width: 20rem; 645 | } 646 | 647 | .w-64 { 648 | width: 16rem; 649 | } 650 | 651 | .w-52 { 652 | width: 13rem; 653 | } 654 | 655 | .w-6 { 656 | width: 1.5rem; 657 | } 658 | 659 | .w-72 { 660 | width: 18rem; 661 | } 662 | 663 | .max-w-md { 664 | max-width: 28rem; 665 | } 666 | 667 | .flex-shrink-0 { 668 | flex-shrink: 0; 669 | } 670 | 671 | .cursor-pointer { 672 | cursor: pointer; 673 | } 674 | 675 | .appearance-none { 676 | -webkit-appearance: none; 677 | -moz-appearance: none; 678 | appearance: none; 679 | } 680 | 681 | .place-items-center { 682 | place-items: center; 683 | } 684 | 685 | .items-center { 686 | align-items: center; 687 | } 688 | 689 | .justify-center { 690 | justify-content: center; 691 | } 692 | 693 | .justify-between { 694 | justify-content: space-between; 695 | } 696 | 697 | .space-y-8 > :not([hidden]) ~ :not([hidden]) { 698 | --tw-space-y-reverse: 0; 699 | margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); 700 | margin-bottom: calc(2rem * var(--tw-space-y-reverse)); 701 | } 702 | 703 | .space-y-6 > :not([hidden]) ~ :not([hidden]) { 704 | --tw-space-y-reverse: 0; 705 | margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); 706 | margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); 707 | } 708 | 709 | .-space-y-px > :not([hidden]) ~ :not([hidden]) { 710 | --tw-space-y-reverse: 0; 711 | margin-top: calc(-1px * calc(1 - var(--tw-space-y-reverse))); 712 | margin-bottom: calc(-1px * var(--tw-space-y-reverse)); 713 | } 714 | 715 | .rounded-lg { 716 | border-radius: 0.5rem; 717 | } 718 | 719 | .rounded { 720 | border-radius: 0.25rem; 721 | } 722 | 723 | .rounded-md { 724 | border-radius: 0.375rem; 725 | } 726 | 727 | .rounded-none { 728 | border-radius: 0px; 729 | } 730 | 731 | .rounded-t-md { 732 | border-top-left-radius: 0.375rem; 733 | border-top-right-radius: 0.375rem; 734 | } 735 | 736 | .rounded-b-md { 737 | border-bottom-right-radius: 0.375rem; 738 | border-bottom-left-radius: 0.375rem; 739 | } 740 | 741 | .border { 742 | border-width: 1px; 743 | } 744 | 745 | .border-\[1px\] { 746 | border-width: 1px; 747 | } 748 | 749 | .border-b { 750 | border-bottom-width: 1px; 751 | } 752 | 753 | .border-gray-300 { 754 | --tw-border-opacity: 1; 755 | border-color: rgb(209 213 219 / var(--tw-border-opacity)); 756 | } 757 | 758 | .border-transparent { 759 | border-color: transparent; 760 | } 761 | 762 | .border-gray-200 { 763 | --tw-border-opacity: 1; 764 | border-color: rgb(229 231 235 / var(--tw-border-opacity)); 765 | } 766 | 767 | .border-red-800 { 768 | --tw-border-opacity: 1; 769 | border-color: rgb(153 27 27 / var(--tw-border-opacity)); 770 | } 771 | 772 | .border-stone-500 { 773 | --tw-border-opacity: 1; 774 | border-color: rgb(120 113 108 / var(--tw-border-opacity)); 775 | } 776 | 777 | .border-stone-400 { 778 | --tw-border-opacity: 1; 779 | border-color: rgb(168 162 158 / var(--tw-border-opacity)); 780 | } 781 | 782 | .border-gray-400 { 783 | --tw-border-opacity: 1; 784 | border-color: rgb(156 163 175 / var(--tw-border-opacity)); 785 | } 786 | 787 | .bg-gray-100 { 788 | --tw-bg-opacity: 1; 789 | background-color: rgb(243 244 246 / var(--tw-bg-opacity)); 790 | } 791 | 792 | .bg-indigo-600 { 793 | --tw-bg-opacity: 1; 794 | background-color: rgb(79 70 229 / var(--tw-bg-opacity)); 795 | } 796 | 797 | .bg-indigo-500 { 798 | --tw-bg-opacity: 1; 799 | background-color: rgb(99 102 241 / var(--tw-bg-opacity)); 800 | } 801 | 802 | .p-3 { 803 | padding: 0.75rem; 804 | } 805 | 806 | .p-2 { 807 | padding: 0.5rem; 808 | } 809 | 810 | .py-12 { 811 | padding-top: 3rem; 812 | padding-bottom: 3rem; 813 | } 814 | 815 | .px-4 { 816 | padding-left: 1rem; 817 | padding-right: 1rem; 818 | } 819 | 820 | .px-5 { 821 | padding-left: 1.25rem; 822 | padding-right: 1.25rem; 823 | } 824 | 825 | .py-4 { 826 | padding-top: 1rem; 827 | padding-bottom: 1rem; 828 | } 829 | 830 | .px-3 { 831 | padding-left: 0.75rem; 832 | padding-right: 0.75rem; 833 | } 834 | 835 | .py-2 { 836 | padding-top: 0.5rem; 837 | padding-bottom: 0.5rem; 838 | } 839 | 840 | .px-8 { 841 | padding-left: 2rem; 842 | padding-right: 2rem; 843 | } 844 | 845 | .px-2 { 846 | padding-left: 0.5rem; 847 | padding-right: 0.5rem; 848 | } 849 | 850 | .pt-16 { 851 | padding-top: 4rem; 852 | } 853 | 854 | .pl-3 { 855 | padding-left: 0.75rem; 856 | } 857 | 858 | .pb-16 { 859 | padding-bottom: 4rem; 860 | } 861 | 862 | .pr-2 { 863 | padding-right: 0.5rem; 864 | } 865 | 866 | .pl-4 { 867 | padding-left: 1rem; 868 | } 869 | 870 | .pr-4 { 871 | padding-right: 1rem; 872 | } 873 | 874 | .text-center { 875 | text-align: center; 876 | } 877 | 878 | .font-serif { 879 | font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; 880 | } 881 | 882 | .font-sans { 883 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 884 | } 885 | 886 | .text-3xl { 887 | font-size: 1.875rem; 888 | line-height: 2.25rem; 889 | } 890 | 891 | .text-sm { 892 | font-size: 0.875rem; 893 | line-height: 1.25rem; 894 | } 895 | 896 | .text-xl { 897 | font-size: 1.25rem; 898 | line-height: 1.75rem; 899 | } 900 | 901 | .text-xs { 902 | font-size: 0.75rem; 903 | line-height: 1rem; 904 | } 905 | 906 | .font-extrabold { 907 | font-weight: 800; 908 | } 909 | 910 | .font-semibold { 911 | font-weight: 600; 912 | } 913 | 914 | .font-medium { 915 | font-weight: 500; 916 | } 917 | 918 | .leading-5 { 919 | line-height: 1.25rem; 920 | } 921 | 922 | .leading-none { 923 | line-height: 1; 924 | } 925 | 926 | .text-gray-900 { 927 | --tw-text-opacity: 1; 928 | color: rgb(17 24 39 / var(--tw-text-opacity)); 929 | } 930 | 931 | .text-indigo-600 { 932 | --tw-text-opacity: 1; 933 | color: rgb(79 70 229 / var(--tw-text-opacity)); 934 | } 935 | 936 | .text-gray-800 { 937 | --tw-text-opacity: 1; 938 | color: rgb(31 41 55 / var(--tw-text-opacity)); 939 | } 940 | 941 | .text-white { 942 | --tw-text-opacity: 1; 943 | color: rgb(255 255 255 / var(--tw-text-opacity)); 944 | } 945 | 946 | .text-indigo-500 { 947 | --tw-text-opacity: 1; 948 | color: rgb(99 102 241 / var(--tw-text-opacity)); 949 | } 950 | 951 | .text-gray-600 { 952 | --tw-text-opacity: 1; 953 | color: rgb(75 85 99 / var(--tw-text-opacity)); 954 | } 955 | 956 | .text-red-600 { 957 | --tw-text-opacity: 1; 958 | color: rgb(220 38 38 / var(--tw-text-opacity)); 959 | } 960 | 961 | .text-violet-100 { 962 | --tw-text-opacity: 1; 963 | color: rgb(237 233 254 / var(--tw-text-opacity)); 964 | } 965 | 966 | .text-orange-800 { 967 | --tw-text-opacity: 1; 968 | color: rgb(154 52 18 / var(--tw-text-opacity)); 969 | } 970 | 971 | .text-indigo-700 { 972 | --tw-text-opacity: 1; 973 | color: rgb(67 56 202 / var(--tw-text-opacity)); 974 | } 975 | 976 | .text-orange-500 { 977 | --tw-text-opacity: 1; 978 | color: rgb(249 115 22 / var(--tw-text-opacity)); 979 | } 980 | 981 | .placeholder-gray-500::-moz-placeholder { 982 | --tw-placeholder-opacity: 1; 983 | color: rgb(107 114 128 / var(--tw-placeholder-opacity)); 984 | } 985 | 986 | .placeholder-gray-500:-ms-input-placeholder { 987 | --tw-placeholder-opacity: 1; 988 | color: rgb(107 114 128 / var(--tw-placeholder-opacity)); 989 | } 990 | 991 | .placeholder-gray-500::placeholder { 992 | --tw-placeholder-opacity: 1; 993 | color: rgb(107 114 128 / var(--tw-placeholder-opacity)); 994 | } 995 | 996 | .shadow-sm { 997 | --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); 998 | --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); 999 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1000 | } 1001 | 1002 | .outline-none { 1003 | outline: 2px solid transparent; 1004 | outline-offset: 2px; 1005 | } 1006 | 1007 | .hover\:rotate-\[45deg\]:hover { 1008 | --tw-rotate: 45deg; 1009 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 1010 | } 1011 | 1012 | .hover\:bg-indigo-700:hover { 1013 | --tw-bg-opacity: 1; 1014 | background-color: rgb(67 56 202 / var(--tw-bg-opacity)); 1015 | } 1016 | 1017 | .hover\:text-orange-600:hover { 1018 | --tw-text-opacity: 1; 1019 | color: rgb(234 88 12 / var(--tw-text-opacity)); 1020 | } 1021 | 1022 | .hover\:opacity-90:hover { 1023 | opacity: 0.9; 1024 | } 1025 | 1026 | .focus\:z-10:focus { 1027 | z-index: 10; 1028 | } 1029 | 1030 | .focus\:border-indigo-500:focus { 1031 | --tw-border-opacity: 1; 1032 | border-color: rgb(99 102 241 / var(--tw-border-opacity)); 1033 | } 1034 | 1035 | .focus\:border-gray-600:focus { 1036 | --tw-border-opacity: 1; 1037 | border-color: rgb(75 85 99 / var(--tw-border-opacity)); 1038 | } 1039 | 1040 | .focus\:outline-none:focus { 1041 | outline: 2px solid transparent; 1042 | outline-offset: 2px; 1043 | } 1044 | 1045 | .focus\:ring-2:focus { 1046 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1047 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1048 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1049 | } 1050 | 1051 | .focus\:ring-gray-700:focus { 1052 | --tw-ring-opacity: 1; 1053 | --tw-ring-color: rgb(55 65 81 / var(--tw-ring-opacity)); 1054 | } 1055 | 1056 | .focus\:ring-indigo-500:focus { 1057 | --tw-ring-opacity: 1; 1058 | --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); 1059 | } 1060 | 1061 | .focus\:ring-offset-2:focus { 1062 | --tw-ring-offset-width: 2px; 1063 | } 1064 | 1065 | .group:hover .group-hover\:text-indigo-400 { 1066 | --tw-text-opacity: 1; 1067 | color: rgb(129 140 248 / var(--tw-text-opacity)); 1068 | } 1069 | 1070 | @media (min-width: 640px) { 1071 | .sm\:w-64 { 1072 | width: 16rem; 1073 | } 1074 | 1075 | .sm\:px-6 { 1076 | padding-left: 1.5rem; 1077 | padding-right: 1.5rem; 1078 | } 1079 | 1080 | .sm\:text-sm { 1081 | font-size: 0.875rem; 1082 | line-height: 1.25rem; 1083 | } 1084 | } 1085 | 1086 | @media (min-width: 768px) { 1087 | .md\:ml-12 { 1088 | margin-left: 3rem; 1089 | } 1090 | 1091 | .md\:mt-0 { 1092 | margin-top: 0px; 1093 | } 1094 | 1095 | .md\:mt-24 { 1096 | margin-top: 6rem; 1097 | } 1098 | 1099 | .md\:block { 1100 | display: block; 1101 | } 1102 | 1103 | .md\:flex { 1104 | display: flex; 1105 | } 1106 | 1107 | .md\:w-64 { 1108 | width: 16rem; 1109 | } 1110 | 1111 | .md\:w-80 { 1112 | width: 20rem; 1113 | } 1114 | 1115 | .md\:w-96 { 1116 | width: 24rem; 1117 | } 1118 | } 1119 | 1120 | @media (min-width: 1024px) { 1121 | .lg\:ml-24 { 1122 | margin-left: 6rem; 1123 | } 1124 | 1125 | .lg\:mt-0 { 1126 | margin-top: 0px; 1127 | } 1128 | 1129 | .lg\:flex { 1130 | display: flex; 1131 | } 1132 | 1133 | .lg\:w-96 { 1134 | width: 24rem; 1135 | } 1136 | 1137 | .lg\:px-8 { 1138 | padding-left: 2rem; 1139 | padding-right: 2rem; 1140 | } 1141 | } 1142 | 1143 | @media (min-width: 1280px) { 1144 | .xl\:w-10\/12 { 1145 | width: 83.333333%; 1146 | } 1147 | 1148 | .xl\:px-24 { 1149 | padding-left: 6rem; 1150 | padding-right: 6rem; 1151 | } 1152 | } --------------------------------------------------------------------------------