Added preliminary image support and improved functionality around application

This commit is contained in:
Barunes Padhy 2024-06-02 07:27:24 +03:00
parent 2d8cc1e7de
commit d450ac3cb4
14 changed files with 203 additions and 70 deletions

View File

@ -93,7 +93,6 @@ class BlogsByCategoryAPIView(APIView):
class BlogCreateAPIView(generics.CreateAPIView):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
lookup_field = 'blog_id'
class BlogUpdateAPIView(generics.RetrieveUpdateAPIView):
queryset = Blog.objects.all()
@ -105,16 +104,6 @@ class BlogRetrieveAPIView(generics.RetrieveAPIView):
serializer_class = BlogSerializer
lookup_field = 'blog_id'
class CategoryDetailView(APIView):
def get(self, request, category_id):
try:
category = Category.objects.get(category_id=category_id)
except Category.DoesNotExist:
return Response({'message': 'Category not found'}, status=404)
serializer = UnifiedCategoryBlogSerializer(category)
return Response(serializer.data)
class BlogDeleteAPIView(generics.DestroyAPIView):
queryset = Blog.objects.all()
serializer_class = BlogSerializer

View File

@ -45,8 +45,8 @@ urlpatterns = [
path('data/category/<slug:category_id>/', BlogsByCategoryAPIView.as_view(), name='blogs-by-category-view'),
path('data/category/update/<slug:category_id>/', CategoryUpdateAPIView.as_view(), name='category-update-view'),
path('data/category/delete/<slug:category_id>/', CategoryDeleteAPIView.as_view(), name='category-delete-view'),
path('data/blog/create/', BlogCreateAPIView.as_view(), name='blog-create-view'),
path('data/blog/<slug:blog_id>/', BlogRetrieveAPIView.as_view(), name='blog-retrieve-view'),
path('data/blog/create/<slug:blog_id>/', BlogCreateAPIView.as_view(), name='blog-create-view'),
path('data/blog/update/<slug:blog_id>/', BlogUpdateAPIView.as_view(), name='blog-update-view'),
path('data/blog/delete/<slug:blog_id>/', BlogDeleteAPIView.as_view(), name='blog-delete-view'),
]

View File

@ -14,7 +14,7 @@
"@tiptap/extension-blockquote": "^2.3.2",
"@tiptap/extension-color": "^2.3.2",
"@tiptap/extension-highlight": "^2.3.2",
"@tiptap/extension-image": "^2.3.2",
"@tiptap/extension-image": "^2.4.0",
"@tiptap/extension-link": "^2.3.2",
"@tiptap/extension-list-item": "^2.3.2",
"@tiptap/extension-text-align": "^2.3.2",
@ -25,8 +25,10 @@
"axios": "^1.6.8",
"bootstrap": "^5.3.3",
"html-react-parser": "^5.1.10",
"interactjs": "^1.10.27",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.22.3",
"reactstrap": "^9.2.2",
"sass": "^1.75.0",
@ -920,6 +922,11 @@
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"dev": true
},
"node_modules/@interactjs/types": {
"version": "1.10.27",
"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.27.tgz",
"integrity": "sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA=="
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
@ -1453,9 +1460,9 @@
}
},
"node_modules/@tiptap/extension-image": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.3.2.tgz",
"integrity": "sha512-otkhqToHnjjpWOIswuotfK/PTPEOhhKRFPf1NuXvqHpMNulz+J1uIuA9R/B1m+bXkxZzCMKkWQi50vjqH9idVg==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.4.0.tgz",
"integrity": "sha512-NIVhRPMO/ONo8OywEd+8zh0Q6Q7EbFHtBxVsvfOKj9KtZkaXQfUO4MzONTyptkvAchTpj9pIzeaEY5fyU87gFA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@ -2211,6 +2218,14 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -3501,6 +3516,14 @@
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz",
"integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g=="
},
"node_modules/interactjs": {
"version": "1.10.27",
"resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.27.tgz",
"integrity": "sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==",
"dependencies": {
"@interactjs/types": "1.10.27"
}
},
"node_modules/internal-slot": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
@ -4706,6 +4729,19 @@
"react": "^18.2.0"
}
},
"node_modules/react-draggable": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz",
"integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==",
"dependencies": {
"clsx": "^1.1.1",
"prop-types": "^15.8.1"
},
"peerDependencies": {
"react": ">= 16.3.0",
"react-dom": ">= 16.3.0"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
@ -4744,6 +4780,18 @@
"node": ">=0.10.0"
}
},
"node_modules/react-resizable": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.5.tgz",
"integrity": "sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==",
"dependencies": {
"prop-types": "15.x",
"react-draggable": "^4.0.3"
},
"peerDependencies": {
"react": ">= 16.3"
}
},
"node_modules/react-router": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",

View File

@ -19,7 +19,7 @@
"@tiptap/extension-blockquote": "^2.3.2",
"@tiptap/extension-color": "^2.3.2",
"@tiptap/extension-highlight": "^2.3.2",
"@tiptap/extension-image": "^2.3.2",
"@tiptap/extension-image": "^2.4.0",
"@tiptap/extension-link": "^2.3.2",
"@tiptap/extension-list-item": "^2.3.2",
"@tiptap/extension-text-align": "^2.3.2",
@ -30,8 +30,10 @@
"axios": "^1.6.8",
"bootstrap": "^5.3.3",
"html-react-parser": "^5.1.10",
"interactjs": "^1.10.27",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.22.3",
"reactstrap": "^9.2.2",
"sass": "^1.75.0",

View File

@ -11,56 +11,80 @@ import {
Card,
Row,
Col,
Button,
CardImg,
CardTitle,
CardText,
CardBody
} from 'reactstrap';
import { Link, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
function BlogList(props) {
const { categoryID } = useParams();
let navigate = useNavigate();
const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig;
const [categoryData, setCategoryData] = useState('loading');
const [currentPage, setCurrentPage] = useState('loading');
useEffect(() => {
const loadBlogs = () => {
EditableDataService.getData(`/data/category/${categoryID}/`).then(response => {
let responseData = response.data
let blogMetadata = []
console.log(responseData)
let localCategoryData = {
"id": responseData["category_id"],
"name": responseData["name"],
"coverImage": responseData["cover_image"],
"tagLine": responseData["tagline"],
"description": responseData["description"],
"featuredBlog": responseData["featured_id"],
"blogMetadata": responseData["blog_metadata"]
}
"id": responseData["category_id"],
"name": responseData["name"],
"coverImage": responseData["cover_image"],
"tagLine": responseData["tagline"],
"description": responseData["description"],
"featuredBlog": responseData["featured_id"],
"blogMetadata": responseData["blog_metadata"]
}
for (let eachBlog of responseData["blog_metadata"]){
blogMetadata.push({
"id": eachBlog["blog_id"],
"name": eachBlog["name"],
"description": eachBlog["description"],
"tagLine": eachBlog["tagline"],
"coverImage": eachBlog["cover_image"],
"parentCategory": eachBlog["parent_category"]
})
blogMetadata.push({
"id": eachBlog["blog_id"],
"name": eachBlog["name"],
"description": eachBlog["description"],
"tagLine": eachBlog["tagline"],
"coverImage": eachBlog["cover_image"],
"parentCategory": eachBlog["parent_category"]
})
}
localCategoryData.blogMetadata = blogMetadata
setCategoryData(localCategoryData)
}
);
);
}
useEffect(() => {
loadBlogs()
}, [categoryID]);
const addNewBlog = () => {
EditableDataService.createData(`/data/blog/create/`, {
"name": "Enter a blog name",
"description": "Enter a description",
"tagline": "Enter a tagline",
"cover_image": "",
"content_body": "<p></p>",
"parent_category": categoryID
}).then(response => {
props.notificationToggler("New blog created")
loadBlogs()
}).catch(error => {
props.notificationToggler('Failed to add a new blog', 'danger');
});
}
if (GlobalTheme && ThemeConfig) {
return (
<Container fluid className={`mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}>
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/`)} className="ms-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<CategoryBar currentPage={categoryID} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} />
<Row className="justify-content-center align-items-center">
<Col className="d-flex flex-column align-items-center">
@ -69,6 +93,7 @@ return (
<CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1">
{`Blogs in ${categoryData.name}`}
<Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewBlog()}>Add New</Button>
</CardTitle>
</CardBody>
</Card>

View File

@ -4,14 +4,20 @@ import EditableDataService from '../../services/editable-data-service';
import MediaService from '../../services/media-service'
import CategoryBar from './shared/category-bar';
import EditorComponent from './shared/tiptap';
import ModalComponent from './shared/modal-component';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody, Input, InputGroup, InputGroupText
} from 'reactstrap';
import { Link, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
function Blog(props) {
let navigate = useNavigate();
const nameField = useRef(null);
const descriptionField = useRef(null);
const tagLineField = useRef(null);
@ -23,6 +29,11 @@ function Blog(props) {
const [blogData, setBlogData] = useState([]);
const [blogContent, setBlogContent] = useState();
const [modal, setModal] = useState(false);
const [modalText, setModalText] = useState(false);
const [modalTitle, setModalTitle] = useState(false);
const toggle = () => setModal(!modal);
const setInfo = (event) => {
EditableDataService.updateData(`/data/blog/update/${blogID}/`,{
@ -38,6 +49,22 @@ function Blog(props) {
});
}
const showModal = () => {
setModalTitle('Confirm')
setModalText('Are you sure that you wish to delete this blog?')
toggle()
}
const deleteResource = () => {
EditableDataService.deleteData(`/data/blog/delete/${blogData.id}/`).then(response => {
props.notificationToggler('Blog successfully deleted')
navigate(`/categories/${blogData.parentCategory}`);
}).catch(error => {
props.notificationToggler('Failed to delete blog', 'danger');
});
toggle()
}
const getInfo = () => {
EditableDataService.getData(`/data/blog/${blogID}/`).then(response => {
let responseData = response.data
@ -61,19 +88,25 @@ function Blog(props) {
if (GlobalTheme && ThemeConfig && blogData) {
return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<ModalComponent modalText={modalText} modalTitle={modalTitle} modal={modal} toggle={toggle} confirmAction={deleteResource}/>
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/${blogData.parentCategory}`)} className="ms-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Row className="mb-4">
<Col className="p-0">
<img
src={MediaService.getMedia(blogData.coverImage)}
alt="Banner"
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>
{
blogData.coverImage !== "" ?
<img
src={MediaService.getMedia(blogData.coverImage)}
alt="Banner"
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>:""
}
</Col>
</Row>
<Row className="mr-2 ml-2 mb-2 mt-1 blogContent">
<Col xs="3" className="d-none d-md-block"></Col>
<Col xs={`${window.screen.width >= 765 ? '6':''}`}>
<Button color='danger' onClick={() => showModal()} className="mb-5">Delete Blog</Button>
<InputGroup className="mb-3">
<InputGroupText>
Name

View File

@ -19,9 +19,13 @@ import {
Button,
ButtonGroup
} from 'reactstrap';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
function Blogs(props) {
let navigate = useNavigate();
const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig;
@ -65,7 +69,7 @@ function Blogs(props) {
const deleteResource = (id) => {
EditableDataService.deleteData(`/data/category/delete/${id}/`).then(response => {
props.notificationToggler('Category delete successfully')
props.notificationToggler('Category successfully deleted')
setCategoryData()
}).catch(error => {
props.notificationToggler('Failed to delete category', 'danger');
@ -74,10 +78,10 @@ function Blogs(props) {
const addNewCategory = () => {
EditableDataService.createData('/data/category/create/', {
"name": "Enter a blog name",
"name": "Enter name",
"featured_blog": "",
"description": "Enter description",
"tagline": "Enter category tagline",
"tagline": "Enter tagline",
"cover_image": ""
}).then(response => {
props.notificationToggler('Category created successfully')
@ -99,9 +103,10 @@ function Blogs(props) {
setCategoryData()
}
if (GlobalTheme && ThemeConfig && categoryMetadata.length > 0) {
if (GlobalTheme && ThemeConfig) {
return (
<Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}>
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/`)} className="ms-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<Row className="justify-content-center align-items-center">
<Col className="d-flex flex-column align-items-center">
<div className="w-100">

View File

@ -116,7 +116,7 @@ function CardListViewer(props) {
<Link className={`${props.textColor}`} to={`/${props.resourceType}/${itemObject.id}`}>
Open this resource
</Link>
<Button color={props.buttonColor} onClick={() => showModal()} outline className="m-1">Delete</Button>
<Button color='danger' onClick={() => showModal()} className="m-2">Delete Blog</Button>
</CardText>
</CardBody>
</Card>
@ -128,13 +128,13 @@ function CardListViewer(props) {
{itemObject.coverImage !== "" ? <CardImg src={MediaService.getMedia(itemObject.coverImage)} style={{ "height": "180px", "objectFit": "cover" }} top width="100%" /> : ""}
<CardBody>
<Link to={`/${props.resourceType}/${itemObject.id}`}>
<CardTitle className={`${props.textColor}`} tag="h5">
<CardTitle className={`${props.textColor}`} tag="h3">
{itemObject.name}
</CardTitle>
<CardText className={`${props.textColor}`}>
<CardText className={`${props.textColor}`} tag="h5">
{itemObject.description}
</CardText>
<CardText>
<CardText tag="h6">
<small className={`${props.textColor}`}>
{itemObject.tagLine}
</small>

View File

@ -8,6 +8,7 @@ import Highlight from '@tiptap/extension-highlight'
import TextAlign from '@tiptap/extension-text-align'
import Underline from '@tiptap/extension-underline'
import Blockquote from '@tiptap/extension-blockquote'
import Image from '@tiptap/extension-image'
import Link from '@tiptap/extension-link'
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
@ -18,7 +19,7 @@ import { faBold, faItalic,
faAlignJustify, faHighlighter,
faStrikethrough, faCode,
faListUl, faLink,
faListOl, faQuoteLeft,
faListOl, faQuoteLeft,
faQuoteRight, faRulerHorizontal,
faRotateLeft, faRotateRight } from '@fortawesome/free-solid-svg-icons';
@ -328,6 +329,12 @@ const extensions = [
}),
Underline,
Blockquote,
Image.configure({
allowBase64: true,
HTMLAttributes: {
class: 'mx-auto d-block',
},
}),
TextAlign.configure({
types: ['heading', 'paragraph'],
}),

View File

@ -11,15 +11,18 @@ import {
Card,
Row,
Col,
CardImg,
Button,
CardTitle,
CardText,
CardBody
} from 'reactstrap';
import { Link, useParams } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
function BlogList(props) {
let navigate = useNavigate();
const { categoryID } = useParams();
const GlobalTheme = props.GlobalTheme;
@ -47,7 +50,8 @@ function BlogList(props) {
if (GlobalTheme && ThemeConfig) {
return (
<Container fluid className={` mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}>
<CategoryBar currentPage={categoryID} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<CategoryBar currentPage={categoryID} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Row className="justify-content-center align-items-center">
<Col className="d-flex flex-column align-items-center">
<div className="w-100">

View File

@ -8,10 +8,13 @@ import CategoryBar from './shared/category-bar';
import {
Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody
} from 'reactstrap';
import { Link, useParams } from 'react-router-dom';
import { Link, useParams, useNavigate } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
function Blog(props) {
let navigate = useNavigate();
const { blogID } = useParams();
const GlobalTheme = props.GlobalTheme;
@ -56,14 +59,18 @@ function Blog(props) {
if (GlobalTheme && ThemeConfig) {
return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/${blogData.parentCategory}`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Row className="mb-4">
<Col className="p-0">
<img
src={MediaService.getMedia(blogData.coverImage)}
alt="Banner"
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>
{
blogData.coverImage !== "" ?
<img
src={MediaService.getMedia(blogData.coverImage)}
alt="Banner"
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>:""
}
</Col>
</Row>
<Row className="mr-2 ml-2 mb-2 mt-1 blogContent">
@ -142,13 +149,13 @@ function Blog(props) {
</Row>
<Row className="mr-2 ml-2 mt-1">
<Col xs="3" className="d-none d-md-block"></Col>
<Col xs="4" className="d-none d-md-block"></Col>
<Col style={{marginBottom: '25px'}}>
<div className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`}>{blogContent}</div>
</Col>
<Col xs="3" className="d-none d-md-block"></Col>
<Col xs="4" className="d-none d-md-block"></Col>
</Row>
</Container>
);

View File

@ -15,11 +15,15 @@ import {
CardImg,
CardTitle,
CardText,
CardBody
CardBody,
Button
} from 'reactstrap';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
function Blogs(props) {
let navigate = useNavigate();
const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig;
@ -34,10 +38,12 @@ function Blogs(props) {
if (GlobalTheme && ThemeConfig) {
return (
<Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}>
<Row className="justify-content-center align-items-center">
<Col className="d-flex flex-column align-items-center">
{/* Top Section - Categories */}
<div className="w-100">
<Col xs="3" className="d-none d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{width: "100%", border: "none"}}>
<CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1">

View File

@ -38,4 +38,11 @@ a {
border: solid grey;
border-radius: 10px;
padding: 1em;
}
.blogContent img {
display: flex;
justify-content: center; /* Center horizontally */
align-items: center;
width: 50%;
}

View File

@ -13,4 +13,4 @@ export default defineConfig({
}
}
}
})
})