diff --git a/app/(defaults)/(blog)/blog/page.tsx b/app/(defaults)/(blog)/blog/page.tsx new file mode 100644 index 0000000..3334644 --- /dev/null +++ b/app/(defaults)/(blog)/blog/page.tsx @@ -0,0 +1,104 @@ +import { Metadata } from 'next'; +import Link from 'next/link'; +import React from 'react'; + +export const metadata: Metadata = { + title: 'Blog', +}; + +const blogs = [ + { + id: 1, + image: '/assets/images/blog/image-1.jpg', + title: 'Excessive sugar is harmful', + description: 'Sugar consumption can have serious effects on your health if taken in excess. Learn how to reduce it.', + author: 'Alma Clark', + profile: '/assets/images/profile-1.jpeg', + date: '06 May', + slug: '/blog/1', + }, + { + id: 2, + image: '/assets/images/blog/image-1.jpg', + title: 'Creative Photography', + description: 'Photography is not just about capturing pictures, but emotions and stories through your lens.', + author: 'Alma Clark', + profile: '/assets/images/profile-2.jpeg', + date: '06 May', + slug: '/blog/2', + }, + { + id: 3, + image: '/assets/images/blog/image-1.jpg', + title: 'Plan your next trip', + description: 'Traveling helps you explore new cultures, food, and make memories that last a lifetime.', + author: 'Alma Clark', + profile: '/assets/images/profile-3.jpeg', + date: '06 May', + slug: '/blog/3', + }, + { + id: 4, + image: '/assets/images/blog/image-1.jpg', + title: 'My latest Vlog', + description: 'Check out my latest vlog where I share behind-the-scenes of my daily life and adventures.', + author: 'Alma Clark', + profile: '/assets/images/profile-4.jpeg', + date: '06 May', + slug: '/blog/4', + }, +]; + +const Blog = () => { + return ( +
+

Blogs

+
+ {/* ✅ First box: Create New Blog */} + +
+
+
+
Create New Blog
+
+ + {blogs.map((blog) => ( +
+
+ {blog.title} +
+ + {/* ✅ Description first, then Title */} +
{blog.title}
+

{blog.description}

+ + {/* +
+
+ {blog.author} +
+
+

{blog.author}

+

{blog.date}

+
+
*/} + + {/* ✅ Read More button */} +
+ + Read More + +
+
+ ))} +
+
+ ); +}; + +export default Blog; diff --git a/app/(defaults)/(blog)/create-blog/page.tsx b/app/(defaults)/(blog)/create-blog/page.tsx new file mode 100644 index 0000000..a2cbd80 --- /dev/null +++ b/app/(defaults)/(blog)/create-blog/page.tsx @@ -0,0 +1,196 @@ +"use client"; +import IconTrashLines from "@/components/icon/icon-trash-lines"; +import React, { useState } from "react"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; + +// Custom toolbar options +const modules = { + toolbar: { + container: [ + [{ header: [1, 2, 3, false] }], + ["bold", "italic", "underline", "strike"], + [{ list: "ordered" }, { list: "bullet" }], + ["blockquote", "code-block"], + [{ align: [] }], + ["link", "image", "video"], + ["clean"], + ], + handlers: { + image: function () { + const input = document.createElement("input"); + input.setAttribute("type", "file"); + input.setAttribute("accept", "image/*"); + input.click(); + + input.onchange = async () => { + const file = input.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = () => { + const quill = this.quill; + const range = quill.getSelection(); + quill.insertEmbed(range.index, "image", reader.result); + }; + reader.readAsDataURL(file); + } + }; + }, + }, + }, +}; + +const formats = [ + "header", + "bold", "italic", "underline", "strike", + "blockquote", "code-block", + "list", "bullet", + "align", + "link", "image", "video", +]; + +const PostForm = () => { + const [formData, setFormData] = useState({ + title: "", + slug: "", + coverImage: null as File | null, + description: "", + }); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] || null; + setFormData((prev) => ({ + ...prev, + coverImage: file, + })); + }; + + const handleRemoveImage = () => { + setFormData((prev) => ({ + ...prev, + coverImage: null, + })); + }; + + const handleDescriptionChange = (value: string) => { + setFormData((prev) => ({ + ...prev, + description: value, + })); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + const data = new FormData(); + data.append("title", formData.title); + data.append("slug", formData.slug); + if (formData.coverImage) { + data.append("coverImage", formData.coverImage); + } + data.append("description", formData.description); + + console.log("FormData prepared:", { + title: formData.title, + slug: formData.slug, + coverImage: formData.coverImage?.name, + description: formData.description, + }); + }; + + return ( +
+

Create Blog

+ {/* Blog Title */} +
+ + +
+ + {/* Slug */} +
+ + +
+ + {/* Cover Image Upload */} +
+ + +
+ + {/* Show Image Preview */} + {formData.coverImage && ( +
+

Preview:

+
+ Selected + +
+
+ )} + + {/* Description (ReactQuill) */} +
+ +
+ + {/* Submit Button */} + +
+ ); +}; + +export default PostForm; diff --git a/components/layouts/sidebar.tsx b/components/layouts/sidebar.tsx index 6c59c0e..4547081 100644 --- a/components/layouts/sidebar.tsx +++ b/components/layouts/sidebar.tsx @@ -249,12 +249,36 @@ const Sidebar = () => { - {/*

+

- {t('user_interface')} + {t('blog')}

-
  • + + + +
      +
    • + {t('list')} +
    • +
    • + {t('add')} +
    • + +
    +
    +
  • + + {/*
  • */} -{/* + {/*

    {t('user_and_pages')} diff --git a/package-lock.json b/package-lock.json index 96b17c2..62395a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "react-i18next": "^15.0.2", "react-perfect-scrollbar": "^1.5.8", "react-popper": "^2.3.0", + "react-quill": "^2.0.0", "react-redux": "^9.1.2", "sweetalert2": "^11.22.2", "typescript": "^5.3.3", @@ -1063,6 +1064,15 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "license": "MIT", + "dependencies": { + "parchment": "^1.1.2" + } + }, "node_modules/@types/react": { "version": "18.3.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.10.tgz", @@ -2041,6 +2051,15 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -3134,11 +3153,29 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -4815,6 +4852,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "license": "BSD-3-Clause" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5271,6 +5314,74 @@ } ] }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "license": "BSD-3-Clause", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "license": "MIT", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/quill-delta/node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quill/node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5364,6 +5475,21 @@ "react-dom": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "license": "MIT", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, "node_modules/react-redux": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", diff --git a/package.json b/package.json index f639360..63efa2f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "react-i18next": "^15.0.2", "react-perfect-scrollbar": "^1.5.8", "react-popper": "^2.3.0", + "react-quill": "^2.0.0", "react-redux": "^9.1.2", "sweetalert2": "^11.22.2", "typescript": "^5.3.3", diff --git a/public/assets/images/blog/image-1.jpg b/public/assets/images/blog/image-1.jpg new file mode 100644 index 0000000..ab2ae53 Binary files /dev/null and b/public/assets/images/blog/image-1.jpg differ