| 1 | import {
|
|---|
| 2 | type Component,
|
|---|
| 3 | createEffect,
|
|---|
| 4 | createSignal,
|
|---|
| 5 | createResource,
|
|---|
| 6 | Show,
|
|---|
| 7 | } from "solid-js";
|
|---|
| 8 | import { useNavigate } from "@solidjs/router";
|
|---|
| 9 | import { useAuth } from "@/context/AuthContext";
|
|---|
| 10 | import { blogApi, type BlogPost } from "@/api/blog";
|
|---|
| 11 | import BlogList from "@/components/BlogList";
|
|---|
| 12 | import BlogModal from "@/components/BlogModal";
|
|---|
| 13 | import CreateBlogForm from "@/components/CreateBlogForm";
|
|---|
| 14 | import { UserType } from "@/enums/UserType";
|
|---|
| 15 |
|
|---|
| 16 | const Blogs: Component = () => {
|
|---|
| 17 | const { user, isAuthenticated } = useAuth();
|
|---|
| 18 | const navigate = useNavigate();
|
|---|
| 19 | const [showCreateForm, setShowCreateForm] = createSignal(false);
|
|---|
| 20 | const [selectedBlogId, setSelectedBlogId] = createSignal<number | null>(null);
|
|---|
| 21 | const [editMode, setEditMode] = createSignal(false);
|
|---|
| 22 |
|
|---|
| 23 | const [blogs, { refetch: refetchBlogs }] = createResource(async () => {
|
|---|
| 24 | if (!isAuthenticated()) return [];
|
|---|
| 25 | return await blogApi.getAllBlogs();
|
|---|
| 26 | });
|
|---|
| 27 |
|
|---|
| 28 | const [selectedBlog, { refetch: refetchSelectedBlog }] = createResource(
|
|---|
| 29 | selectedBlogId,
|
|---|
| 30 | async (id) => {
|
|---|
| 31 | if (!id) return null;
|
|---|
| 32 | return await blogApi.getBlog(id);
|
|---|
| 33 | },
|
|---|
| 34 | );
|
|---|
| 35 |
|
|---|
| 36 | createEffect(() => {
|
|---|
| 37 | if (!isAuthenticated()) {
|
|---|
| 38 | navigate("/login", { replace: true });
|
|---|
| 39 | return;
|
|---|
| 40 | }
|
|---|
| 41 |
|
|---|
| 42 | const currentUser = user();
|
|---|
| 43 | if (currentUser?.userType !== UserType.PATIENT) {
|
|---|
| 44 | navigate("/", { replace: true });
|
|---|
| 45 | }
|
|---|
| 46 | });
|
|---|
| 47 |
|
|---|
| 48 | const handleCreateBlog = async (title: string, content: string) => {
|
|---|
| 49 | await blogApi.createBlog({ title, content });
|
|---|
| 50 | setShowCreateForm(false);
|
|---|
| 51 | refetchBlogs();
|
|---|
| 52 | };
|
|---|
| 53 |
|
|---|
| 54 | const handleLike = async (blogId: number) => {
|
|---|
| 55 | try {
|
|---|
| 56 | await blogApi.toggleLike(blogId);
|
|---|
| 57 | } finally {
|
|---|
| 58 | refetchBlogs();
|
|---|
| 59 | if (selectedBlogId() === blogId) {
|
|---|
| 60 | refetchSelectedBlog();
|
|---|
| 61 | }
|
|---|
| 62 | }
|
|---|
| 63 | };
|
|---|
| 64 |
|
|---|
| 65 | const handleAddComment = async (content: string) => {
|
|---|
| 66 | const blog = selectedBlog();
|
|---|
| 67 | if (!blog) return;
|
|---|
| 68 |
|
|---|
| 69 | await blogApi.addComment(blog.idBlog, content);
|
|---|
| 70 | refetchBlogs();
|
|---|
| 71 | refetchSelectedBlog();
|
|---|
| 72 | };
|
|---|
| 73 |
|
|---|
| 74 | const handleUpdateBlog = async (
|
|---|
| 75 | blogId: number,
|
|---|
| 76 | title: string,
|
|---|
| 77 | content: string,
|
|---|
| 78 | ) => {
|
|---|
| 79 | await blogApi.updateBlog(blogId, { title, content });
|
|---|
| 80 | refetchBlogs();
|
|---|
| 81 | if (selectedBlogId() === blogId) {
|
|---|
| 82 | refetchSelectedBlog();
|
|---|
| 83 | }
|
|---|
| 84 | };
|
|---|
| 85 |
|
|---|
| 86 | const handleDeleteBlog = async (blogId: number) => {
|
|---|
| 87 | await blogApi.deleteBlog(blogId);
|
|---|
| 88 | if (selectedBlogId() === blogId) {
|
|---|
| 89 | closeBlog();
|
|---|
| 90 | }
|
|---|
| 91 | refetchBlogs();
|
|---|
| 92 | };
|
|---|
| 93 |
|
|---|
| 94 | const handleUpdateComment = async (commentId: number, content: string) => {
|
|---|
| 95 | await blogApi.updateComment(commentId, content);
|
|---|
| 96 | refetchBlogs();
|
|---|
| 97 | refetchSelectedBlog();
|
|---|
| 98 | };
|
|---|
| 99 |
|
|---|
| 100 | const handleDeleteComment = async (commentId: number) => {
|
|---|
| 101 | await blogApi.deleteComment(commentId);
|
|---|
| 102 | refetchBlogs();
|
|---|
| 103 | refetchSelectedBlog();
|
|---|
| 104 | };
|
|---|
| 105 |
|
|---|
| 106 | const openBlog = (blog: BlogPost) => {
|
|---|
| 107 | setSelectedBlogId(blog.idBlog);
|
|---|
| 108 | setEditMode(false);
|
|---|
| 109 | };
|
|---|
| 110 |
|
|---|
| 111 | const openBlogInEditMode = (blog: BlogPost) => {
|
|---|
| 112 | setSelectedBlogId(blog.idBlog);
|
|---|
| 113 | setEditMode(true);
|
|---|
| 114 | };
|
|---|
| 115 |
|
|---|
| 116 | const handleQuickDelete = async (blogId: number) => {
|
|---|
| 117 | await blogApi.deleteBlog(blogId);
|
|---|
| 118 | refetchBlogs();
|
|---|
| 119 | };
|
|---|
| 120 |
|
|---|
| 121 | const closeBlog = () => {
|
|---|
| 122 | setSelectedBlogId(null);
|
|---|
| 123 | setEditMode(false);
|
|---|
| 124 | };
|
|---|
| 125 |
|
|---|
| 126 | return (
|
|---|
| 127 | <div class="max-w-4xl mx-auto px-4 py-8">
|
|---|
| 128 | <div class="flex justify-between items-center mb-8">
|
|---|
| 129 | <h1 class="text-3xl font-bold text-gray-900">Community Blogs</h1>
|
|---|
| 130 | <button
|
|---|
| 131 | onClick={() => setShowCreateForm(!showCreateForm())}
|
|---|
| 132 | class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors cursor-pointer"
|
|---|
| 133 | >
|
|---|
| 134 | {showCreateForm() ? "Cancel" : "Create Post"}
|
|---|
| 135 | </button>
|
|---|
| 136 | </div>
|
|---|
| 137 |
|
|---|
| 138 | <Show when={blogs.error}>
|
|---|
| 139 | <div class="mb-4 p-4 bg-red-50 border border-red-200 text-red-700 rounded-md">
|
|---|
| 140 | {blogs.error instanceof Error
|
|---|
| 141 | ? blogs.error.message
|
|---|
| 142 | : "Failed to load blogs"}
|
|---|
| 143 | </div>
|
|---|
| 144 | </Show>
|
|---|
| 145 |
|
|---|
| 146 | <Show when={showCreateForm()}>
|
|---|
| 147 | <CreateBlogForm
|
|---|
| 148 | onSubmit={handleCreateBlog}
|
|---|
| 149 | onCancel={() => setShowCreateForm(false)}
|
|---|
| 150 | />
|
|---|
| 151 | </Show>
|
|---|
| 152 |
|
|---|
| 153 | <BlogList
|
|---|
| 154 | blogs={blogs() || []}
|
|---|
| 155 | loading={blogs.loading}
|
|---|
| 156 | openBlog={openBlog}
|
|---|
| 157 | onLike={handleLike}
|
|---|
| 158 | onEdit={openBlogInEditMode}
|
|---|
| 159 | onDelete={handleQuickDelete}
|
|---|
| 160 | />
|
|---|
| 161 |
|
|---|
| 162 | <Show when={selectedBlogId() && selectedBlog()}>
|
|---|
| 163 | <BlogModal
|
|---|
| 164 | blog={selectedBlog()!}
|
|---|
| 165 | onClose={closeBlog}
|
|---|
| 166 | onLike={handleLike}
|
|---|
| 167 | onAddComment={handleAddComment}
|
|---|
| 168 | onUpdateBlog={handleUpdateBlog}
|
|---|
| 169 | onDeleteBlog={handleDeleteBlog}
|
|---|
| 170 | onUpdateComment={handleUpdateComment}
|
|---|
| 171 | onDeleteComment={handleDeleteComment}
|
|---|
| 172 | initialEditMode={editMode()}
|
|---|
| 173 | />
|
|---|
| 174 | </Show>
|
|---|
| 175 | </div>
|
|---|
| 176 | );
|
|---|
| 177 | };
|
|---|
| 178 |
|
|---|
| 179 | export default Blogs;
|
|---|