Added support to update blogs

This commit is contained in:
Barunes Padhy 2024-05-28 08:56:04 +03:00
parent c091baf334
commit 2d8cc1e7de
6 changed files with 154 additions and 79 deletions

View File

@ -58,6 +58,40 @@ class BlogSerializer(serializers.ModelSerializer):
] ]
class UnifiedCategoryBlogSerializer(serializers.ModelSerializer):
blog_metadata = serializers.SerializerMethodField() # Custom field for related blogs
class Meta:
model = Category
fields = [
'category_id',
'name',
'cover_image',
'tagline',
'description',
'featured_id',
'blog_metadata'
]
def get_blog_metadata(self, obj):
# Serializes all blogs related to the category, assuming `blogs` as the related_name
blogs = obj.blogs.all()
return [{
'blog_id': blog.blog_id, # Using UUID
'name': blog.name,
'description': blog.description,
'cover_image': blog.cover_image,
'tagline': blog.tagline,
'parent_category': blog.parent_category.category_id
# Assuming parent_category is a reference to a Category object
} for blog in blogs]
def to_representation(self, instance):
representation = super().to_representation(instance)
# Set a featured blog based on some logic, e.g., the first blog
representation['featured_id'] = instance.blogs.first().blog_id if instance.blogs.exists() else None
return representation
class MediaSerializer(serializers.Serializer): class MediaSerializer(serializers.Serializer):
media = serializers.ListField( media = serializers.ListField(
child=serializers.FileField(max_length=100000, allow_empty_file=False, use_url=False) child=serializers.FileField(max_length=100000, allow_empty_file=False, use_url=False)

View File

@ -24,6 +24,7 @@ from .serializers import (
ThemeDataSerializer, ThemeDataSerializer,
CategorySerializer, CategorySerializer,
BlogSerializer, BlogSerializer,
UnifiedCategoryBlogSerializer,
MediaSerializer MediaSerializer
) )
################################################################ ################################################################
@ -76,6 +77,16 @@ class CategoryDeleteAPIView(generics.DestroyAPIView):
queryset = Category.objects.all() queryset = Category.objects.all()
serializer_class = CategorySerializer serializer_class = CategorySerializer
lookup_field = 'category_id' lookup_field = 'category_id'
class BlogsByCategoryAPIView(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)
################################################################ ################################################################
#Blog related views################################################## #Blog related views##################################################
@ -94,15 +105,14 @@ class BlogRetrieveAPIView(generics.RetrieveAPIView):
serializer_class = BlogSerializer serializer_class = BlogSerializer
lookup_field = 'blog_id' lookup_field = 'blog_id'
class BlogsByCategoryAPIView(APIView): class CategoryDetailView(APIView):
def get(self, request, category_id): def get(self, request, category_id):
try: try:
category = Category.objects.get(category_id=category_id) category = Category.objects.get(category_id=category_id)
except Category.DoesNotExist: except Category.DoesNotExist:
return Response({'message': 'Category not found'}, status=404) return Response({'message': 'Category not found'}, status=404)
blogs = category.blogs.all() serializer = UnifiedCategoryBlogSerializer(category)
serializer = BlogSerializer(blogs, many=True)
return Response(serializer.data) return Response(serializer.data)
class BlogDeleteAPIView(generics.DestroyAPIView): class BlogDeleteAPIView(generics.DestroyAPIView):

View File

@ -31,7 +31,7 @@ from apimanager.views import (
BlogUpdateAPIView, BlogUpdateAPIView,
BlogRetrieveAPIView, BlogRetrieveAPIView,
BlogDeleteAPIView, BlogDeleteAPIView,
BlogsByCategoryAPIView BlogsByCategoryAPIView,
) )
urlpatterns = [ urlpatterns = [

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import DataService from '../../services/data-service'; import EditableDataService from '../../services/editable-data-service';
import MediaService from '../../services/media-service'; import MediaService from '../../services/media-service';
import CardListViewer from './shared/card-list-viewer'; import CardListViewer from './shared/card-list-viewer';
import CategoryBar from './shared/category-bar'; import CategoryBar from './shared/category-bar';
@ -26,20 +26,34 @@ function BlogList(props) {
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
const [categoryData, setCategoryData] = useState('loading'); const [categoryData, setCategoryData] = useState('loading');
const [featuredBlogData, setFeaturedBlogData] = useState('loading');
const [currentPage, setCurrentPage] = useState('loading'); const [currentPage, setCurrentPage] = useState('loading');
useEffect(() => { useEffect(() => {
DataService.getData(`category/${categoryID}/category-data`).then(response =>{ EditableDataService.getData(`/data/category/${categoryID}/`).then(response => {
setCategoryData(response.data); let responseData = response.data
console.log(response.data) let blogMetadata = []
if (response.data.featuredBlog){ console.log(responseData)
DataService.getData(`blog/${response.data.featuredBlog}/blog-data`).then(response => let localCategoryData = {
setFeaturedBlogData(response.data) "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"]
} }
else for (let eachBlog of responseData["blog_metadata"]){
setFeaturedBlogData("nodata") 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)
} }
); );
}, [categoryID]); }, [categoryID]);
@ -61,26 +75,10 @@ function BlogList(props) {
</div> </div>
<div className="" style={{ width: '70%', margin: 'auto', display: 'flex', flexWrap: 'wrap', gap: '1rem' }}> <div className="" style={{ width: '70%', margin: 'auto', display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
<h3 className={`${ThemeConfig[GlobalTheme].textColor}`}> <h3 className={`${ThemeConfig[GlobalTheme].textColor}`}>
{`Featured`} {categoryData === 'loading' ? <Spinner /> :
</h3>
{
featuredBlogData === 'loading' ? <Spinner /> :
<CardListViewer
key={featuredBlogData.id}
totalItems={featuredBlogData === 'nodata' ? 0 : 1}
cardType={"longCard"}
resourceType={"blog"}
textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor}
itemObject={featuredBlogData}
/>
}
{
categoryData === 'loading' ? <Spinner /> :
categoryData.blogMetadata.map((item, index) => ( categoryData.blogMetadata.map((item, index) => (
<CardListViewer <CardListViewer
key={item.id} key={item.blog_id} // Ensuring keys are unique and correct
totalItems={categoryData.blogMetadata.length} totalItems={categoryData.blogMetadata.length}
cardType={"smallCard"} cardType={"smallCard"}
resourceType={"blog"} resourceType={"blog"}
@ -91,6 +89,7 @@ function BlogList(props) {
/> />
)) ))
} }
</h3>
</div> </div>
</Col> </Col>
</Row> </Row>

View File

@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import DataService from '../../services/data-service'; import EditableDataService from '../../services/editable-data-service';
import MediaService from '../../services/media-service' import MediaService from '../../services/media-service'
import CategoryBar from './shared/category-bar'; import CategoryBar from './shared/category-bar';
import EditorComponent from './shared/tiptap'; import EditorComponent from './shared/tiptap';
@ -25,21 +25,37 @@ function Blog(props) {
const [blogContent, setBlogContent] = useState(); const [blogContent, setBlogContent] = useState();
const setInfo = (event) => { const setInfo = (event) => {
let localEditedBlogData = {...blogData}; EditableDataService.updateData(`/data/blog/update/${blogID}/`,{
localEditedBlogData["name"] = nameField.current.value; "name": nameField.current.value,
localEditedBlogData["description"] = descriptionField.current.value; "description": descriptionField.current.value,
localEditedBlogData["tagLine"] = tagLineField.current.value; "tagline": tagLineField.current.value,
localEditedBlogData["contentBody"] = blogContent "content_body": blogContent
setBlogData(localEditedBlogData); }).then(response => {
props.notificationToggler('Data saved!'); props.notificationToggler('Blog data saved!');
getInfo()
}).catch(error => {
props.notificationToggler('Failed to update blog!', 'danger');
});
}
const getInfo = () => {
EditableDataService.getData(`/data/blog/${blogID}/`).then(response => {
let responseData = response.data
setBlogData({
"id": responseData["blog_id"],
"name": responseData["name"],
"description": responseData["description"],
"tagLine": responseData["tagline"],
"coverImage": responseData["cover_image"],
"parentCategory": responseData["parent_category"]
})
setBlogContent(responseData["content_body"])
}
);
} }
useEffect(() => { useEffect(() => {
DataService.getData(`blog/${blogID}/blog-data`).then(response =>{ getInfo()
setBlogData(response.data)
setBlogContent(response.data.contentBody)
}
);
}, []); }, []);
if (GlobalTheme && ThemeConfig && blogData) { if (GlobalTheme && ThemeConfig && blogData) {
@ -89,7 +105,7 @@ function Blog(props) {
<Col xs="3" className="d-none d-md-block"></Col> <Col xs="3" className="d-none d-md-block"></Col>
<Col className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`} style={{marginBottom: '25px'}}> <Col className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`} style={{marginBottom: '25px'}}>
<EditorComponent setContent={setBlogContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={blogData.contentBody}/> <EditorComponent setContent={setBlogContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={blogContent}/>
<ButtonGroup className='mt-4'> <ButtonGroup className='mt-4'>
<Button onClick={(event) => setInfo(event)} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button> <Button onClick={(event) => setInfo(event)} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
<Button color={ThemeConfig[GlobalTheme].buttonColor} outline>Publish Data</Button> <Button color={ThemeConfig[GlobalTheme].buttonColor} outline>Publish Data</Button>

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import DataService from '../../../services/data-service'; import EditableDataService from '../../../services/editable-data-service';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Container, Row, Col, Button, Spinner, ListGroup, ListGroupItem, ButtonGroup } from 'reactstrap'; import { Container, Row, Col, Button, Spinner, ListGroup, ListGroupItem, ButtonGroup } from 'reactstrap';
@ -7,10 +7,26 @@ function CategoryBar(props) {
const [categoryMetadata, setCategoryMetadata] = useState([]); const [categoryMetadata, setCategoryMetadata] = useState([]);
useEffect(() => { const setCategoryData = () => {
DataService.getData('category/category-metadata').then(response => EditableDataService.getData('/data/category/').then(response => {
setCategoryMetadata(response.data) let responseData = response.data
let localCategoryMetadata = []
for (let eachResponse of responseData){
localCategoryMetadata.push({
"id": eachResponse["category_id"],
"name": eachResponse["name"],
"featuredBlog": eachResponse["featured_id"],
"description": eachResponse["description"],
"tagLine": eachResponse["tagline"],
"coverImage": eachResponse["cover_image"]
})
}
setCategoryMetadata(localCategoryMetadata)
}
); );
}
useEffect(() => {
setCategoryData();
}, []); }, []);
const rowStyle = { const rowStyle = {