diff --git a/package-lock.json b/package-lock.json index 110a876..b297ae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,10 @@ "@reduxjs/toolkit": "^1.9.5", "axios": "^1.4.0", "formik": "^2.2.9", + "js-cookie": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^4.8.0", + "react-icons": "^4.10.1", "react-redux": "^8.0.5", "react-router-dom": "^6.12.1", "yup": "^1.1.1" @@ -2807,6 +2808,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-sdsl": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", @@ -3568,9 +3577,9 @@ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, "node_modules/react-icons": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", - "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", "peerDependencies": { "react": "*" } @@ -6345,6 +6354,11 @@ "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", "dev": true }, + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + }, "js-sdsl": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", @@ -6866,9 +6880,9 @@ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, "react-icons": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", - "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", "requires": {} }, "react-is": { diff --git a/package.json b/package.json index d09831c..d680c13 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,10 @@ "@reduxjs/toolkit": "^1.9.5", "axios": "^1.4.0", "formik": "^2.2.9", + "js-cookie": "^3.0.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^4.8.0", + "react-icons": "^4.10.1", "react-redux": "^8.0.5", "react-router-dom": "^6.12.1", "yup": "^1.1.1" diff --git a/src/App.jsx b/src/App.jsx index ac54132..de40eb7 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,27 +1,16 @@ -import AddNote from "./components/AddNote"; -import Notes from "./components/Notes"; -import EditNote from "./components/EditNote"; -import { Routes, Route } from "react-router-dom"; +// import { Route, Routes } from "react-router-dom"; +import Home from "./components/Home"; +// import Notes from "./components/Notes"; +// import AddNote from "./components/AddNote"; +// import EditNote from "./components/EditNote"; +// import Login from "./Auth/Login"; +// import Register from "./Auth/Register"; function App() { return (
-
-
-

My Notes

- - {window.location.pathname === "/" && ( - } /> - ) } else { - } /> - } - - - - -
-
+
); } diff --git a/src/Auth/Login.jsx b/src/Auth/Login.jsx new file mode 100644 index 0000000..3e7a7b5 --- /dev/null +++ b/src/Auth/Login.jsx @@ -0,0 +1,48 @@ + +import { ErrorMessage, Field, Form, Formik } from "formik"; +import * as Yup from "yup"; +import { useLoginMutation } from "../store/api/AuthSlice"; +import { useNavigate } from "react-router-dom"; +const Login = () => { + const [login] = useLoginMutation(); + const navigate = useNavigate(); + const initialValue = { + email: '', + password: '' + } + const ValidationSchema = Yup.object({ + email: Yup.string().required("enter your email "), + password: Yup.string().required("enter your password ") + }) + const handleSubmit = (values) => { + login({ + email: values.email, + password: values.password + }).then(() => { + navigate('/') + }).catch((err) => { + console.log(err) + }) + } + return ( +
+

Sign In

+
+ +
+ + + + + + +
+
+
+ ) +} + +export default Login \ No newline at end of file diff --git a/src/Auth/Register.jsx b/src/Auth/Register.jsx new file mode 100644 index 0000000..a7e51e2 --- /dev/null +++ b/src/Auth/Register.jsx @@ -0,0 +1,52 @@ +import { ErrorMessage, Field, Form, Formik } from "formik"; +import * as Yup from "yup"; +import { useRegisterMutation } from "../store/api/AuthSlice"; +import { useNavigate } from "react-router-dom"; +const Register = () => { + const [register] =useRegisterMutation(); + const navigate = useNavigate(); + const initialValue = { + name: '', + email: '', + password : '' + } + const ValidationSchema = Yup.object({ + name: Yup.string().required("enter your name "), + email: Yup.string().required("enter your email "), + password: Yup.string().required("enter your password ") + }) + const handleSubmit = (values) => { + console.log(values); + register({ + name: values.name, + email: values.email, + password : values.password + }).then(() => { navigate('/Login')}) + .catch( (error) => { + console.log(error) + }) + } + return ( +
+

Sign Up

+
+ +
+ + + + + + + + +
+
+
+ ) +} + +export default Register \ No newline at end of file diff --git a/src/BaseQuery.js b/src/BaseQuery.js new file mode 100644 index 0000000..b839a5d --- /dev/null +++ b/src/BaseQuery.js @@ -0,0 +1,2 @@ +const BaseQuery = "https://notes-60by.onrender.com"; +export default BaseQuery; \ No newline at end of file diff --git a/src/assets/avator.png b/src/assets/avator.png new file mode 100644 index 0000000..85aaea9 Binary files /dev/null and b/src/assets/avator.png differ diff --git a/src/components/AddNote.jsx b/src/components/AddNote.jsx index 931f954..9297792 100644 --- a/src/components/AddNote.jsx +++ b/src/components/AddNote.jsx @@ -1,74 +1,48 @@ -import React from 'react'; -import { Formik, Form, Field, ErrorMessage } from 'formik'; -import * as Yup from 'yup'; -import { useAddNoteMutation } from '../store/api/NoteSlice'; - +import { ErrorMessage, Field, Form, Formik } from "formik"; +import * as Yup from "yup"; +import { useAddNotesMutation } from "../store/api/NoteSlice"; +import { useNavigate } from "react-router-dom"; const AddNote = () => { - - const [addNote ] = useAddNoteMutation(); - - const initialValues = { + const [addNotes] = useAddNotesMutation(); + const navigate = useNavigate(); + const initialValue = { title: '', - content: '', - }; - - const validationSchema = Yup.object({ - title: Yup.string().required('Title is required'), - content: Yup.string().required('Content is required'), - }); - - const handleSubmit = (values, { resetForm }) => { - // Send the data to the server (localhost:9000/create_note) + content: '' + } + const ValidationSchema = Yup.object({ + title: Yup.string().required("enter your title note "), + content: Yup.string().required("enter your content note ") + }) + const handleSubmit = (values) => { console.log(values); - addNote({ + addNotes({ title: values.title, - content: values.content, - }); - - - // Reset the form after submission - resetForm(); - }; - + content: values.content + }).then(() => { + navigate('/'); + }).catch((err) => { + console.log(err); + }) + } return ( -
- -
-
- +
+

Add notes

+
+ + + -
- -
- + -
- - - - + + + +
- ); -}; + ) +} -export default AddNote; +export default AddNote \ No newline at end of file diff --git a/src/components/EditNote.jsx b/src/components/EditNote.jsx index 78f83bd..a9fa00d 100644 --- a/src/components/EditNote.jsx +++ b/src/components/EditNote.jsx @@ -1,92 +1,68 @@ -import React, { useEffect, useState } from 'react'; -import { Formik, Form, Field, ErrorMessage } from 'formik'; -import * as Yup from 'yup'; -import { useParams } from "react-router-dom"; -import { useNavigate } from "react-router-dom"; -import { useUpdateNoteMutation, useGetNotesQuery } from '../store/api/NoteSlice'; - +import { ErrorMessage, Field, Form, Formik } from "formik"; +import * as Yup from "yup"; +import { useGetNotesQuery, useUpdateNotesMutation } from "../store/api/NoteSlice"; +import { useNavigate, useParams } from "react-router-dom"; +import { useEffect } from "react"; +import { useState } from "react"; const EditNote = () => { - - const [updateNote ] = useUpdateNoteMutation(); - const { data: allNotes = [] } = useGetNotesQuery(); - const params = useParams(); const navigate = useNavigate(); - - const [initialValues, setInitialValues] = useState({ - title: '', - content: '', - }); - - - useEffect(() => { - const note = allNotes.find((note) => note.id === Number(params.id)); - if (note) { - setInitialValues({ - title: note.title, - content: note.content, - }); + const { data : notes = [] } = useGetNotesQuery(); + const [value , setValue] = useState({ + title :'', + content :'' + }) + useEffect(()=>{ + const note = notes.find(note =>{ + return note.id === Number(params.id) + }) + if(note){ + setValue({ + title : note.title, + content :note.content + }) } - }, [allNotes, params.id]); - - - - const validationSchema = Yup.object({ - title: Yup.string().required('Title is required'), - content: Yup.string().required('Content is required'), - }); - + },[notes , params.id]) + const [updateNote] = useUpdateNotesMutation(); + const initialValue = { + title: value.title, + content: value.content, + } + const ValidationSchema = Yup.object({ + title: Yup.string().required("enter your title note "), + content: Yup.string().required("enter your content note ") + }) const handleSubmit = (values) => { - + console.log(values); updateNote({ - id: Number(params.id), - updatedNote: values, + updateNote : values, + id : Number(params.id) + }).then(()=>{ + navigate('/'); + }).catch((err)=>{ + console.log(err); }) - - navigate("/"); - - }; - + } return ( -
- -
-
- +
+

Edit notes

+
+ + + -
- -
- + -
- - - - + + + +
- ); -}; + ) +} -export default EditNote; +export default EditNote \ No newline at end of file diff --git a/src/components/Home.jsx b/src/components/Home.jsx new file mode 100644 index 0000000..f94bcff --- /dev/null +++ b/src/components/Home.jsx @@ -0,0 +1,44 @@ +import { Route, Routes } from 'react-router-dom'; +import Notes from './Notes'; +import Sidebar from './Sidebar' +import { CiMenuKebab } from "react-icons/ci"; +import AddNote from './AddNote'; +import EditNote from './EditNote'; +import Login from '../Auth/Login'; +import Register from '../Auth/Register'; +import PrivateRoutes from '../privateRoutes/PrivateRoutes'; +import { useState } from 'react'; +import Profile from './Profile'; +const Home = () => { + const [ showMenu , setShowMenu ] = useState(false); + const HandleShowMenu = ()=>{ + setShowMenu(!showMenu); + } + const HandleHideMenu = ()=>{ + setShowMenu(false); + } + return ( +
+
+

Gabi Note App

+

+
+
+ + + } /> + }/> + }> + } /> + + + } /> + } /> + } /> + +
+
+ ) +} + +export default Home \ No newline at end of file diff --git a/src/components/Notes.jsx b/src/components/Notes.jsx index 83f9456..d6b2ab2 100644 --- a/src/components/Notes.jsx +++ b/src/components/Notes.jsx @@ -1,48 +1,47 @@ -/* eslint-disable react/prop-types */ - -import React, { useEffect } from "react"; -import { FaEdit, FaTrash } from "react-icons/fa"; +import { TiDocumentDelete } from "react-icons/ti"; +import { LuFileEdit } from "react-icons/lu"; import { Link } from "react-router-dom"; -import { useGetNotesQuery, useDeleteNoteMutation } from "../store/api/NoteSlice"; +import { useDeleteNotesMutation, useGetNotesQuery } from "../store/api/NoteSlice"; +import { useGetUserQuery } from "../store/api/UserSlice"; +const Notes = () => { + const [deleteNotes] = useDeleteNotesMutation(); + const { data: notes = [] } = useGetNotesQuery(); + const { data: users = {} } = useGetUserQuery(); + + console.log(notes); + console.log("users", users); + const handleDlete = (id)=>{ + deleteNotes(id) + } + return ( +
+

List notes

+
-function Notes() { + { + notes.map(note => { + { + if(users.id === note.user_id){ + return ( +
+

{note.title}

+

{note.content}

+
+ + +
+
+ ) + } + } - const { data: notes = [], status, error } = useGetNotesQuery(); - const [deleteNote] = useDeleteNoteMutation(); + }) + } - const deleteNoteHandler = (id) => { - deleteNote(id); - }; - +
- return ( -
- {status === "loading" &&
Loading...
} - {status === "failed" &&
Sorry, {error}
} - {notes.map((note) => ( -
-
-

{note.title}

-

{note.content}

-
-
-
- - - - -
-
- ))}
- ); + ) } -export default Notes; \ No newline at end of file +export default Notes \ No newline at end of file diff --git a/src/components/Profile.jsx b/src/components/Profile.jsx new file mode 100644 index 0000000..f000e86 --- /dev/null +++ b/src/components/Profile.jsx @@ -0,0 +1,20 @@ +import avatar from "../assets/avator.png"; +import { useGetUserQuery } from "../store/api/UserSlice"; +const Profile = () => { + const { data:users = { } } = useGetUserQuery(); + console.log(users.name) + return ( +
+

Profile

+
+ avatar profile +
+

Name : {users.name}

+

Email :{users.email}

+
+
+
+ ) +} + +export default Profile \ No newline at end of file diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx new file mode 100644 index 0000000..c74eccf --- /dev/null +++ b/src/components/Sidebar.jsx @@ -0,0 +1,53 @@ +import { Link, useNavigate } from "react-router-dom" +import { MdNoteAdd, MdHome } from "react-icons/md" +import { BiSolidLogInCircle } from "react-icons/bi" +import { BsPersonFillAdd } from "react-icons/bs" +import { useState } from "react" +import { useEffect } from "react" +import Cookies from "js-cookie" +import { FaUserCircle } from "react-icons/fa" +import "./Style.css" +const Sidebar = ({ HandleHideMenu, showMenu }) => { + const token = Cookies.get("token"); + const [auth, setAuth] = useState(false); + const navigate = useNavigate(); + useEffect(() => { + if (token) { + setAuth(true); + } + }, [token]) + const handleLogOut = () => { + Cookies.remove("token"); + setAuth(false); + navigate('/Login'); + } + return ( +
+ + +
+ ) +} + +export default Sidebar \ No newline at end of file diff --git a/src/components/Style.css b/src/components/Style.css new file mode 100644 index 0000000..4037587 --- /dev/null +++ b/src/components/Style.css @@ -0,0 +1,8 @@ +@media (max-width: 768px) { + .show { + display: block; + } + .hide { + display: none; + } +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 87b7375..29450eb 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,9 +2,9 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' -import { store } from './store' import { Provider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' +import { store } from './store/store.js' ReactDOM.createRoot(document.getElementById('root')).render( diff --git a/src/privateRoutes/PrivateRoutes.jsx b/src/privateRoutes/PrivateRoutes.jsx new file mode 100644 index 0000000..d94ef42 --- /dev/null +++ b/src/privateRoutes/PrivateRoutes.jsx @@ -0,0 +1,12 @@ +import Cookies from "js-cookie" +import { Navigate , Outlet} from "react-router-dom" +import Login from "../Auth/Login"; +const PrivateRoutes = () => { + const token = Cookies.get('token'); + if(!token) { + return }/> + } + return +} + +export default PrivateRoutes \ No newline at end of file diff --git a/src/store/api/AuthSlice.js b/src/store/api/AuthSlice.js new file mode 100644 index 0000000..9c7be19 --- /dev/null +++ b/src/store/api/AuthSlice.js @@ -0,0 +1,43 @@ +import { createApi , fetchBaseQuery } from "@reduxjs/toolkit/query/react"; +import BaseQuery from "../../BaseQuery"; +import Cookies from "js-cookie"; +const setcookie = (token)=>{ + return Cookies.set("token",token , { expires : 1 }); +} +export const AuthSlice = createApi({ + reducerPath : "authSlice", + baseQuery : fetchBaseQuery({ + baseUrl : BaseQuery + }) + , + endpoints : (builder)=>({ + register:builder.mutation({ + query:(newUser)=>({ + url : "register", + method : "POST", + body : newUser + }) + }), + login:builder.mutation({ + query:(user)=>({ + url : "login", + method : "POST", + body :user + }) + , + onQueryStarted : async (arg,{queryFulfilled})=>{ + try{ + const result = await queryFulfilled; + console.log("i alikey",result) + + setcookie(result.data.token); + } + catch(err){ + console.log(err) + } + } + }), + }) +}) + +export const {useLoginMutation , useRegisterMutation} = AuthSlice \ No newline at end of file diff --git a/src/store/api/NoteSlice.js b/src/store/api/NoteSlice.js index 1ce82f4..fab5c78 100644 --- a/src/store/api/NoteSlice.js +++ b/src/store/api/NoteSlice.js @@ -1,43 +1,72 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; +import BaseQuery from '../../BaseQuery'; +import Cookies from 'js-cookie'; -export const noteApi = createApi({ - reducerPath: 'noteApi', - baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:9000' }), +const getcookie = ()=>{ + return Cookies.get("token"); +} +export const NoteSlice = createApi({ + reducerPath: 'noteSlice', + baseQuery: fetchBaseQuery({ + baseUrl: BaseQuery, + prepareHeaders : (Headers)=>{ + const token = getcookie(); + if(token){ + Headers.set("Authorization",`Bearer ${token}`); + } + return Headers; + } + }), tagTypes: ['Note'], endpoints: (builder) => ({ + getNotes: builder.query({ - query: () => '/notes', - providesTags: ['Note'], + query: () => { + return{ + url: "notes", + method: "GET", + } + }, + providesTags: ["Note"] }), - addNote: builder.mutation({ - query: (body) => ({ - url: 'create_note', - method: 'POST', - body, + + addNotes: builder.mutation({ + query: (addNote) => ({ + url: "create_note", + method: "POST", + body: addNote }), - invalidatesTags: ['Note'], + invalidatesTags: ["Note"] }), - updateNote: builder.mutation({ - query: ({ id, updatedNote }) => ({ + + updateNotes: builder.mutation({ + query: ({ updateNote, id }) => ({ url: `update_note/${id}`, - method: 'PUT', - body: updatedNote, + method: "PUT", + body: updateNote }), - invalidatesTags: ['Note'], + invalidatesTags: ["Note"] + }), - deleteNote: builder.mutation({ + + deleteNotes: builder.mutation({ query: (id) => ({ url: `delete_note/${id}`, - method: 'DELETE', + method: "DELETE", }), - invalidatesTags: ['Note'], - }), - }), + invalidatesTags: ["Note"] + + }) + + }) }) -export const { - useGetNotesQuery, - useAddNoteMutation, - useUpdateNoteMutation, - useDeleteNoteMutation, -} = noteApi +export const { useGetNotesQuery, useAddNotesMutation, useUpdateNotesMutation, useDeleteNotesMutation } = NoteSlice; + + + + + + + + diff --git a/src/store/api/UserSlice.js b/src/store/api/UserSlice.js new file mode 100644 index 0000000..b43ec88 --- /dev/null +++ b/src/store/api/UserSlice.js @@ -0,0 +1,33 @@ +import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; +import BaseQuery from "../../BaseQuery"; +import Cookies from "js-cookie"; +const getcookie = () => { + return Cookies.get("token"); +} +export const UserSlice = createApi({ + reducerPath: "userSlice", + baseQuery: fetchBaseQuery({ + baseUrl: BaseQuery, + prepareHeaders: (Headers) => { + const token = getcookie(); + if (token) { + Headers.set("Authorization", `Bearer ${token}`); + } + return Headers + } + }), + tagTypes: ['userSlice'], + endpoints: (builder) => ({ + getUser: builder.query({ + query: () => { + return{ + url: "user", + method: "GET", + } + }, + providesTags: ["userSlice"] + }), + }) +}) + +export const { useGetUserQuery } = UserSlice \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js deleted file mode 100644 index dd2a6f5..0000000 --- a/src/store/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit' -import { noteApi } from './api/NoteSlice' -import { setupListeners } from '@reduxjs/toolkit/query' - -export const store = configureStore({ - reducer: { - [noteApi.reducerPath]: noteApi.reducer, - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(noteApi.middleware), - -}) - -setupListeners(store.dispatch) - diff --git a/src/store/store.js b/src/store/store.js new file mode 100644 index 0000000..a89d3d7 --- /dev/null +++ b/src/store/store.js @@ -0,0 +1,16 @@ +import { configureStore} from "@reduxjs/toolkit"; +import { NoteSlice } from "./api/NoteSlice"; +import { setupListeners } from "@reduxjs/toolkit/dist/query"; +import { AuthSlice } from "./api/AuthSlice"; +import { UserSlice } from "./api/UserSlice"; + +export const store = configureStore({ + reducer : { + [NoteSlice.reducerPath] : NoteSlice.reducer, + [AuthSlice.reducerPath] : AuthSlice.reducer, + [UserSlice.reducerPath] : UserSlice.reducer + }, + middleware : (getDefaultMiddleware)=>getDefaultMiddleware().concat(NoteSlice.middleware) + .concat(AuthSlice.middleware).concat(UserSlice.middleware) +}) +setupListeners(store.dispatch) \ No newline at end of file