Skip to content
Merged

Dev #53

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
18c4cb9
Delete Environment Variables
EdwinDev6 Aug 1, 2023
1a9bea7
Feat Application with notifications (#26)
EdwinDev6 Sep 11, 2023
30d2cc7
feat (auth): user data protected by cookies (#27)
EdwinDev6 Sep 19, 2023
4d001de
Fix: navigate to home by clicking on the logo (#28)
EdwinDev6 Sep 20, 2023
8afe00a
refactor changing HTML tags for semantic tags (#29)
EdwinDev6 Sep 27, 2023
6220788
Fix cancel pointer event (#30)
EdwinDev6 Oct 3, 2023
3d2c11f
Feat functionality that does not allow viewing the navbar in the sign…
EdwinDev6 Oct 3, 2023
f24a6bf
Feat error notifications when doing login or signup (#32)
EdwinDev6 Oct 3, 2023
77b6728
Fix password length validation (#33)
EdwinDev6 Oct 10, 2023
1b2f5d2
Fix carousel transition (#34)
EdwinDev6 Oct 10, 2023
fa04de6
Fix iterable posts
EdwinDev6 Oct 10, 2023
8340d6b
feat(posts): Add author, category and source to admin and user views …
EdwinDev6 Oct 17, 2023
c249597
feat(image-edit): allow changing images after publication (#36)
EdwinDev6 Oct 23, 2023
85293f2
Refactor: position to source in the post (#37)
EdwinDev6 Oct 23, 2023
47b8888
Refactor: source position in the post
EdwinDev6 Oct 23, 2023
da41ad9
feat: Add Markdown formatting (#38)
EdwinDev6 Oct 23, 2023
87f0662
Refactor: space between title and source description
EdwinDev6 Oct 24, 2023
6e940c7
Refactor: improve image src update (#40)
EdwinDev6 Oct 29, 2023
d22f013
Fix:(auth) login If the user exists (#41)
EdwinDev6 Dec 5, 2023
1d63da6
Fix(post) data required to publish
EdwinDev6 Dec 5, 2023
5e3c847
fix: source not requeride
EdwinDev6 Dec 5, 2023
5823a9c
Fix(post): bug that towards editing the obligatory category
EdwinDev6 Dec 5, 2023
d3923d3
feat: Implementation of detailed views of posts and interactive comme…
EdwinDev6 Jan 11, 2024
63392a6
fix: getting the token correctly
EdwinDev6 Jan 11, 2024
51bb634
Fix/admin/view (#43)
EdwinDev6 Jan 12, 2024
ed165ad
Feat: goback button in detailPost
EdwinDev6 Jan 12, 2024
6b3caaa
feat(subscription): implement automatic unsubscribe process (#44)
EdwinDev6 Jan 17, 2024
f4ae9d0
fix: routes in navbar
EdwinDev6 Jan 17, 2024
84cc0ca
refactor: size carousel (#45)
EdwinDev6 Jan 17, 2024
9f5fb0b
Feat: implementation of user options (#46)
EdwinDev6 Jan 18, 2024
f55b2e9
Feat(auth): forgot and reset password (#47)
EdwinDev6 Jan 19, 2024
8285dc8
feat: manage/roles (#48)
EdwinDev6 Jan 19, 2024
e5b7140
Fix/routes/navbar (#49)
EdwinDev6 Jan 22, 2024
784f978
Fix/views (#50)
EdwinDev6 Jan 23, 2024
50a659a
Merge branch 'main' into dev
EdwinDev6 Jan 25, 2024
0d2712b
Fix/views (#52)
EdwinDev6 Jan 25, 2024
6c297f4
Merge branch 'main' into dev
EdwinDev6 Jan 25, 2024
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
4 changes: 4 additions & 0 deletions src/Config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const baseUrl = process.env.REACT_APP_API_BASE_URL;
export const apiWeb = process.env.REACT_APP_API_CONTACT;
export const apiEmail = process.env.REACT_APP_EMAIL_KEY;

58 changes: 31 additions & 27 deletions src/api/posts.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import axios from "axios";
import Cookies from "js-cookie";
import Cookies from "js-cookie";
import { baseUrl } from "../Config";

const PostUrl = process.env.REACT_APP_POST_URL;
const UpdatePost = process.env.REACT_APP_UPDATE_POST;
const DeletePost = process.env.REACT_APP_POST_DELETE;
const PostId = process.env.REACT_APP_POST_ID;

export const getTokenFromCookie = () => {
return Cookies.get("token") || "";
};


export const getTokenFromCookie = () => {
Expand All @@ -14,7 +14,7 @@ export const getTokenFromCookie = () => {


export const getPostsRequest = async () =>
await axios.get(PostUrl, {});
await axios.get(`${baseUrl}/posts`, {});

export const createPostRequest = async (post) => {
const form = new FormData();
Expand All @@ -23,36 +23,40 @@ export const createPostRequest = async (post) => {
form.append(key, post[key]);
}

return await axios.post(PostUrl, form, {
return await axios.post(`${baseUrl}/posts`, form, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${getTokenFromCookie()}`,
Authorization: `Bearer ${getTokenFromCookie()}`,

},
});
};

export const deletePostRequest = async (id) =>
await axios.delete(DeletePost + id, {

await axios.delete(`${baseUrl}/posts/${id}`, {
headers: {
Authorization: `Bearer ${getTokenFromCookie()}`,
Authorization: `Bearer ${getTokenFromCookie()}`,

},
});

export const getPostRequest = async (id) =>
await axios.get(PostId + id);

export const updatePostRequest = async (id, formData) => {
try {
const response = await axios.put(`${UpdatePost}/${id}`, formData, {
headers: {
Authorization: `Bearer ${getTokenFromCookie()}`,
'Content-Type': 'multipart/form-data',
},
});

return response.data;
} catch (error) {

throw error;
}
};

await axios.get(`${baseUrl}/posts/` + id);

export const updatePostRequest = async (id, formData) => {
try {
const response = await axios.put(`${baseUrl}/posts/${id}`, formData, {
headers: {
Authorization: `Bearer ${getTokenFromCookie()}`,
"Content-Type": "multipart/form-data",
},
});

return response.data;
} catch (error) {
throw error;
}
};

60 changes: 43 additions & 17 deletions src/components/Comments.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ import axios from "axios";
import toast from "react-hot-toast";
import Cookies from "js-cookie";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faTrash,
faPenToSquare,
faXmark,
faCheck,
} from "@fortawesome/free-solid-svg-icons";
import { baseUrl } from "../Config";

export const Comment = ({ comment, onDelete, onEdit }) => {
const [isEditing, setIsEditing] = useState(false);
const [editedText, setEditedText] = useState(comment.text);

const handleDeleteComment = async () => {
try {
await axios.delete(
`http://localhost:4000/api/posts/${comment._id}/comments/`,
{
withCredentials: true,
}
);

await axios.delete(`${baseUrl}/posts/${comment._id}/comments/`, {
withCredentials: true,
});


onDelete(comment._id);

Expand All @@ -28,7 +36,9 @@ export const Comment = ({ comment, onDelete, onEdit }) => {
const handleEditComment = async () => {
try {
const response = await axios.put(
`http://localhost:4000/api/posts/${comment._id}/comments/`,

`${baseUrl}/posts/${comment._id}/comments/`,

{ text: editedText },
{
withCredentials: true,
Expand All @@ -47,7 +57,9 @@ export const Comment = ({ comment, onDelete, onEdit }) => {
};

const canEditAndDelete =
comment?.commentator?.trim() === Cookies.get("username")?.trim()

comment?.commentator?.trim() === Cookies.get("username")?.trim();


return (
<div className=" mflex-1 border rounded-lg px-4 py-2 sm:px-6 sm:py-4 leading-relaxed my-2">
Expand All @@ -64,34 +76,42 @@ export const Comment = ({ comment, onDelete, onEdit }) => {
<>
<button
type="button"
className="relative inline-flex items-center rounded-l-md bg-white px-2 py-1 text-xs font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"

className="mx-1 flex items-center justify-center bg-green-500 rounded-sm hover:rounded-3xl hover:bg-green-600 transition-all duration-300 text-white"
style={{ width: "20px", height: "20px" }}
onClick={handleEditComment}
>
Save
<FontAwesomeIcon icon={faCheck} />
</button>
<button
type="button"
className="relative inline-flex items-center rounded-l-md bg-white px-2 py-1 text-xs font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
className="flex items-center justify-center bg-red-500 rounded-sm hover:rounded-3xl hover:bg-red-600 transition-all duration-300 text-white"
style={{ width: "20px", height: "20px" }}
onClick={() => setIsEditing(false)}
>
Cancel
<FontAwesomeIcon icon={faXmark} />

</button>
</>
) : (
<>
<button
type="button"
className="relative inline-flex items-center rounded-l-md bg-white px-2 py-1 text-xs font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"

className="mx-1 flex items-center justify-center bg-blue-500 rounded-sm hover:rounded-3xl hover:bg-blue-600 transition-all duration-300 text-white "
style={{ width: "20px", height: "20px" }}
onClick={() => setIsEditing(true)}
>
Edit
<FontAwesomeIcon icon={faPenToSquare} />
</button>
<button
type="button"
className="relative inline-flex items-center rounded-l-md bg-white px-2 py-1 text-xs font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
className="flex items-center justify-center bg-red-500 rounded-sm hover:rounded-3xl hover:bg-red-600 transition-all duration-300 text-white"
style={{ width: "20px", height: "20px" }}
onClick={handleDeleteComment}
>
Delete
<FontAwesomeIcon icon={faTrash} />

</button>
</>
)}
Expand All @@ -104,7 +124,13 @@ export const Comment = ({ comment, onDelete, onEdit }) => {
<textarea
value={editedText}
onChange={(e) => setEditedText(e.target.value)}
className="text-sm text-gray-600 border p-1 mt-2 w-full"


className="px-3 py-2 border shadow-sm border-gray-300 rounded-md w-full block placeholder:text-gray-400 placeholder-gray-500
focus:outline-none focus:ring-1 bg-gray-50 focus:ring-blue-600 focus:border-blue-600 text-sm"
style={{ minHeight: "50px", maxHeight: "100px" }}


/>
) : (
<div className="text-sm text-gray-600">{comment.text}</div>
Expand Down
129 changes: 129 additions & 0 deletions src/components/ModeratorDropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useState, useRef, useEffect } from "react";
import { Link } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faEllipsisVertical,
faPenToSquare,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import toast from "react-hot-toast";
import { usePosts } from "../context/postContext";
import { useNavigate } from "react-router-dom";

export const ModeratorDropdown = ({ postId }) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const dropdownRef = useRef(null);
const { deletePost } = usePosts();
const navigate = useNavigate();

const toggleMenu = () => {
setIsMenuOpen((prevIsMenuOpen) => !prevIsMenuOpen);
};

const closeMenu = () => {
setIsMenuOpen(false);
};

const handleMenuItemClick = () => {
closeMenu();
};

const handleDelete = (id) => {
toast(
(t) => (
<div>
<p className="text-white">
<b> Do you want to Delete?</b>
</p>
<div>
<button
className="bg-red-500 hover:bg-red-400 px-3 py-2 text-sm text-white rounded-sm mx-2"
onClick={() => {
deletePost(id);
toast.dismiss(t.id);
toast.success("Post Deleted Successfully");
}}
>
Delete
</button>

<button
className="bg-slate-400 hover:bg-slate-500 px-3 py-2 text-white rounded-sm mx-2"
onClick={() => {
toast.dismiss(t.id);
toast(
"Notification: Post deletion cancelled. Your post has been preserved and will not be deleted. Thank you for your understanding and collaboration! ",
{
duration: 6000,
style: {
borderRadius: "10px",
background: "#333",
color: "#fff",
},
}
);
}}
>
Cancel
</button>
</div>
</div>
),
{
style: {
background: "#202020",
},
}
);
};

useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
closeMenu();
}
};

document.addEventListener("mousedown", handleClickOutside);

return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);

return (
<div className="relative z-50" ref={dropdownRef}>
<button
className="flex items-center p-2 text-sm text-gray-600 bg-white border border-transparent rounded-md focus:ring-opacity-40 dark:focus:ring-opacity-40 focus:ring-blue-300 dark:focus:ring-blue-400 focus:ring dark:text-white dark:bg-gray-800 focus:outline-none"
onClick={toggleMenu}
>
<span className="mx-1 text-sm font-semibold "></span>
<FontAwesomeIcon icon={faEllipsisVertical} size="xl" />
</button>

<div
className={`absolute right-0 z-20 w-[100px] py-2 mt-2 overflow-hidden bg-white rounded-md shadow-xl dark:bg-gray-800${
isMenuOpen ? " visible opacity-100" : " invisible opacity-0"
}`}
>
<Link
onClick={handleMenuItemClick}
className="flex items-center p-3 -mt-2 text-sm text-gray-600 transition-colors duration-200 transform dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-white"
></Link>
<button
className="block px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-white"
onClick={() => navigate(`/edit/${postId}`)}
>
Edit <FontAwesomeIcon icon={faPenToSquare} />
</button>

<button
className="block px-4 py-3 text-sm text-gray-600 capitalize transition-colors duration-200 transform dark:text-gray-300 dark:hover:bg-gray-700 dark:hover:text-white"
onClick={() => handleDelete(postId)}
>
Delete <FontAwesomeIcon icon={faTrash} />
</button>
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions src/components/Navbar/Location.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ export const useRouteVariables = () => {
isConfirmPage: location.pathname==="/confirm-unsubscribe",
isForgotPage: location.pathname ==="/forgot-password",
isResetPasswordPage: location.pathname.startsWith("/reset-password/"),
isEditPostPage: location.pathname.startsWith("/edit/")

};
};
7 changes: 5 additions & 2 deletions src/components/Navbar/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ const Navbar = () => {
isEmailPage,
isConfirmPage,
isForgotPage,
isResetPasswordPage
isResetPasswordPage,
isEditPostPage

} = useRouteVariables();

const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -77,7 +79,8 @@ const Navbar = () => {
!isAdminPage &&
!isFormPage &&
!userListPage &&
!isDetailPage && (
!isDetailPage && !isEditPostPage && (

<Link
to="/"
className="px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 md:ml-4 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline hover:underline"
Expand Down
Loading