Skip to content
Merged
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
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"react-hot-toast": "^2.4.1",
"react-icons": "^4.8.0",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.14.2",
"react-router-dom": "^6.21.1",
"react-scripts": "5.0.1",
"react-simplemde-editor": "^5.2.0",
"simplemde": "^1.11.2",
Expand Down
8 changes: 5 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HomePage, PostForm, NotFoundPage } from "./pages/index";
import { HomePage, PostForm, NotFoundPage, SubscriptionPage } from "./pages/index";
import { Routes, Route } from "react-router-dom";
import { PostProvider } from "./context/postContext";
import { Toaster } from "react-hot-toast";
Expand All @@ -10,6 +10,7 @@ import Navbar from "./components/Navbar/Navbar";
import ContactForm from "./pages/Contact";
import { DefaultCarousel } from "./components/Carousel/Carousel";
import UserList from "./pages/Users";
import { PostDetailsCard } from "./components/PostDetailsCard";

function App() {
return (
Expand All @@ -23,11 +24,12 @@ function App() {
<Route path="login" exact element={<Login />} />
<Route path="/" exact element={<HomePageWithCarousel />} />
<Route path="/contact" exact element={<ContactForm />} />

<Route path="/post/:id" exact element={<PostDetailsCard />} />
<Route path="/subscription" exact element={<SubscriptionPage />} />
{/* Private routes */}
<Route element={<RequiresAuth allowedRoles={["moderator"]} />}>
<Route path="/admin" exact element={<HomePage />} />
<Route path="/posts/:id" element={<PostForm />} />
<Route path="/edit/:id" element={<PostForm />} />
<Route path="/new" exact element={<PostForm />} />
<Route path="/users" exact element={<UserList />} />
</Route>
Expand Down
128 changes: 128 additions & 0 deletions src/components/Comments.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from "react";
import moment from "moment";
import axios from "axios";
import toast from "react-hot-toast";
import Cookies from "js-cookie";
import { useState } from "react";

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,
}
);

onDelete(comment._id);

toast.success("Comment successfully deleted.");
} catch (error) {
toast.error("Error deleting the comment", error);
}
};

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

if (onEdit) {
onEdit(response.data);
}

toast.success("Comment successfully edited.");
setIsEditing(false);
} catch (error) {
toast.error("Error editing the comment", error);
}
};

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

return (
<div className="comentario mb-3">
<div className="comentario-info">
<strong className="text-base font-semibold text-gray-900">
{comment.commentator}
</strong>
<small className="text-sm font-normal text-gray-500 ml-1">
-{moment(comment.createdAt).format("DD/MM/YYYY")}
<span className="isolate inline-flex rounded-md shadow-sm">
{canEditAndDelete && (
<>
{isEditing ? (
<>
<button
type="button"
className="relative inline-flex items-center 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"
onClick={handleEditComment}
>
Save
</button>
<button
type="button"
className="relative -ml-px inline-flex items-center rounded-r-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"
onClick={() => setIsEditing(false)}
>
Cancel
</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"
onClick={() => setIsEditing(true)}
>
Edit
</button>
<button
type="button"
className="relative -ml-px inline-flex items-center 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"
onClick={handleDeleteComment}
>
Delete
</button>
</>
)}
</>
)}
</span>
</small>
</div>
{isEditing ? (
<textarea
value={editedText}
onChange={(e) => setEditedText(e.target.value)}
className="text-sm text-gray-600 border p-1 mt-2"
/>
) : (
<div className="text-sm text-gray-600">{comment.text}</div>
)}
</div>
);
};

export const Comments = ({ comments, onDeleteComment, onEditComment }) => (
<div className="comentarios">
{comments.map((comment) => (
<Comment
key={comment._id}
comment={comment}
onDelete={onDeleteComment}
onEdit={onEditComment}
/>
))}
</div>
);
18 changes: 18 additions & 0 deletions src/components/Navbar/Location.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useLocation } from "react-router-dom";

export const useRouteVariables = () => {
const location = useLocation();

return {
isLoginPage: location.pathname === "/login",
isHomePage: location.pathname === "/",
isSignupPage: location.pathname === "/signup",
isFormPage: location.pathname === "/new",
isAdminPage: location.pathname === "/admin",
userListPage: location.pathname === "/users",
isContactPage: location.pathname === "/contact",
isSubscribePage: location.pathname === "/subscription",
isDetailPage: location.pathname.startsWith("/post/"),
hideHomeLink: location.pathname.startsWith("/posts/"),
};
};
45 changes: 32 additions & 13 deletions src/components/Navbar/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSignOutAlt } from "@fortawesome/free-solid-svg-icons";
import { Link, useLocation } from "react-router-dom";
import { Link } from "react-router-dom";
import useAuth from "../../hooks/useAuth";
import Cookies from "js-cookie";

import { useLocation } from "react-router-dom";
import { useRouteVariables } from "./Location";
const Navbar = () => {
const location = useLocation();
const isLoginPage = location.pathname === "/login";
const isHomePage = location.pathname === "/";
const isSignupPage = location.pathname === "/signup";
const isFormPage = location.pathname === "/new";
const isAdminPage = location.pathname === "/admin";
const userListPage = location.pathname === "/users";
const isContactPage = location.pathname === "/contact";

const {
isLoginPage,
isSignupPage,
hideHomeLink,
isAdminPage,
isDetailPage,
isFormPage,
userListPage,
isSubscribePage,
isHomePage,
isContactPage,
} = useRouteVariables();
const { setAuth } = useAuth();

const handleLogout = () => {
Expand All @@ -32,7 +37,7 @@ const Navbar = () => {
if (isLoginPage || isSignupPage) {
return null;
}
const hideHomeLink = location.pathname.startsWith("/posts/");

return (
<header className="antialiased bg-white dark-mode:bg-gray-900">
<nav className="w-full text-gray-700 bg-white dark-mode:text-gray-200 dark-mode:bg-gray-800">
Expand Down Expand Up @@ -70,14 +75,26 @@ const Navbar = () => {
open ? "flex" : "hidden"
} pb-4 md:pb-0 md:flex md:justify-end md:flex-row navbar-transition animate-flip-down duration-700`}
>
{ !hideHomeLink &&!isAdminPage && !isFormPage && !userListPage && (
{!hideHomeLink && !isAdminPage && !isFormPage && !userListPage && (
<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"
>
Home
</Link>
)}
{!hideHomeLink &&
!isAdminPage &&
!isFormPage &&
!userListPage &&
!isSubscribePage && (
<Link
to="/subscription"
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"
>
Subscribe
</Link>
)}

{!isHomePage && isAdminPage && (
<Link
Expand All @@ -90,7 +107,9 @@ const Navbar = () => {

{(!isHomePage || (userListPage && !isContactPage)) &&
!isAdminPage &&
location.pathname !== "/contact" && (
!isSubscribePage &&
location.pathname !== "/contact" &&
!isDetailPage && (
<Link
to="/admin"
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