From a382f7f36a47a45058f730c0b3ce5109e879c616 Mon Sep 17 00:00:00 2001 From: lina elman Date: Mon, 2 Jun 2025 18:34:28 +0300 Subject: [PATCH 1/6] added changes to feed page, changed new-post to be a popup --- nextstep-frontend/src/App.tsx | 1 - nextstep-frontend/src/pages/Feed.tsx | 65 ++++++++++++++++--- nextstep-frontend/src/pages/NewPost.tsx | 84 ++++++++++--------------- 3 files changed, 89 insertions(+), 61 deletions(-) diff --git a/nextstep-frontend/src/App.tsx b/nextstep-frontend/src/App.tsx index f9f548c..fec3af9 100644 --- a/nextstep-frontend/src/App.tsx +++ b/nextstep-frontend/src/App.tsx @@ -74,7 +74,6 @@ const App: React.FC = () => { } /> } /> - } /> } /> } /> } /> diff --git a/nextstep-frontend/src/pages/Feed.tsx b/nextstep-frontend/src/pages/Feed.tsx index 6ba9901..3c37266 100644 --- a/nextstep-frontend/src/pages/Feed.tsx +++ b/nextstep-frontend/src/pages/Feed.tsx @@ -26,6 +26,7 @@ import { Post } from "../models/Post.tsx"; import api from "../serverApi.ts"; import {getUserAuth} from "../handlers/userAuth.ts"; import defaultProfileImage from '../../assets/defaultProfileImage.jpg'; // Import the default profile image +import NewPostModal from './NewPost'; const Feed: React.FC = () => { @@ -42,10 +43,12 @@ const Feed: React.FC = () => { const [profileImages, setProfileImages] = useState<{ [key: string]: string }>({}); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); + const [showNewPostModal, setShowNewPostModal] = useState(false); + const auth = getUserAuth(); const handleCreatePost = () => { - navigate('/new-post'); + setShowNewPostModal(true); }; const handleDeletePost = async () => { @@ -197,10 +200,46 @@ const Feed: React.FC = () => { - - + + + + What's on your mind{', ' + auth.username || 'User'}? + + + + + + + + + + { color="primary" /> } - label="Show My Posts" - /> - + label="Show only my posts" + /> + setShowNewPostModal(false)} + onPostCreated={() => loadPosts(currentPage)} + /> {isLoading ? ( @@ -220,11 +263,12 @@ const Feed: React.FC = () => { {error} ) : ( <> - + {posts.map((post) => ( { )} - + { totalPages > 0 && + } diff --git a/nextstep-frontend/src/pages/NewPost.tsx b/nextstep-frontend/src/pages/NewPost.tsx index 176b4b7..5c7d41b 100644 --- a/nextstep-frontend/src/pages/NewPost.tsx +++ b/nextstep-frontend/src/pages/NewPost.tsx @@ -1,27 +1,37 @@ import React, { useState } from 'react'; -import { Container, Button, Typography, Box } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; -import { config } from '../config'; +import { + Button, + Typography, + Container, + Modal, +} from '@mui/material'; import FroalaEditor from 'react-froala-wysiwyg'; import 'froala-editor/css/froala_style.min.css'; import 'froala-editor/css/froala_editor.pkgd.min.css'; import 'froala-editor/js/plugins/image.min.js'; +import { config } from '../config'; import api from "../serverApi.ts"; import { getUserAuth } from '../handlers/userAuth.ts'; -const NewPost: React.FC = () => { + +type Props = { + open: boolean; + onClose: () => void; + onPostCreated?: () => void; +}; + +const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); - const [images, setImages] = useState([]); // Store images locally - const navigate = useNavigate(); + const [images, setImages] = useState([]); const auth = getUserAuth(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { - // Upload images to the server const uploadedImages: { [placeholder: string]: string } = {}; + for (const image of images) { const formData = new FormData(); formData.append('file', image); @@ -33,36 +43,32 @@ const NewPost: React.FC = () => { }, }); - // Map the placeholder to the actual URL const imageUrl = `${config.app.backend_url()}/resources/images/${response.data}`; uploadedImages[image.name] = imageUrl; } - // Replace placeholders in the content with actual URLs let updatedContent = content; Object.keys(uploadedImages).forEach((placeholder) => { updatedContent = updatedContent.replace(placeholder, uploadedImages[placeholder]); }); - // Submit the post with the updated content await api.post(`/post`, { title, content: updatedContent, }); - navigate('/feed'); // Redirect to feed after successful post creation + onClose(); + onPostCreated?.(); // Call callback to reload posts in Feed } catch (error) { console.error('Error creating post:', error); } }; return ( - - - - Create New Post - -
+ + + Create New Post + { onModelChange={setContent} config={{ placeholderText: "Edit Your Content Here!", - charCounterCount: false, - toolbarButtons: [ - "bold", - "italic", - "underline", - "insertImage", - "insertLink", - "paragraphFormat", - "alert", - ], - imageUploadRemoteUrls: true, + toolbarButtons: ["bold", "italic", "underline", "insertImage", "insertLink", "paragraphFormat"], + pluginsEnabled: ["image"], imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif'], events: { - // Custom image upload handling - "image.beforeUpload": async function (fileList: File[]) { + "image.beforeUpload": function (fileList: File[]) { const editor = this as any; const firstFile = fileList[0]; if (firstFile) { - // Generate a placeholder for the image const placeholder = `[[image-${firstFile.name}]]`; - - // Insert the placeholder into the editor editor.image.insert(placeholder, null, null, editor.image.get()); - - // Store the image locally - setImages((prevImages) => [...prevImages, firstFile]); + setImages((prev) => [...prev, firstFile]); } - return false; // Prevent Froala's default upload mechanism + return false; }, }, - pluginsEnabled: ["image"], // Ensure image plugin is enabled }} /> - - -
-
+ + ); }; -export default NewPost; \ No newline at end of file +export default NewPostModal; From ee3a005d4ef8c8e55f4922b4ad96a348ecfb39a4 Mon Sep 17 00:00:00 2001 From: lina elman Date: Mon, 2 Jun 2025 19:09:16 +0300 Subject: [PATCH 2/6] trying to fix froala --- nextstep-frontend/src/App.tsx | 1 - .../src/{pages => components}/NewPost.tsx | 79 +++++++++++-------- nextstep-frontend/src/pages/Feed.tsx | 2 +- nextstep-frontend/src/pages/PostDetails.tsx | 2 +- 4 files changed, 46 insertions(+), 38 deletions(-) rename nextstep-frontend/src/{pages => components}/NewPost.tsx (59%) diff --git a/nextstep-frontend/src/App.tsx b/nextstep-frontend/src/App.tsx index fec3af9..9c411ef 100644 --- a/nextstep-frontend/src/App.tsx +++ b/nextstep-frontend/src/App.tsx @@ -7,7 +7,6 @@ import './App.css'; import Feed from './pages/Feed'; import Footer from './components/Footer'; import RequireAuth from './hoc/RequireAuth'; -import NewPost from './pages/NewPost'; import PostDetails from './pages/PostDetails'; import Chat from './pages/Chat'; import Resume from './pages/Resume'; diff --git a/nextstep-frontend/src/pages/NewPost.tsx b/nextstep-frontend/src/components/NewPost.tsx similarity index 59% rename from nextstep-frontend/src/pages/NewPost.tsx rename to nextstep-frontend/src/components/NewPost.tsx index 5c7d41b..0545361 100644 --- a/nextstep-frontend/src/pages/NewPost.tsx +++ b/nextstep-frontend/src/components/NewPost.tsx @@ -9,11 +9,10 @@ import FroalaEditor from 'react-froala-wysiwyg'; import 'froala-editor/css/froala_style.min.css'; import 'froala-editor/css/froala_editor.pkgd.min.css'; import 'froala-editor/js/plugins/image.min.js'; -import { config } from '../config'; +import { config } from '../config.ts'; import api from "../serverApi.ts"; import { getUserAuth } from '../handlers/userAuth.ts'; - type Props = { open: boolean; onClose: () => void; @@ -23,42 +22,23 @@ type Props = { const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); - const [images, setImages] = useState([]); const auth = getUserAuth(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { - const uploadedImages: { [placeholder: string]: string } = {}; - - for (const image of images) { - const formData = new FormData(); - formData.append('file', image); - - const response = await api.post(`/resource/image`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - 'Authorization': `Bearer ${auth.accessToken}`, - }, - }); - - const imageUrl = `${config.app.backend_url()}/resources/images/${response.data}`; - uploadedImages[image.name] = imageUrl; - } - - let updatedContent = content; - Object.keys(uploadedImages).forEach((placeholder) => { - updatedContent = updatedContent.replace(placeholder, uploadedImages[placeholder]); - }); - await api.post(`/post`, { title, - content: updatedContent, + content, + }, { + headers: { + Authorization: `Bearer ${auth.accessToken}`, + }, }); onClose(); - onPostCreated?.(); // Call callback to reload posts in Feed + onPostCreated?.(); // Refresh feed if needed } catch (error) { console.error('Error creating post:', error); } @@ -66,8 +46,21 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { return ( - - Create New Post + + + Create New Post +
= ({ open, onClose, onPostCreated }) => { model={content} onModelChange={setContent} config={{ + imageUpload: true, + imageUploadParam: 'file', + imageUploadMethod: 'POST', placeholderText: "Edit Your Content Here!", toolbarButtons: ["bold", "italic", "underline", "insertImage", "insertLink", "paragraphFormat"], pluginsEnabled: ["image"], imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif'], events: { - "image.beforeUpload": function (fileList: File[]) { + "image.beforeUpload": async function (files: File[]) { const editor = this as any; - const firstFile = fileList[0]; + const file = files[0]; + + if (!file) return false; + + try { + const formData = new FormData(); + formData.append('file', file); + + const response = await api.post(`/resource/image`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + 'Authorization': `Bearer ${auth.accessToken}`, + }, + }); - if (firstFile) { - const placeholder = `[[image-${firstFile.name}]]`; - editor.image.insert(placeholder, null, null, editor.image.get()); - setImages((prev) => [...prev, firstFile]); + const imageUrl = `${config.app.backend_url()}/resources/images/${response.data}`; + editor.image.insert(imageUrl, null, null, editor.image.get()); + } catch (err) { + console.error('Error uploading image:', err); } return false; diff --git a/nextstep-frontend/src/pages/Feed.tsx b/nextstep-frontend/src/pages/Feed.tsx index 3c37266..34e76ef 100644 --- a/nextstep-frontend/src/pages/Feed.tsx +++ b/nextstep-frontend/src/pages/Feed.tsx @@ -26,7 +26,7 @@ import { Post } from "../models/Post.tsx"; import api from "../serverApi.ts"; import {getUserAuth} from "../handlers/userAuth.ts"; import defaultProfileImage from '../../assets/defaultProfileImage.jpg'; // Import the default profile image -import NewPostModal from './NewPost'; +import NewPostModal from '../components/NewPost.tsx'; const Feed: React.FC = () => { diff --git a/nextstep-frontend/src/pages/PostDetails.tsx b/nextstep-frontend/src/pages/PostDetails.tsx index 02404b9..9d4cb4d 100644 --- a/nextstep-frontend/src/pages/PostDetails.tsx +++ b/nextstep-frontend/src/pages/PostDetails.tsx @@ -237,7 +237,7 @@ const PostDetails: React.FC = () => { {error} ) : ( post && ( - + Date: Mon, 2 Jun 2025 19:35:55 +0300 Subject: [PATCH 3/6] maybe working now --- nextstep-frontend/src/components/NewPost.tsx | 72 ++++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/nextstep-frontend/src/components/NewPost.tsx b/nextstep-frontend/src/components/NewPost.tsx index 0545361..1b36b19 100644 --- a/nextstep-frontend/src/components/NewPost.tsx +++ b/nextstep-frontend/src/components/NewPost.tsx @@ -4,6 +4,8 @@ import { Typography, Container, Modal, + Snackbar, + Alert, } from '@mui/material'; import FroalaEditor from 'react-froala-wysiwyg'; import 'froala-editor/css/froala_style.min.css'; @@ -22,15 +24,41 @@ type Props = { const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); + const [images, setImages] = useState([]); // Store images locally const auth = getUserAuth(); + const [error, setError] = useState(null); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + // Upload images to the server + const uploadedImages: { [placeholder: string]: string } = {}; + for (const image of images) { + const formData = new FormData(); + formData.append('file', image); + + const response = await api.post(`/resource/image`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + 'Authorization': `Bearer ${auth.accessToken}`, + }, + }); + + // Map the placeholder to the actual URL + const imageUrl = `${config.app.backend_url()}/resources/images/${response.data}`; + uploadedImages[image.name] = imageUrl; + } + + // Replace placeholders in the content with actual URLs + let updatedContent = content; + Object.keys(uploadedImages).forEach((placeholder) => { + updatedContent = updatedContent.replace(placeholder, uploadedImages[placeholder]); + }); + try { await api.post(`/post`, { title, - content, + content: updatedContent, }, { headers: { Authorization: `Bearer ${auth.accessToken}`, @@ -40,7 +68,7 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { onClose(); onPostCreated?.(); // Refresh feed if needed } catch (error) { - console.error('Error creating post:', error); + setError('Error creating post: ' + error); } }; @@ -78,35 +106,29 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { imageUpload: true, imageUploadParam: 'file', imageUploadMethod: 'POST', + charCounterCount: false, placeholderText: "Edit Your Content Here!", toolbarButtons: ["bold", "italic", "underline", "insertImage", "insertLink", "paragraphFormat"], pluginsEnabled: ["image"], + imageUploadRemoteUrls: true, imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif'], events: { - "image.beforeUpload": async function (files: File[]) { + "image.beforeUpload": async function (fileList: File[]) { const editor = this as any; - const file = files[0]; - - if (!file) return false; + const firstFile = fileList[0]; - try { - const formData = new FormData(); - formData.append('file', file); + if (firstFile) { + // Generate a placeholder for the image + const placeholder = `[[image-${firstFile.name}]]`; - const response = await api.post(`/resource/image`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - 'Authorization': `Bearer ${auth.accessToken}`, - }, - }); + // Insert the placeholder into the editor + editor.image.insert(placeholder, null, null, editor.image.get()); - const imageUrl = `${config.app.backend_url()}/resources/images/${response.data}`; - editor.image.insert(imageUrl, null, null, editor.image.get()); - } catch (err) { - console.error('Error uploading image:', err); + // Store the image locally + setImages((prevImages) => [...prevImages, firstFile]); } - return false; + return false; // Prevent Froala's default upload mechanism }, }, }} @@ -118,6 +140,16 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { Cancel + setError(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'center' }} + > + setError(null)}> + {error} + +
); From ec9e7c0d79ec86537ed27a2bdbd6f2aa0d76d900 Mon Sep 17 00:00:00 2001 From: Tal Jacob Date: Sat, 7 Jun 2025 12:09:27 +0300 Subject: [PATCH 4/6] Fixed Empty Space When Comments Are Collapsed Signed-off-by: Tal Jacob --- nextstep-frontend/src/pages/PostDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextstep-frontend/src/pages/PostDetails.tsx b/nextstep-frontend/src/pages/PostDetails.tsx index 2077994..51a0f91 100644 --- a/nextstep-frontend/src/pages/PostDetails.tsx +++ b/nextstep-frontend/src/pages/PostDetails.tsx @@ -237,7 +237,7 @@ const PostDetails: React.FC = () => { {error} ) : ( post && ( - + Date: Sat, 7 Jun 2025 12:51:39 +0300 Subject: [PATCH 5/6] Support Feed Whats On Your Mind Message In Dark Mode Signed-off-by: Tal Jacob --- nextstep-frontend/src/pages/Feed.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nextstep-frontend/src/pages/Feed.tsx b/nextstep-frontend/src/pages/Feed.tsx index 34e76ef..2c0b226 100644 --- a/nextstep-frontend/src/pages/Feed.tsx +++ b/nextstep-frontend/src/pages/Feed.tsx @@ -218,14 +218,14 @@ const Feed: React.FC = () => { - What's on your mind{', ' + auth.username || 'User'}? + What's on your mind{', ' + auth.username || 'User'}? From deebd495f88cf6fbdd22a8e1538d11a958143c00 Mon Sep 17 00:00:00 2001 From: Tal Jacob Date: Sat, 7 Jun 2025 12:53:21 +0300 Subject: [PATCH 6/6] Support Feed New Post Modal In Dark Mode Signed-off-by: Tal Jacob --- nextstep-frontend/src/components/NewPost.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/nextstep-frontend/src/components/NewPost.tsx b/nextstep-frontend/src/components/NewPost.tsx index 4c0f0ca..15a0dbe 100644 --- a/nextstep-frontend/src/components/NewPost.tsx +++ b/nextstep-frontend/src/components/NewPost.tsx @@ -50,15 +50,16 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { maxWidth="md" sx={{ mt: 10, - backgroundColor: 'white', + backgroundColor: 'background.paper', borderRadius: 2, p: 4, width: '40%', overflowY: 'auto', height: '80vh', + color: 'text.primary', }} > - + Create New Post
@@ -67,7 +68,17 @@ const NewPostModal: React.FC = ({ open, onClose, onPostCreated }) => { placeholder="Title" value={title} onChange={(e) => setTitle(e.target.value)} - style={{ width: '100%', marginBottom: '1rem', padding: '10px', fontSize: '16px' }} + style={{ + width: '100%', + marginBottom: '1rem', + padding: '10px', + fontSize: '16px', + backgroundColor: 'transparent', + color: 'inherit', + border: '1px solid', + borderColor: 'divider', + borderRadius: '4px', + }} required />