Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHE-3] Create Forums Page #83

Merged
merged 40 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
18981a8
CHE-57 Created forum model
brok3turtl3 Apr 4, 2024
542ca9e
CHE-57 Created thread model
brok3turtl3 Apr 4, 2024
c1fbe91
CHE-57 Created post model
brok3turtl3 Apr 4, 2024
63fdecd
Merge pull request #61 from Code-Hammers/CHE-57/subtask/Create-Databa…
brok3turtl3 Apr 4, 2024
b4c7580
CHE-58 Updated index.ts and added first forum route and controller
brok3turtl3 Apr 4, 2024
2971a14
CHE-58 Added getAllForums controller and route
brok3turtl3 Apr 4, 2024
38f4c14
CHE-58 Added getForumById controller and route
brok3turtl3 Apr 4, 2024
f74ce60
CHE-58 Added updateForum controller and route
brok3turtl3 Apr 4, 2024
17a9ab8
CHE-58 Added deleteForum controller and route
brok3turtl3 Apr 4, 2024
d67e581
CHE-58 Added createThread controller and route and added a CustomRequ…
brok3turtl3 Apr 4, 2024
38a4c2f
CHE-58 Removed unused auth mock and imports
brok3turtl3 Apr 4, 2024
4ea7c5c
CHE-58 Added listThreadsByForumId controller and route
brok3turtl3 Apr 4, 2024
2d426ba
CHE-58 Added getThreadById controller and route
brok3turtl3 Apr 4, 2024
cf9c9a8
CHE-58 Added updateThread controller and route as well as adjusted au…
brok3turtl3 Apr 5, 2024
ed5d40a
CHE-58 Added deleteThread controller and route
brok3turtl3 Apr 5, 2024
99e0152
CHE-58 Added listPostsByThread controller and route
brok3turtl3 Apr 5, 2024
9145261
CHE-58 Added createPost controller and route
brok3turtl3 Apr 5, 2024
900ed19
CHE-58 Added updatePost controller and route
brok3turtl3 Apr 5, 2024
9567e0b
CHE-58 Added deletePost controller and route
brok3turtl3 Apr 5, 2024
c4e35b5
Merge pull request #62 from Code-Hammers/CHE-58/subtask/Create-Forums…
brok3turtl3 Apr 5, 2024
5f92deb
CHE-62 Created a basic ForumsList component to display forums
brok3turtl3 Apr 19, 2024
bb802b1
Merge branch 'dev' into CHE-3/story/Create-Forums-Page
brok3turtl3 Apr 19, 2024
acc7948
Merge branch 'CHE-3/story/Create-Forums-Page' into CHE-62/subtask/Cre…
brok3turtl3 Apr 19, 2024
5c733e7
CHE-62 Functionality and some styling for Forums, ThreadsDisplay, and…
brok3turtl3 Apr 20, 2024
9d80563
CHE-62 Reset snapshot
brok3turtl3 Apr 20, 2024
5e46876
CHE-62 Simplified testing file to a basic render test.
brok3turtl3 Apr 20, 2024
bf23079
Merge pull request #74 from Code-Hammers/CHE-62/subtask/Create-Forums…
brok3turtl3 Apr 20, 2024
29291c4
Quick refactors and updates to allow the display of the Forum title i…
brok3turtl3 Apr 20, 2024
3c2cd9c
Merge pull request #75 from Code-Hammers/forum-titles
brok3turtl3 Apr 20, 2024
3ead519
CHE-64 Added a Post type and created basic ThreadDetail component
brok3turtl3 Apr 20, 2024
1d49f9f
CHE-64 Functionality for displaying posts on threads in place
brok3turtl3 Apr 20, 2024
175d75b
Merge pull request #77 from Code-Hammers/CHE-64/subtask/Create-Thread…
brok3turtl3 Apr 21, 2024
37bd4b8
CHE-65 Created CreateThread component and updated ThreadsDisplay to s…
brok3turtl3 Apr 21, 2024
9b07314
CHE-65 Quick typing fix
brok3turtl3 Apr 21, 2024
4837ef5
Merge pull request #78 from Code-Hammers/CHE-65/subtask/Create-Create…
brok3turtl3 Apr 21, 2024
207cdc7
CHE-66 Created CreatePost component
brok3turtl3 Apr 21, 2024
6084bed
CHE-66 Adjustments
brok3turtl3 Apr 21, 2024
122ad5f
CHE-66 Quick typing fix
brok3turtl3 Apr 21, 2024
1e86039
Merge pull request #80 from Code-Hammers/CHE-66/subtask/Create-Create…
brok3turtl3 Apr 21, 2024
d93a245
CHE-3 merged dev code in to resolve any conflicts before merge to dev
brok3turtl3 Apr 22, 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
68 changes: 68 additions & 0 deletions client/src/components/Forums/CreatePost/CreatePost.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useState, ChangeEvent, FormEvent } from "react";
import axios from "axios";

interface CreatePostProps {
threadId: string;
forumId: string | null;
onClose: () => void;
}

const CreatePost: React.FC<CreatePostProps> = ({
forumId,
threadId,
onClose,
}) => {
const [content, setContent] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");

const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
setContent(e.target.value);
};

const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setIsLoading(true);

try {
const response = await axios.post(
`/api/forums/${forumId}/threads/${threadId}/posts`,
{
content,
},
{
withCredentials: true,
}
);
setContent("");
onClose();
setIsLoading(false);
} catch (error) {
console.error("Failed to create post:", error);
setError("Failed to create post");
setIsLoading(false);
}
};

return (
<form onSubmit={handleSubmit}>
<textarea
className="w-full rounded border p-2"
placeholder="Write your response..."
value={content}
onChange={handleChange}
required
/>
{error && <p className="text-red-500">{error}</p>}
<button
type="submit"
disabled={isLoading}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
{isLoading ? "Posting..." : "Post Reply"}
</button>
</form>
);
};

export default CreatePost;
96 changes: 96 additions & 0 deletions client/src/components/Forums/CreateThread/CreateThread.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useState, ChangeEvent, FormEvent } from "react";
import axios from "axios";

interface CreateThreadProps {
forumId: string;
onClose: () => void;
}

const CreateThread: React.FC<CreateThreadProps> = ({ forumId, onClose }) => {
const [formData, setFormData] = useState<{ title: string; content: string }>({
title: "",
content: "",
});

const { title, content } = formData;

const handleChange = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const response = await axios.post(
`/api/forums/${forumId}/threads`,
formData,
{
withCredentials: true,
}
);
console.log("Thread created:", response.data);
onClose();
} catch (error) {
console.error("Failed to create thread:", error);
//TODO userfeedback with errors
}
};

return (
<div className="w-full max-w-lg">
<form
className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
onSubmit={handleSubmit}
>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="title"
>
Title
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="title"
name="title"
type="text"
placeholder="Thread Title"
value={title}
onChange={handleChange}
required
/>
</div>
<div className="mb-6">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="content"
>
Content
</label>
<textarea
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="content"
name="content"
rows={4}
placeholder="Write something..."
value={content}
onChange={handleChange}
required
/>
</div>
<div className="flex items-center justify-between">
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Create Thread
</button>
</div>
</form>
</div>
);
};

export default CreateThread;
73 changes: 73 additions & 0 deletions client/src/components/Forums/ForumsList/ForumsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useEffect, useState } from "react";
import axios from "axios";

interface Forum {
_id: string;
title: string;
description?: string;
}

interface ForumsListProps {
onForumSelect: (forumId: string | null) => void;
selectedForumId: string | null;
}

const ForumsList: React.FC<ForumsListProps> = ({
onForumSelect,
selectedForumId,
}) => {
const [forums, setForums] = useState<Forum[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchForums = async () => {
setLoading(true);
try {
const { data } = await axios.get("/api/forums", {
withCredentials: true,
});
setForums(data);
setLoading(false);
} catch (err) {
const error = err as Error;
setError(error.message);
setLoading(false);
}
};

fetchForums();
}, []);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;

return (
<div>
<h2 className="text-xl font-bold mb-4">Forums</h2>
<ul>
<li
onClick={() => onForumSelect(null)}
className={`cursor-pointer p-2 hover:bg-gray-800 rounded-md ${
selectedForumId === null ? "bg-gray-700" : ""
}`}
>
All Forums
</li>
{forums.map((forum) => (
<li
key={forum._id}
onClick={() => onForumSelect(forum._id)}
className={`cursor-pointer p-2 hover:bg-gray-800 rounded-md ${
selectedForumId === forum._id ? "bg-gray-700" : ""
}`}
>
{forum.title}
</li>
))}
</ul>
</div>
);
};

export default ForumsList;
81 changes: 81 additions & 0 deletions client/src/components/Forums/ThreadDetails/ThreadDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import CreatePost from "../CreatePost/CreatePost";
import { Thread, IPost } from "../../../../types/forums";

interface ThreadDetailProps {
forumId: string | null;
threadId: string;
}

const ThreadDetail: React.FC<ThreadDetailProps> = ({ forumId, threadId }) => {
const [thread, setThread] = useState<Thread | null>(null);
const [posts, setPosts] = useState<IPost[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [creatingPost, setCreatingPost] = useState(false);

useEffect(() => {
const fetchThreadDetails = async () => {
setLoading(true);
try {
const endpoint = forumId
? `/api/forums/${forumId}/threads/${threadId}`
: `/api/forums/threads/${threadId}`;
const response = await axios.get(endpoint, { withCredentials: true });
setThread(response.data.thread);
setPosts(response.data.posts || []);

setLoading(false);
} catch (err) {
const error = err as Error;
setError(error.message);
setLoading(false);
}
};

fetchThreadDetails();
}, [forumId, threadId, creatingPost]);

const toggleCreatePost = () => {
setCreatingPost(!creatingPost);
};

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!thread) return <div>Thread not found.</div>;

return (
<div>
<h2 className="text-3xl font-bold">{thread.title}</h2>
<p className="my-4">{thread.content}</p>
<button
onClick={toggleCreatePost}
className="mb-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
{creatingPost ? "Cancel" : "Add Reply"}
</button>
{creatingPost && (
<CreatePost
forumId={forumId}
threadId={threadId}
onClose={toggleCreatePost}
/>
)}
<div>
<h3 className="text-2xl font-bold">Replies</h3>
{posts.map((post) => (
<div key={post._id} className="mb-4">
<p>{post.content}</p>
<small>
By {post.user.firstName} {post.user.lastName} on{" "}
{new Date(post.createdAt).toLocaleDateString()}
</small>
</div>
))}
</div>
</div>
);
};

export default ThreadDetail;
Loading
Loading