Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified server/notes.db
Binary file not shown.
2 changes: 1 addition & 1 deletion server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(cors());
const port = process.env.PORT || 9000;
const port = process.env.PORT || 9002;

// Create SQLite database connection
const db = new sqlite3.Database('./notes.db'); // Replace with your desired database file name or path
Expand Down
17 changes: 8 additions & 9 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import AddNote from "./components/AddNote";
import Notes from "./components/Notes";
import EditNote from "./components/EditNote";
import { Routes, Route } from "react-router-dom";
import { Routes, Route, useLocation } from "react-router-dom";

function App() {

const locations = useLocation();

return (
<div className="bg-blue-600 min-h-screen flex">
<div className="w-full">
<div className="flex flex-col items-center">
<h3 className="text-3xl text-white mb-5 mt-5">My Notes</h3>
<div className="flex flex-col items-center bg-white lg:w-7/12 lg:mx-auto h-auto mt-10 rounded-md">
<h3 className="text-3xl text-start text-blue-600 mb-5 mt-5">My Notes</h3>
<Routes>
{window.location.pathname === "/" && (
<Route path="/" element={<AddNote />} />
) } else {
{
locations.pathname === "/" ? <Route path="/" element={<AddNote />} /> :
<Route path="/edit/:id" element={<EditNote />} />
}
}
</Routes>


<Notes />
</div>
</div>
Expand Down
29 changes: 10 additions & 19 deletions src/components/AddNote.jsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useDispatch } from "react-redux";
import { addNote } from "../store/api/NoteSlice";
import { useAddNotesMutation } from '../store/api/NoteSlice';
const AddNote = () => {

const AddNote = (props) => {

const dispatch = useDispatch();
const [ addNotes ] = useAddNotesMutation();

const initialValues = {
title: '',
content: '',
};
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)
dispatch(addNote({
title: values.title,
content: values.content,
}));


// Reset the form after submission
const handleSubmit = (values,{ resetForm }) => {
addNotes(values);
resetForm();

};

return (
<div className="bg-white p-10 rounded-lg shadow md:w-3/4 mx-auto lg:w-1/2">
<div className="bg-white p-10 shadow w-[95%]">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
Expand Down
70 changes: 40 additions & 30 deletions src/components/EditNote.jsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,62 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { useSelector, useDispatch } from "react-redux";
import { editNote, fetchNotes } from "../store/api/NoteSlice";
import { useParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { useUpdateNotesMutation, useFetchNotesQuery } from '../store/api/NoteSlice';

const EditNote = () => {

const dispatch = useDispatch();
const params = useParams();
const navigate = useNavigate();

const [initialValues, setInitialValues] = useState({
title: '',
content: '',
const { data: notes = [] } = useFetchNotesQuery();
const [note, setNote] = useState({
title: "",
content: "",
});

const allNotes = useSelector((state) => state.notes.notes);


const [updateNotes] = useUpdateNotesMutation();

useEffect(() => {
dispatch(fetchNotes());
}, [dispatch]);

useEffect(() => {
const note = allNotes.find((note) => note.id === Number(params.id));
if (note) {
setInitialValues({
title: note.title,
content: note.content,
const noteInfo = notes.find(note => note.id === Number(params.id));
if (noteInfo) {
setNote({
title: noteInfo.title,
content: noteInfo.content,
});
}
}, [allNotes, params.id]);


}, [params.id, notes])

const initialValues = {
title: note.title,
content: note.content
}
const validationSchema = Yup.object({
title: Yup.string().required('Title is required'),
content: Yup.string().required('Content is required'),
});

const handleSubmit = (values) => {

dispatch(editNote({
noteId: Number(params.id),
updateNote: values,
})).then(() => {
navigate('/');
});
updateNotes({
id: Number(params.id),
updateNote: values
}).unwrap().then(()=>{
navigate("/")
})
.catch((err)=>{
console.log(err)
})
};

return (
<div className="bg-white p-10 rounded-lg shadow md:w-3/4 mx-auto lg:w-1/2">
<div className="bg-white p-10 rounded-lg shadow w-[95%]">
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
enableReinitialize
>
<Form>
<div className="mb-5">
Expand Down Expand Up @@ -90,6 +90,16 @@ const EditNote = () => {
</Formik>
</div>
);

// useEffect(() => {
// const note = allNotes.find((note) => note.id === Number(params.id));
// if (note) {
// setInitialValues({
// title: note.title,
// content: note.content,
// });
// }
// }, [allNotes, params.id]);
};

export default EditNote;
34 changes: 13 additions & 21 deletions src/components/Notes.jsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
/* eslint-disable react/prop-types */

import React, { useEffect } from "react";
import { FaEdit, FaTrash } from "react-icons/fa";
import { useSelector, useDispatch } from "react-redux";
import { deleteNote, fetchNotes } from "../store/api/NoteSlice";
import { Link } from "react-router-dom";
import { useDeleteNotesMutation, useFetchNotesQuery } from "../store/api/NoteSlice";

function Notes() {
const allNotes = useSelector((state) => state.notes);

const { notes, status, error } = allNotes;

const dispatch = useDispatch();

useEffect(() => {
dispatch(fetchNotes());
}, [dispatch]);
const { data : notes = [] , isLoading , errors} = useFetchNotesQuery();
const [ deleteNotes] = useDeleteNotesMutation ();
console.log("miirshe",notes);

const deleteNoteHandler = (id) => {
dispatch(deleteNote(id));
deleteNotes(id)
};


return (
<div className="flex flex-wrap justify-center mt-5">
{status === "loading" && <div className="relative p-5 bg-yellow-400 w-64 h-64 m-5 shadow-2xl overflow-hidden">Loading...</div>}
{status === "failed" && <div className="relative p-5 bg-yellow-400 w-64 h-64 m-5 shadow-2xl overflow-hidden">Sorry, {error}</div>}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-2 mt-5">
{isLoading === "loading" && <div className="relative p-5 bg-yellow-400 w-64 h-64 m-5 shadow-2xl overflow-hidden">Loading...</div>}
{errors === "failed" && <div className="relative p-5 bg-yellow-400 w-64 h-64 m-5 shadow-2xl overflow-hidden">Sorry, {errors}</div>}
{notes.map((note) => (
<div
className="relative bg-yellow-400 w-64 h-64 m-5 shadow-2xl overflow-hidden"
className="relative bg-slate-50 lg:w-56 h-64 m-5 shadow-2xl overflow-hidden"
key={note.id}
>
<div className="p-5">
<h3 className="font-bold text-2xl mb-4">{note.title}</h3>
<h3 className="font-bold text-2xl mb-4 text-blue-600">{note.title}</h3>
<p>{note.content}</p>
</div>
<div className="absolute bg-yellow-400 w-12 h-12 rotate-45 -top-6 -left-6" />
<div className="absolute bottom-0 left-0 right-0 flex justify-center p-4">
<div className="absolute bottom-0 left-0 right-0 flex justify-start items-center p-4">
<Link to={`/edit/${note.id}`}>
<button className="mr-2">
<FaEdit size={20} />
<FaEdit className="text-blue-600" size={25} />
</button>
</Link>
<button>
<FaTrash size={20} onClick={() => deleteNoteHandler(note.id)} />
<FaTrash className="text-red-500" size={20} onClick={() => deleteNoteHandler(note.id)} />
</button>
</div>
</div>
Expand Down
110 changes: 49 additions & 61 deletions src/store/api/NoteSlice.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,50 @@
import {createSlice, createAsyncThunk} from "@reduxjs/toolkit";
import axios from "axios";

const initialState = {
notes: [],
status: "idle",
error: null
};

export const fetchNotes = createAsyncThunk("note/fetchNotes", async () => {
const response = await axios.get("http://localhost:9000/notes");
return response.data;
});

export const addNote = createAsyncThunk("note/addNote", async (newNote) => {
const response = await axios.post("http://localhost:9000/create_note", newNote);
return response.data;
});

export const editNote = createAsyncThunk("note/editNote", async ({noteId, updateNote}) => {
const response = await axios.put(`http://localhost:9000/update_note/${noteId}`, updateNote);
return response.data;
});

export const deleteNote = createAsyncThunk("note/deleteNote", async (noteId) => {
await axios.delete(`http://localhost:9000/delete_note/${noteId}`);
return noteId;
});

export const noteSlice = createSlice({
name: "notes",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchNotes.pending, (state) => {
state.status = "loading";
state.error = null;
}).addCase(fetchNotes.fulfilled, (state, action) => {
state.status = "succeeded";
state.notes = action.payload;
}).addCase(fetchNotes.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
}).addCase(addNote.fulfilled, (state, action) => {
state.notes.push(action.payload);
}).addCase(editNote.fulfilled, (state, action) => {
const {id, title, content} = action.payload;
const existingNote = state.notes.find((note) => Number(note.id) === Number(id));
if (existingNote) {
existingNote.title = title;
existingNote.content = content;
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
// const Base_Url = 'http://localhost:9000';
export const NoteSlice = createApi({
reducerPath: "notesApi",
baseQuery: fetchBaseQuery({
baseUrl : 'http://localhost:9002'
}),

tagTypes : ["notesApi"],

endpoints: (builder) => ({
fetchNotes: builder.query({
query: () => {
return {
url: "notes",
method: "GET"
}
}

}).addCase(deleteNote.fulfilled, (state, action) => {
const noteId = action.payload;
state.notes = state.notes.filter((note) => note.id !== noteId);
});
}
});


export default noteSlice.reducer;
,
providesTags : ["notesApi"]
})
,
addNotes: builder.mutation({
query : (newBook) => ({
url : 'create_note',
method : 'POST',
body : newBook
}),
invalidatesTags : ["notesApi"]
})
,
deleteNotes: builder.mutation({
query : (id)=>({
url : `delete_note/${id}`,
method : 'DELETE',
}),
invalidatesTags : ["notesApi"]
}),
updateNotes: builder.mutation({
query : ({id , updateNote}) => ({
url : `update_note/${id}`,
method : 'PUT',
body : updateNote
}),
invalidatesTags : ["notesApi"]
})
})
})
// export { useFetchNotesQuery} from NoteSlice;
export const { useFetchNotesQuery , useAddNotesMutation , useDeleteNotesMutation , useUpdateNotesMutation} = NoteSlice;
12 changes: 8 additions & 4 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { configureStore } from '@reduxjs/toolkit'
import NoteReducer from './api/NoteSlice'

import { NoteSlice } from "./api/NoteSlice"
import { setupListeners } from '@reduxjs/toolkit/dist/query'
export const store = configureStore({
reducer: {
notes: NoteReducer

[NoteSlice.reducerPath] : NoteSlice.reducer
}
})
,
middleware : (getDefaultMiddleware)=>
getDefaultMiddleware().concat(NoteSlice.middleware)
})
setupListeners(store.dispatch);