From 8110a63895792c36e5ac6d5ce70630c06f1cb516 Mon Sep 17 00:00:00 2001 From: Sawan Date: Sat, 2 Nov 2024 20:25:32 +0530 Subject: [PATCH] Added feature to post the blog from draft also filter the blog according to category --- backend/controllers/addBlogController.js | 29 ++- frontend/src/pages/BloggerProfile.js | 261 +++++++++++++---------- frontend/src/pages/Blogs.js | 54 +++-- frontend/src/styles/output.css | 101 ++++----- 4 files changed, 247 insertions(+), 198 deletions(-) diff --git a/backend/controllers/addBlogController.js b/backend/controllers/addBlogController.js index 9d8c2400..232f2fcf 100644 --- a/backend/controllers/addBlogController.js +++ b/backend/controllers/addBlogController.js @@ -1,6 +1,7 @@ import BlogPost from '../models/addBlog.js'; import multer from 'multer'; import path from 'path'; +import fs from 'fs'; // Configure multer for file uploads const storage = multer.diskStorage({ @@ -19,11 +20,28 @@ const upload = multer({ storage: storage }); // Function to save a new blog post export async function saveBlog(req, res) { try { - const { title, category, summary, excerpt, tags, publish } = req.body; - const featuredImage = req.file ? req.file.path : null; // Assuming `req.file` is set if an image is uploaded + const { title, category, summary, excerpt, tags, publish, featuredImage } = req.body; + + // If an image is uploaded, use its path + let imagePath = req.file ? req.file.path : null; + + // Handle base64 image if provided + if (featuredImage && featuredImage.startsWith('data:image/')) { + // Extract the base64 part of the image data + const base64Image = featuredImage.split(';base64,').pop(); + // Convert base64 string to Buffer + const buffer = Buffer.from(base64Image, 'base64'); + + // Create a path to save the image + const imagePathFromBase64 = `uploads/${Date.now()}.png`; // Use a unique name based on timestamp + + // Save the image buffer to a file + await fs.promises.writeFile(imagePathFromBase64, buffer); + imagePath = imagePathFromBase64; // Set imagePath to the saved file path + } + + // Log request body and file info - console.log(req.body); - console.log(req.file); // Check if required fields are provided if (!title || !category || !summary || !excerpt) { @@ -38,7 +56,7 @@ export async function saveBlog(req, res) { excerpt, tags, publish, - featuredImage + featuredImage: imagePath // Use the determined image path }); console.log(newBlog); @@ -57,6 +75,7 @@ export async function saveBlog(req, res) { } } + // Function to retrieve all blog posts export async function getAllBlog(req, res) { try { diff --git a/frontend/src/pages/BloggerProfile.js b/frontend/src/pages/BloggerProfile.js index a63841d4..a4ef00dd 100644 --- a/frontend/src/pages/BloggerProfile.js +++ b/frontend/src/pages/BloggerProfile.js @@ -1,4 +1,3 @@ - function getQueryParams() { const params = new URLSearchParams(window.location.search); return { @@ -12,7 +11,12 @@ import signOut from "./Login"; const userDetails = getQueryParams(); +function loadDrafts() { + const storedDrafts = localStorage.getItem('drafts'); + return storedDrafts ? JSON.parse(storedDrafts) : []; +} +let drafts = loadDrafts(); export function renderProfilePage(container) { container.innerHTML = ` @@ -22,160 +26,193 @@ export function renderProfilePage(container) {

Draft a New Blog

Create and save your blog drafts below.

- - - - - - - + + + - - - - - + + +
- -
- - Profile -
-

- ${userDetails.name} -

-

${userDetails.email}

-
+
+ + Profile +

${userDetails.name}

+

${userDetails.email}

- -
- -
+

My Drafts

    `; + document.getElementById('logout-btn').addEventListener('click', () => { - signOut() + signOut(); window.location.href = '/'; }); - // Sample Data (Simulating State) - let drafts = []; // Save draft functionality - const saveDraftButton = container.querySelector('#save-draft-btn'); - saveDraftButton.addEventListener('click', () => { + container.querySelector('#save-draft-btn').addEventListener('click', () => { const draftTitle = container.querySelector('#draft-title').value.trim(); - const draftDetails = container.querySelector('#draft-details').value.trim(); + const draftContent = container.querySelector('#draft-content').value.trim(); + const draftSummary = container.querySelector('#draft-summary').value.trim(); + const draftTags = container.querySelector('#draft-tags').value.trim(); const draftCategory = container.querySelector('#draft-category').value; const draftImageInput = container.querySelector('#draft-image'); - const draftImage = draftImageInput.files[0]; // Get the selected file + const draftImage = draftImageInput.files[0]; - if (draftTitle && draftDetails && draftCategory && draftImage) { + if (draftTitle && draftContent && draftCategory && draftImage) { const reader = new FileReader(); reader.onload = function (e) { - drafts.push({ + const newDraft = { title: draftTitle, - details: draftDetails, + excerpt: draftContent, // Keeping excerpt as intended + summary: draftSummary, category: draftCategory, - image: e.target.result, // Save the image as a data URL + tags: draftTags, + image: e.target.result, date: new Date() - }); - renderDrafts(); - container.querySelector('#draft-title').value = ''; - container.querySelector('#draft-details').value = ''; - container.querySelector('#draft-category').value = ''; // Reset category - draftImageInput.value = ''; // Reset the file input + }; + + drafts.push(newDraft); + localStorage.setItem('drafts', JSON.stringify(drafts)); + renderDrafts(container); + + // Reset the input fields + resetDraftForm(container); }; - reader.readAsDataURL(draftImage); // Convert image to data URL + reader.readAsDataURL(draftImage); } }); + + const handlePostingDraft = async (event) => { + const index = event.target.dataset.index; + const postDraftData = drafts[index]; + + const formData = new FormData(); + formData.append('title', postDraftData.title); + formData.append('category', postDraftData.category); + formData.append('summary', postDraftData.summary); + formData.append('excerpt', postDraftData.excerpt); + formData.append('tags', postDraftData.tags); + formData.append('publish', true); + + // Ensure the key name matches the backend expectation + if (postDraftData.image) { // Check with correct key name + formData.append('featuredImage', postDraftData.image); + } + + console.log(postDraftData); + + try { + const response = await fetch('http://localhost:5000/api/addBlog/saveBlog', { + method: 'POST', + body: formData + }); + + if (response.ok) { + const result = await response.json(); + console.log('Blog post created:', result); + alert('Blog post successfully created!'); + + // Remove the draft and update localStorage + drafts.splice(index, 1); + localStorage.setItem('drafts', JSON.stringify(drafts)); + + // Redirect to blogs page + window.location.href = '/blogs'; + } else { + console.error('Error creating blog post:', response.statusText); + alert('Failed to create blog post. Please try again.'); + } + } catch (error) { + console.error('Error:', error); + alert('An error occurred. Please try again.'); + } + }; + // Render Drafts - // Render Drafts - function renderDrafts() { + renderDrafts(container) + + function renderDrafts(container) { const draftList = container.querySelector('#draft-list'); + draftList.innerHTML = drafts.length - ? drafts.map((draft, index) => - `
    - ${draft.title} -
    -

    - ${draft.title} -

    -

    ${draft.details}

    -
    - - - - - - - - ${draft.category} + ? drafts.map((draft, index) => ` +
    + ${draft.title} +
    +

    + ${draft.title} +

    +

    ${draft.excerpt}

    +
    + + ${draft.category} + ${draft.tags} +
    -
    - -
    +
    + + + +
    + `).join('') - : `

    No drafts available. Create your first draft!

    `; - - // Attach edit event listeners - const editButtons = draftList.querySelectorAll('.edit-btn'); - editButtons.forEach(button => { - button.addEventListener('click', (event) => { - event.preventDefault(); // Prevent default link behavior + : '

    No drafts available.

    '; - const draftIndex = event.target.dataset.index; // Get the index of the draft to edit - const draftToEdit = drafts[draftIndex]; // Get the draft object + draftList.querySelectorAll('.edit-btn').forEach(btn => { + btn.addEventListener('click', handleEditDraft); + }); - // Fill the form with draft details - container.querySelector('#draft-title').value = draftToEdit.title; - container.querySelector('#draft-details').value = draftToEdit.details; - container.querySelector('#draft-category').value = draftToEdit.category; + draftList.querySelectorAll('.delete-btn').forEach(btn => { + btn.addEventListener('click', handleDeleteDraft); + }); - // Remove the draft from the drafts array - drafts.splice(draftIndex, 1); - renderDrafts(); // Re-render the drafts list - }); + draftList.querySelectorAll('.post-btn').forEach(btn => { + btn.addEventListener('click', handlePostingDraft); }); } + function handleEditDraft(event) { + const index = event.target.dataset.index; + const draft = drafts[index]; + container.querySelector('#draft-title').value = draft.title; + container.querySelector('#draft-summary').value = draft.summary; + container.querySelector('#draft-content').value = draft.excerpt; + container.querySelector('#draft-category').value = draft.category; + container.querySelector('#draft-tags').value = draft.tags; + // Handle the image upload for the draft as needed + // You can set the draft image if you have a preview option + } + function handleDeleteDraft(event) { + const index = event.target.dataset.index; + drafts.splice(index, 1); + localStorage.setItem('drafts', JSON.stringify(drafts)); + renderDrafts(container); + } - + function resetDraftForm(container) { + container.querySelector('#draft-title').value = ''; + container.querySelector('#draft-summary').value = ''; + container.querySelector('#draft-content').value = ''; + container.querySelector('#draft-category').value = ''; + container.querySelector('#draft-tags').value = ''; + container.querySelector('#draft-image').value = ''; + } } diff --git a/frontend/src/pages/Blogs.js b/frontend/src/pages/Blogs.js index 17673b5b..69e97281 100644 --- a/frontend/src/pages/Blogs.js +++ b/frontend/src/pages/Blogs.js @@ -11,8 +11,6 @@ const fetchBlogData = async () => { } }; - - export async function renderBlogs(container) { try { const blogPosts = await fetchBlogData(); // Fetch the blog data @@ -41,15 +39,16 @@ export async function renderBlogs(container) { `; - // Set up the search functionality + // Set up the search and category filtering functionality setupSearch(blogPosts); + setupCategoryFilter(blogPosts); } catch (error) { console.log('Error rendering blogs:', error); container.innerHTML = '

    Error loading blog posts. Please try again later.

    '; @@ -70,13 +69,29 @@ function setupSearch(blogPosts) { }); } +function setupCategoryFilter(blogPosts) { + const categoryLinks = document.querySelectorAll('.category-link'); + categoryLinks.forEach(link => { + link.addEventListener('click', function (event) { + event.preventDefault(); // Prevent default anchor click behavior + const selectedCategory = this.dataset.category; + const filteredPosts = selectedCategory === 'all' + ? blogPosts + : blogPosts.filter(post => post.category === selectedCategory); // Filter by category + + const blogPostsContainer = document.getElementById('blog-posts-container'); + blogPostsContainer.innerHTML = `
    ${renderBlogPosts(filteredPosts)}
    `; + }); + }); +} + function renderBlogPosts(posts) { return posts.map(post => renderBlogPost(post.id, post.title, post.excerpt, post.date, post.tags, post.featuredImage, post.publish)).join(''); } function renderBlogPost(id, title, excerpt, date, tags, imageUrl, publish) { - if (!publish) return; - let imagePath = " "; + if (!publish) return ''; + let imagePath = ""; if (imageUrl) { imagePath = `http://localhost:5000/${imageUrl.replace(/\\/g, '/')}`; } @@ -89,14 +104,8 @@ function renderBlogPost(id, title, excerpt, date, tags, imageUrl, publish) {

    ${excerpt}

    - - - - - - - ${tags} + ${tags} Read More @@ -123,22 +132,21 @@ function renderSearchWidget() { `; } -function renderCategoriesWidget() { - // Your categories widget code here - return `
    +function renderCategoriesWidget(blogPosts) { + const categories = Array.from(new Set(blogPosts.map(post => post.category))).sort(); + return ` +

    Categories

    `; } function renderRecentPostsWidget() { - // Your recent posts widget code here return `

    Recent Posts

      @@ -155,4 +163,4 @@ function formatDate(date) { month: 'long', day: 'numeric' }); -} \ No newline at end of file +} diff --git a/frontend/src/styles/output.css b/frontend/src/styles/output.css index 05892709..abf5b9b3 100644 --- a/frontend/src/styles/output.css +++ b/frontend/src/styles/output.css @@ -608,8 +608,8 @@ video { position: relative; } -.-top-\[17px\] { - top: -17px; +.-top-4 { + top: -1rem; } .left-3 { @@ -629,9 +629,9 @@ video { margin-right: auto; } -.my-8 { - margin-top: 2rem; - margin-bottom: 2rem; +.my-12 { + margin-top: 3rem; + margin-bottom: 3rem; } .mb-12 { @@ -799,10 +799,6 @@ video { max-width: 1280px; } -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .cursor-pointer { cursor: pointer; } @@ -947,10 +943,6 @@ video { border-top-width: 1px; } -.border-t-2 { - border-top-width: 2px; -} - .border-none { border-style: none; } @@ -975,11 +967,6 @@ video { border-color: rgb(209 213 219 / var(--tw-border-opacity)); } -.border-gray-500 { - --tw-border-opacity: 1; - border-color: rgb(107 114 128 / var(--tw-border-opacity)); -} - .border-gray-700 { --tw-border-opacity: 1; border-color: rgb(55 65 81 / var(--tw-border-opacity)); @@ -1019,6 +1006,11 @@ video { background-color: rgb(243 244 246 / var(--tw-bg-opacity)); } +.bg-gray-300 { + --tw-bg-opacity: 1; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)); +} + .bg-gray-50 { --tw-bg-opacity: 1; background-color: rgb(249 250 251 / var(--tw-bg-opacity)); @@ -1310,6 +1302,11 @@ video { color: rgb(239 68 68 / var(--tw-text-opacity)); } +.text-red-600 { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} + .text-red-700 { --tw-text-opacity: 1; color: rgb(185 28 28 / var(--tw-text-opacity)); @@ -1335,6 +1332,11 @@ video { color: rgb(133 77 14 / var(--tw-text-opacity)); } +.text-green-600 { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + .shadow-lg { --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -1375,24 +1377,12 @@ video { transition-duration: 150ms; } -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - .transition-colors { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; } -.transition-transform { - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - .duration-150 { transition-duration: 150ms; } @@ -1428,17 +1418,6 @@ body.dark-mode { padding: 20px; } -.hover\:-translate-y-1:hover { - --tw-translate-y: -0.25rem; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.hover\:scale-105:hover { - --tw-scale-x: 1.05; - --tw-scale-y: 1.05; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .hover\:bg-\[\#753ff1\]:hover { --tw-bg-opacity: 1; background-color: rgb(117 63 241 / var(--tw-bg-opacity)); @@ -1464,6 +1443,11 @@ body.dark-mode { background-color: rgb(67 56 202 / var(--tw-bg-opacity)); } +.hover\:bg-red-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); +} + .hover\:bg-red-600:hover { --tw-bg-opacity: 1; background-color: rgb(220 38 38 / var(--tw-bg-opacity)); @@ -1479,9 +1463,9 @@ body.dark-mode { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } -.hover\:text-blue-600:hover { - --tw-text-opacity: 1; - color: rgb(37 99 235 / var(--tw-text-opacity)); +.hover\:bg-gray-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); } .hover\:text-gray-900:hover { @@ -1503,12 +1487,6 @@ body.dark-mode { text-decoration-line: underline; } -.hover\:shadow-xl:hover { - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - .hover\:shadow-gray-300:hover { --tw-shadow-color: #d1d5db; --tw-shadow: var(--tw-shadow-colored); @@ -1529,10 +1507,6 @@ body.dark-mode { border-color: rgb(99 102 241 / var(--tw-border-opacity)); } -.focus\:border-transparent:focus { - border-color: transparent; -} - .focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; @@ -1613,6 +1587,11 @@ body.dark-mode { background-color: rgb(30 58 138 / var(--tw-bg-opacity)); } +.dark\:bg-gray-600:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)); +} + .dark\:bg-gray-700:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(55 65 81 / var(--tw-bg-opacity)); @@ -1753,6 +1732,16 @@ body.dark-mode { background-color: rgb(79 70 229 / var(--tw-bg-opacity)); } +.dark\:hover\:bg-red-600:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} + +.dark\:hover\:bg-green-600:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(22 163 74 / var(--tw-bg-opacity)); +} + .dark\:hover\:text-indigo-500:hover:is(.dark *) { --tw-text-opacity: 1; color: rgb(99 102 241 / var(--tw-text-opacity)); @@ -1778,10 +1767,6 @@ body.dark-mode { display: inline; } - .sm\:w-auto { - width: auto; - } - .sm\:flex-row { flex-direction: row; }