source: frontend/src/components/BlogModal.tsx@ 700e2f9

main
Last change on this file since 700e2f9 was 700e2f9, checked in by 186079 <matej.milevski@…>, 5 days ago

Init

  • Property mode set to 100644
File size: 8.4 KB
Line 
1import { type Component, createSignal, createEffect, Show } from "solid-js";
2import CommentSection from "@/components/CommentSection";
3import { formatDate } from "@/utils";
4import { useAuth } from "@/context/AuthContext";
5import type { BlogPost } from "@/api/blog";
6
7interface BlogModalProps {
8 blog: BlogPost;
9 onClose: () => void;
10 onLike: (blogId: number) => void;
11 onAddComment: (content: string) => Promise<void>;
12 onUpdateBlog?: (
13 blogId: number,
14 title: string,
15 content: string,
16 ) => Promise<void>;
17 onDeleteBlog?: (blogId: number) => Promise<void>;
18 onUpdateComment?: (commentId: number, content: string) => Promise<void>;
19 onDeleteComment?: (commentId: number) => Promise<void>;
20 initialEditMode?: boolean;
21}
22
23const BlogModal: Component<BlogModalProps> = (props) => {
24 const { user } = useAuth();
25 const [isEditing, setIsEditing] = createSignal(false);
26 const [editTitle, setEditTitle] = createSignal("");
27 const [editContent, setEditContent] = createSignal("");
28
29 const isOwner = () => user()?.userId === props.blog?.patientId;
30
31 createEffect(() => {
32 if (props.initialEditMode && props.blog) {
33 setEditTitle(props.blog.title || "");
34 setEditContent(props.blog.content || "");
35 setIsEditing(true);
36 }
37 });
38
39 const startEdit = () => {
40 setEditTitle(props.blog?.title || "");
41 setEditContent(props.blog?.content || "");
42 setIsEditing(true);
43 };
44
45 const cancelEdit = () => {
46 setIsEditing(false);
47 };
48
49 const saveEdit = async () => {
50 if (props.onUpdateBlog && props.blog) {
51 await props.onUpdateBlog(props.blog.idBlog, editTitle(), editContent());
52 setIsEditing(false);
53 }
54 };
55
56 const handleDelete = async () => {
57 if (
58 props.onDeleteBlog &&
59 props.blog &&
60 confirm("Are you sure you want to delete this blog post?")
61 ) {
62 await props.onDeleteBlog(props.blog.idBlog);
63 }
64 };
65
66 return (
67 <div
68 class="fixed inset-0 backdrop-blur-sm bg-gray-900/20 flex items-center justify-center p-4 z-50"
69 onClick={props.onClose}
70 >
71 <div
72 class="bg-white rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-y-auto"
73 onClick={(e) => e.stopPropagation()}
74 >
75 <div class="sticky top-0 bg-white border-b border-gray-200 p-6">
76 <div class="flex justify-between items-start">
77 <div class="flex-1">
78 <Show
79 when={!isEditing()}
80 fallback={
81 <div class="space-y-3">
82 <input
83 type="text"
84 value={editTitle()}
85 onInput={(e) => setEditTitle(e.currentTarget.value)}
86 class="w-full text-3xl font-bold text-gray-900 border-b-2 border-blue-500 focus:outline-none"
87 placeholder="Title"
88 />
89 </div>
90 }
91 >
92 <h2 class="text-3xl font-bold text-gray-900 mb-2">
93 {props.blog?.title}
94 </h2>
95 </Show>
96 <div class="flex items-center gap-2 text-sm text-gray-600 mt-2">
97 <span class="font-medium">{props.blog?.patientName}</span>
98 <span>•</span>
99 <span>{formatDate(props.blog?.dateOfPost || "")}</span>
100 </div>
101 </div>
102 <div class="flex items-center gap-2 ml-4">
103 <Show when={isOwner() && !isEditing()}>
104 <button
105 onClick={startEdit}
106 class="text-gray-400 hover:text-blue-600 transition-colors cursor-pointer"
107 title="Edit blog"
108 >
109 <svg
110 class="w-5 h-5"
111 fill="none"
112 stroke="currentColor"
113 viewBox="0 0 24 24"
114 >
115 <path
116 stroke-linecap="round"
117 stroke-linejoin="round"
118 stroke-width="2"
119 d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
120 />
121 </svg>
122 </button>
123 <button
124 onClick={handleDelete}
125 class="text-gray-400 hover:text-red-600 transition-colors cursor-pointer"
126 title="Delete blog"
127 >
128 <svg
129 class="w-5 h-5"
130 fill="none"
131 stroke="currentColor"
132 viewBox="0 0 24 24"
133 >
134 <path
135 stroke-linecap="round"
136 stroke-linejoin="round"
137 stroke-width="2"
138 d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
139 />
140 </svg>
141 </button>
142 </Show>
143 <button
144 onClick={props.onClose}
145 class="text-gray-400 hover:text-gray-600 transition-colors cursor-pointer"
146 >
147 <svg
148 class="w-6 h-6"
149 fill="none"
150 stroke="currentColor"
151 viewBox="0 0 24 24"
152 >
153 <path
154 stroke-linecap="round"
155 stroke-linejoin="round"
156 stroke-width="2"
157 d="M6 18L18 6M6 6l12 12"
158 />
159 </svg>
160 </button>
161 </div>
162 </div>
163 </div>
164
165 <div class="p-6">
166 <Show
167 when={!isEditing()}
168 fallback={
169 <div class="space-y-4 mb-6">
170 <textarea
171 value={editContent()}
172 onInput={(e) => setEditContent(e.currentTarget.value)}
173 class="w-full min-h-50 p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
174 placeholder="Write your blog content..."
175 />
176 <div class="flex gap-2">
177 <button
178 onClick={saveEdit}
179 class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors cursor-pointer"
180 >
181 Save Changes
182 </button>
183 <button
184 onClick={cancelEdit}
185 class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors cursor-pointer"
186 >
187 Cancel
188 </button>
189 </div>
190 </div>
191 }
192 >
193 <p class="text-gray-700 text-lg mb-6 whitespace-pre-wrap">
194 {props.blog?.content}
195 </p>
196 </Show>
197
198 <div class="flex items-center gap-4 pb-6 mb-6 border-b border-gray-200">
199 <button
200 onClick={() => {
201 if (props.blog) {
202 props.onLike(props.blog.idBlog);
203 }
204 }}
205 class={`flex items-center gap-2 px-3 py-1 rounded-md transition-colors cursor-pointer ${
206 props.blog?.likedByCurrentUser
207 ? "bg-blue-100 text-blue-700"
208 : "bg-gray-100 text-gray-700 hover:bg-gray-200"
209 }`}
210 >
211 <svg
212 class="w-5 h-5"
213 fill={props.blog?.likedByCurrentUser ? "currentColor" : "none"}
214 stroke="currentColor"
215 viewBox="0 0 24 24"
216 >
217 <path
218 stroke-linecap="round"
219 stroke-linejoin="round"
220 stroke-width="2"
221 d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
222 />
223 </svg>
224 <span class="font-medium">{props.blog?.likesCount}</span>
225 </button>
226 </div>
227
228 <CommentSection
229 comments={props.blog?.comments || []}
230 commentsCount={props.blog?.commentsCount || 0}
231 onAddComment={props.onAddComment}
232 onUpdateComment={props.onUpdateComment}
233 onDeleteComment={props.onDeleteComment}
234 />
235 </div>
236 </div>
237 </div>
238 );
239};
240
241export default BlogModal;
Note: See TracBrowser for help on using the repository browser.