Added edit structure for categories
This commit is contained in:
parent
4bb5b6aa8e
commit
4649023dc5
@ -62,7 +62,6 @@ class ThemeDataListAPIView(generics.ListAPIView):
|
||||
class CategoryCreateAPIView(generics.CreateAPIView):
|
||||
queryset = Category.objects.all()
|
||||
serializer_class = CategorySerializer
|
||||
lookup_field = 'category_id'
|
||||
|
||||
class CategoryUpdateAPIView(generics.RetrieveUpdateAPIView):
|
||||
queryset = Category.objects.all()
|
||||
|
||||
@ -41,8 +41,8 @@ urlpatterns = [
|
||||
path('data/shared/theme-config/', ThemeDataListAPIView.as_view(), name='theme-data-list-view'),
|
||||
path('data/shared/update/theme-config/', ThemeDataUpdateAPIView.as_view(), name='theme-data-update-view'),
|
||||
path('data/category/', CategoryListAPIView.as_view(), name='category-list-view'),
|
||||
path('data/category/create/', CategoryCreateAPIView.as_view(), name='category-create-view'),
|
||||
path('data/category/<slug:category_id>/', BlogsByCategoryAPIView.as_view(), name='blogs-by-category-view'),
|
||||
path('data/category/create/<slug:category_id>/', CategoryCreateAPIView.as_view(), name='category-create-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/<slug:blog_id>/', BlogRetrieveAPIView.as_view(), name='blog-retrieve-view'),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
//import services
|
||||
import DataService from '../../services/data-service';
|
||||
import EditableDataService from '../../services/editable-data-service';
|
||||
|
||||
//import views
|
||||
import CardListViewer from './shared/card-list-viewer';
|
||||
@ -26,14 +26,80 @@ function Blogs(props) {
|
||||
const ThemeConfig = props.ThemeConfig;
|
||||
|
||||
const [categoryMetadata, setCategoryMetadata] = useState([]);
|
||||
const [idsToUpdate, setIdsToUpdate] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
DataService.getData('category/category-metadata').then(response =>
|
||||
setCategoryMetadata(response.data)
|
||||
);
|
||||
setCategoryData()
|
||||
}, []);
|
||||
|
||||
if (GlobalTheme && ThemeConfig) {
|
||||
const setCategoryData = () => {
|
||||
EditableDataService.getData('/data/category/').then(response => {
|
||||
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)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const addToIdsToUpdate = (resourceObject) => {
|
||||
let localIdsToUpdate = {...idsToUpdate}
|
||||
localIdsToUpdate[resourceObject.id]={
|
||||
"name": resourceObject.name,
|
||||
"featured_blog": resourceObject.featuredBlog,
|
||||
"description": resourceObject.description,
|
||||
"tagline": resourceObject.tagLine,
|
||||
"cover_image": resourceObject.coverImage
|
||||
}
|
||||
setIdsToUpdate(localIdsToUpdate)
|
||||
}
|
||||
|
||||
const deleteResource = (id) => {
|
||||
EditableDataService.deleteData(`/data/category/delete/${id}/`).then(response => {
|
||||
props.notificationToggler('Category delete successfully')
|
||||
setCategoryData()
|
||||
}).catch(error => {
|
||||
props.notificationToggler('Failed to delete category', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
const addNewCategory = () => {
|
||||
EditableDataService.createData('/data/category/create/', {
|
||||
"name": "Enter a blog name",
|
||||
"featured_blog": "",
|
||||
"description": "Enter description",
|
||||
"tagline": "Enter category tagline",
|
||||
"cover_image": ""
|
||||
}).then(response => {
|
||||
props.notificationToggler('Category created successfully')
|
||||
setCategoryData()
|
||||
}
|
||||
).catch(error => {
|
||||
props.notificationToggler('Failed to add category', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
const updateInfo = () => {
|
||||
for (let id of Object.keys(idsToUpdate)) {
|
||||
EditableDataService.updateData(`/data/category/update/${id}/`, idsToUpdate[id]).then(response=>{
|
||||
props.notificationToggler('Category data updated successfully')
|
||||
}).catch(error => {
|
||||
props.notificationToggler('Failed to update category data', 'danger');
|
||||
});
|
||||
}
|
||||
setCategoryData()
|
||||
}
|
||||
|
||||
if (GlobalTheme && ThemeConfig && categoryMetadata.length > 0) {
|
||||
return (
|
||||
<Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}>
|
||||
<Row className="justify-content-center align-items-center">
|
||||
@ -42,7 +108,8 @@ function Blogs(props) {
|
||||
<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">
|
||||
{"Categories"}<Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline>Add New</Button>
|
||||
{"Categories"}
|
||||
<Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewCategory()}>Add New</Button>
|
||||
</CardTitle>
|
||||
</CardBody>
|
||||
</Card>
|
||||
@ -54,17 +121,21 @@ function Blogs(props) {
|
||||
categoryMetadata.map((item, index) => (
|
||||
<CardListViewer
|
||||
key={item.id}
|
||||
totalItems={categoryMetadata.length}
|
||||
cardType={"longCard"}
|
||||
id = {item.id}
|
||||
totalItems={categoryMetadata.length}
|
||||
addToIdsToUpdate={addToIdsToUpdate}
|
||||
cardType={"longCard"}
|
||||
deleteResource={deleteResource}
|
||||
resourceType={"categories"}
|
||||
textColor={ThemeConfig[GlobalTheme].textColor}
|
||||
bgColor={ThemeConfig[GlobalTheme].background}
|
||||
borderColor={ThemeConfig[GlobalTheme].borderColor}
|
||||
buttonColor={ThemeConfig[GlobalTheme].buttonColor}
|
||||
itemObject={item}
|
||||
/>
|
||||
)) : <Spinner />}
|
||||
<ButtonGroup className='mt-4'>
|
||||
<Button color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
|
||||
<Button onClick={() => updateInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
|
||||
<Button color={ThemeConfig[GlobalTheme].buttonColor} outline>Publish Data</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import MediaService from '../../../services/media-service'
|
||||
import {
|
||||
Spinner,
|
||||
@ -7,17 +7,73 @@ import {
|
||||
CardTitle,
|
||||
CardText,
|
||||
CardBody,
|
||||
Input, InputGroup, InputGroupText
|
||||
Input, InputGroup, InputGroupText, FormFeedback, Button
|
||||
} from 'reactstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ModalComponent from './modal-component';
|
||||
|
||||
function CardListViewer(props) {
|
||||
|
||||
const [nameFieldInvalid, setNameFieldInvalid] = useState(false)
|
||||
const [descriptionFieldInvalid, setDescriptionFieldInvalid] = useState(false)
|
||||
const [taglineFieldInvalid, setTaglineFieldInvalid] = useState(false)
|
||||
const [modal, setModal] = useState(false);
|
||||
const [modalText, setModalText] = useState(false);
|
||||
const [modalTitle, setModalTitle] = useState(false);
|
||||
|
||||
const toggle = () => setModal(!modal);
|
||||
|
||||
const nameField = useRef(null)
|
||||
const descriptionField = useRef(null)
|
||||
const taglineField = useRef(null)
|
||||
|
||||
const handleInputUpdate = (elementValue, fieldType) => {
|
||||
if (fieldType === 'nameField'){
|
||||
if (elementValue === '')
|
||||
setNameFieldInvalid(true)
|
||||
else
|
||||
setNameFieldInvalid(false)
|
||||
}
|
||||
if (fieldType === 'descriptionField'){
|
||||
if (elementValue === '')
|
||||
setDescriptionFieldInvalid(true)
|
||||
else
|
||||
setDescriptionFieldInvalid(false)
|
||||
}
|
||||
if (fieldType === 'taglineField'){
|
||||
if (elementValue === '')
|
||||
setTaglineFieldInvalid(true)
|
||||
else
|
||||
setTaglineFieldInvalid(false)
|
||||
}
|
||||
props.addToIdsToUpdate({
|
||||
"id": props.id,
|
||||
"name": nameField.current.value,
|
||||
"featuredBlog": "",
|
||||
"description": descriptionField.current.value,
|
||||
"tagLine": taglineField.current.value,
|
||||
"coverImage": ""
|
||||
})
|
||||
}
|
||||
|
||||
const showModal = () => {
|
||||
setModalTitle('Confirm')
|
||||
setModalText('Are you sure that you wish to delete this category?')
|
||||
toggle()
|
||||
}
|
||||
|
||||
const deleteResource = () => {
|
||||
props.deleteResource(props.id)
|
||||
toggle()
|
||||
}
|
||||
|
||||
const itemObject = props.itemObject
|
||||
|
||||
if (props.totalItems > 0 && itemObject && Object.keys(itemObject).length !== 0){
|
||||
if (props.resourceType === 'categories')
|
||||
return (
|
||||
<>
|
||||
<ModalComponent modalText={modalText} modalTitle={modalTitle} modal={modal} toggle={toggle} confirmAction={deleteResource}/>
|
||||
<Card color={props.borderColor} outline className={`my-2 ${props.bgColor}`} style={{"width": props.cardType === "smallCard" ? "18rem": "100%"}}>
|
||||
{itemObject.coverImage !== "" ? <CardImg src={MediaService.getMedia(itemObject.coverImage)} style={{ "height": "180px", "objectFit": "cover" }} top width="100%" /> : ""}
|
||||
<CardBody>
|
||||
@ -26,7 +82,10 @@ function CardListViewer(props) {
|
||||
<InputGroupText>
|
||||
Name
|
||||
</InputGroupText>
|
||||
<Input defaultValue={itemObject.name} />
|
||||
<Input defaultValue={itemObject.name} invalid={nameFieldInvalid} innerRef={nameField} onChange={() => handleInputUpdate(nameField.current.value, 'nameField')}/>
|
||||
{nameFieldInvalid ? <FormFeedback tooltip className="mt-1">
|
||||
This field cannot be empty
|
||||
</FormFeedback>:''}
|
||||
</InputGroup>
|
||||
</CardTitle>
|
||||
<CardText className={`${props.textColor}`}>
|
||||
@ -34,7 +93,10 @@ function CardListViewer(props) {
|
||||
<InputGroupText>
|
||||
Description
|
||||
</InputGroupText>
|
||||
<Input defaultValue={itemObject.description} />
|
||||
<Input defaultValue={itemObject.description} invalid={descriptionFieldInvalid} innerRef={descriptionField} onChange={() => handleInputUpdate(descriptionField.current.value, 'descriptionField')}/>
|
||||
{descriptionFieldInvalid ? <FormFeedback tooltip className="mt-1">
|
||||
This field cannot be empty
|
||||
</FormFeedback>:''}
|
||||
</InputGroup>
|
||||
</CardText>
|
||||
<CardText>
|
||||
@ -43,7 +105,10 @@ function CardListViewer(props) {
|
||||
<InputGroupText>
|
||||
Tagline
|
||||
</InputGroupText>
|
||||
<Input defaultValue={itemObject.tagLine} />
|
||||
<Input defaultValue={itemObject.tagLine} invalid={taglineFieldInvalid} innerRef={taglineField} onChange={() => handleInputUpdate(taglineField.current.value, 'taglineField')} />
|
||||
{taglineFieldInvalid ? <FormFeedback tooltip className="mt-1">
|
||||
This field cannot be empty
|
||||
</FormFeedback>:''}
|
||||
</InputGroup>
|
||||
</small>
|
||||
</CardText>
|
||||
@ -51,9 +116,11 @@ 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>
|
||||
</CardText>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
else
|
||||
return (
|
||||
|
||||
26
frontend/src/components/editable/shared/modal-component.jsx
Normal file
26
frontend/src/components/editable/shared/modal-component.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
|
||||
function ModalComponent(props) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal isOpen={props.modal} toggle={props.toggle}>
|
||||
<ModalHeader toggle={props.toggle}>{props.modalTitle}</ModalHeader>
|
||||
<ModalBody>
|
||||
{props.modalText}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={props.confirmAction}>
|
||||
Yes
|
||||
</Button>{' '}
|
||||
<Button color="secondary" onClick={props.toggle}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalComponent;
|
||||
@ -18,4 +18,12 @@ const updateData = (endPoint, data) => {
|
||||
return axios.patch(endPoint, data);
|
||||
};
|
||||
|
||||
export default { getData, updateData };
|
||||
const createData = (endPoint, data) => {
|
||||
return axios.post(endPoint, data);
|
||||
};
|
||||
|
||||
const deleteData = (endPoint) => {
|
||||
return axios.delete(endPoint);
|
||||
};
|
||||
|
||||
export default { getData, updateData, createData, deleteData };
|
||||
Loading…
Reference in New Issue
Block a user