bro got linted

This commit is contained in:
Barunes Padhy 2024-06-13 20:46:15 +01:00
parent 2369d5c8e6
commit d41d3501c3
38 changed files with 7328 additions and 1398 deletions

View File

@ -0,0 +1,34 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"indent": [
"error",
2
],
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"react/display-name": "off",
"quotes": [
"error",
"single"
],
"no-console": "error"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -43,9 +43,11 @@
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-react-refresh": "^0.4.6",
"vite": "^5.2.0" "vite": "^5.2.0",
"vite-plugin-eslint": "^1.8.1"
} }
} }

View File

@ -21,9 +21,9 @@ import EditableDataService from './services/editable-data-service'
function App() { function App() {
const [userData, setUserData] = useState(null); const [userData, setUserData] = useState(null);
const [themeConfig, setThemeConfig] = useState(null); const [themeConfig, setThemeConfig] = useState(null);
const [globalTheme, setGlobalTheme] = useState("lightTheme"); const [globalTheme, setGlobalTheme] = useState('lightTheme');
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [notificationMessage, setNotificationMessage] = useState("") const [notificationMessage, setNotificationMessage] = useState('')
const notificationToggler = (message, color) => { const notificationToggler = (message, color) => {
setIsOpen(true) setIsOpen(true)
@ -45,25 +45,24 @@ function App() {
const setConfigData = () => { const setConfigData = () => {
EditableDataService.getData('/data/shared/user-data/').then( response => { EditableDataService.getData('/data/shared/user-data/').then( response => {
let responseData = response.data[0] let responseData = response.data[0]
setUserData({ setUserData({
"name": responseData["name"], 'name': responseData['name'],
"introContent": responseData["intro_content"], 'introContent': responseData['intro_content'],
"profilePhoto": responseData["profile_photo"], 'profilePhoto': responseData['profile_photo'],
"builtWith": responseData["built_with"] 'builtWith': responseData['built_with']
}) })
document.title = responseData.name document.title = responseData.name
} })
)
EditableDataService.getData('/data/shared/theme-config/').then( response =>{ EditableDataService.getData('/data/shared/theme-config/').then( response =>{
let responseData = response.data[0] let responseData = response.data[0]
setThemeConfig({ setThemeConfig({
"defaultTheme": responseData["default_theme"], 'defaultTheme': responseData['default_theme'],
"darkTheme": JSON.parse(responseData["dark_theme"]), 'darkTheme': JSON.parse(responseData['dark_theme']),
"lightTheme": JSON.parse(responseData["light_theme"]) 'lightTheme': JSON.parse(responseData['light_theme'])
}) })
} })
)
} }
useEffect(() => { useEffect(() => {
@ -75,23 +74,23 @@ function App() {
} }
if (themeConfig && userData && globalTheme) if (themeConfig && userData && globalTheme)
return ( return (
<div className="app-container"> <div className='app-container'>
<Router> <Router>
<Header className="header" ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} notificationToggler={notificationToggler} setInfo={setInfo} /> <Header className='header' ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} notificationToggler={notificationToggler} setInfo={setInfo} />
<div className={`p-0 ${themeConfig[globalTheme].background}`}> <div className={`p-0 ${themeConfig[globalTheme].background}`}>
<Routes> <Routes>
<Route path="/" element={<Home notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} setInfo={setInfo} />} /> <Route path='/' element={<Home notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} setInfo={setInfo} />} />
<Route path="/categories" element={<CategoryList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/categories' element={<CategoryList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
<Route path="/categories/:categoryID" element={<BlogList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/categories/:categoryID' element={<BlogList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
<Route path="/blog/:blogID" element={<Blog notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/blog/:blogID' element={<Blog notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
</Routes> </Routes>
</div> </div>
<Footer notificationToggler={notificationToggler} setInfo={setInfo} className="footer" ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} /> <Footer notificationToggler={notificationToggler} setInfo={setInfo} className='footer' ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} />
<Notification style={{width: '20%'}} className="fixed-top" isOpen={isOpen} message={notificationMessage} /> <Notification style={{width: '20%'}} className='fixed-top' isOpen={isOpen} message={notificationMessage} />
</Router> </Router>
</div> </div>
); );
} }
export default App; export default App;

View File

@ -29,33 +29,32 @@ function BlogList(props) {
const [featuredBlogData, setFeaturedBlogData] = useState(null); const [featuredBlogData, setFeaturedBlogData] = useState(null);
const loadBlogs = () => { const loadBlogs = () => {
EditableDataService.getData(`/data/category/${categoryID}/`).then(response => { EditableDataService.getData(`/data/category/${categoryID}/`).then( response => {
let responseData = response.data let responseData = response.data
let blogMetadata = [] let blogMetadata = []
let localCategoryData = { let localCategoryData = {
"id": responseData["category_id"], 'id': responseData['category_id'],
"name": responseData["name"], 'name': responseData['name'],
"coverImage": responseData["cover_image"], 'coverImage': responseData['cover_image'],
"tagLine": responseData["tagline"], 'tagLine': responseData['tagline'],
"description": responseData["description"], 'description': responseData['description'],
"featuredBlog": responseData["featured_id"], 'featuredBlog': responseData['featured_id'],
"blogMetadata": responseData["blog_metadata"] '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"]
})
}
localCategoryData.blogMetadata = blogMetadata
setCategoryData(localCategoryData)
setFeaturedBlogData(blogMetadata.find(blog => blog.id === localCategoryData.featuredBlog))
} }
); 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']
})
}
localCategoryData.blogMetadata = blogMetadata
setCategoryData(localCategoryData)
setFeaturedBlogData(blogMetadata.find(blog => blog.id === localCategoryData.featuredBlog))
});
} }
useEffect(() => { useEffect(() => {
@ -63,23 +62,23 @@ function BlogList(props) {
}, [categoryID]); }, [categoryID]);
const addNewBlog = () => { const addNewBlog = () => {
EditableDataService.createData(`/data/blog/create/`, { EditableDataService.createData('/data/blog/create/', {
"name": "Enter a blog name", 'name': 'Enter a blog name',
"description": "Enter a description", 'description': 'Enter a description',
"tagline": "Enter a tagline", 'tagline': 'Enter a tagline',
"cover_image": "", 'cover_image': '',
"content_body": "<p></p>", 'content_body': '<p></p>',
"parent_category": categoryID 'parent_category': categoryID
}).then(response => { }).then(() => {
props.notificationToggler("New blog created") props.notificationToggler('New blog created')
loadBlogs() loadBlogs()
}).catch(error => { }).catch(() => {
props.notificationToggler('Failed to add a new blog', 'danger'); props.notificationToggler('Failed to add a new blog', 'danger');
}); });
} }
const updateFeaturedBlog = (featuredId) => { const updateFeaturedBlog = (featuredId) => {
EditableDataService.updateData(`/data/category/update/${categoryID}/`, {"featured_id": featuredId}).then(response=>{ EditableDataService.updateData(`/data/category/update/${categoryID}/`, {'featured_id': featuredId}).then(() =>{
if (featuredId) if (featuredId)
props.notificationToggler('Blog set as featured') props.notificationToggler('Blog set as featured')
else props.notificationToggler('Featured blog removed') else props.notificationToggler('Featured blog removed')
@ -88,66 +87,64 @@ function BlogList(props) {
} }
if (GlobalTheme && ThemeConfig) { if (GlobalTheme && ThemeConfig) {
return ( return (
<Container fluid className={`mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}>
<Col className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col className='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} /> <CategoryBar currentPage={categoryID} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} />
<Row className="justify-content-center align-items-center"> <Row className='justify-content-center align-items-center'>
<Col className="d-flex flex-column align-items-center"> <Col className='d-flex flex-column align-items-center'>
<div className="w-100"> <div className='w-100'>
<Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{ width: "100%", border: "none" }}> <Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{ width: '100%', border: 'none' }}>
<CardBody> <CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1"> <CardTitle style={{ display: 'grid' }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag='h1'>
{`Blogs in ${categoryData.name}`} {`Blogs in ${categoryData.name}`}
<Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewBlog()}>Add New</Button> <Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewBlog()}>Add New</Button>
</CardTitle> </CardTitle>
</CardBody> </CardBody>
</Card> </Card>
</div> </div>
<div className="container"> <div className='container'>
{ {
featuredBlogData ? featuredBlogData ?
<CardListViewer <CardListViewer
key={featuredBlogData.id} key={featuredBlogData.id}
totalItems={featuredBlogData === 'nodata' ? 0 : 1} totalItems={featuredBlogData === 'nodata' ? 0 : 1}
cardType={"longCard"} cardType={'longCard'}
resourceType={"blog"} resourceType={'blog'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}
itemObject={featuredBlogData} itemObject={featuredBlogData}
/> : '' /> : ''
} }
<Row> <Row>
{categoryData === 'loading' ? <Spinner /> : { categoryData === 'loading' ? <Spinner /> :
categoryData.blogMetadata.map((item, index) => ( categoryData.blogMetadata.map((item) => (
<Col key={item.blog_id}> <Col key={item.blog_id}>
<div className={`p-2 ml-2 ${ThemeConfig[GlobalTheme].textColor}`}> <div className={`p-2 ml-2 ${ThemeConfig[GlobalTheme].textColor}`}>
<CardListViewer <CardListViewer
totalItems={categoryData.blogMetadata.length} totalItems={categoryData.blogMetadata.length}
featuredBlog={categoryData.featuredBlog} featuredBlog={categoryData.featuredBlog}
updateFeaturedBlog={updateFeaturedBlog} updateFeaturedBlog={updateFeaturedBlog}
cardType={"smallCard"} cardType={'smallCard'}
resourceType={"blog"} resourceType={'blog'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}
itemObject={item} itemObject={item}
/> />
</div> </div>
</Col> </Col>
)) ))
} }
</Row> </Row>
</div> </div>
</Col> </Col>
</Row> </Row>
</Container> </Container>
); );
} else { } else {
return null; return null;
} }
} }
export default BlogList export default BlogList

View File

@ -11,7 +11,7 @@ import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { import {
Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody, Input, InputGroup, InputGroupText Container,Row, Col,Spinner, Button, ButtonGroup, Input, InputGroup, InputGroupText
} from 'reactstrap'; } from 'reactstrap';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@ -39,17 +39,17 @@ function Blog(props) {
const toggle = () => setModal(!modal); const toggle = () => setModal(!modal);
const toggleFileModal = () => setFileModal(!fileModal); const toggleFileModal = () => setFileModal(!fileModal);
const setInfo = (event) => { const setInfo = () => {
EditableDataService.updateData(`/data/blog/update/${blogID}/`,{ EditableDataService.updateData(`/data/blog/update/${blogID}/`,{
"name": nameField.current.value, 'name': nameField.current.value,
"description": descriptionField.current.value, 'description': descriptionField.current.value,
"tagline": tagLineField.current.value, 'tagline': tagLineField.current.value,
"content_body": blogContent, 'content_body': blogContent,
"cover_image": coverImage ? coverImage !== '-' ? coverImage : '' : blogData.coverImage 'cover_image': coverImage ? coverImage !== '-' ? coverImage : '' : blogData.coverImage
}).then(response => { }).then(() => {
props.notificationToggler('Blog data saved!'); props.notificationToggler('Blog data saved!');
getInfo() getInfo()
}).catch(error => { }).catch(() => {
props.notificationToggler('Failed to update blog!', 'danger'); props.notificationToggler('Failed to update blog!', 'danger');
}); });
} }
@ -61,33 +61,32 @@ function Blog(props) {
} }
const deleteResource = () => { const deleteResource = () => {
EditableDataService.deleteData(`/data/blog/delete/${blogData.id}/`).then(response => { EditableDataService.deleteData(`/data/blog/delete/${blogData.id}/`).then(() => {
props.notificationToggler('Blog successfully deleted') props.notificationToggler('Blog successfully deleted')
navigate(`/categories/${blogData.parentCategory}`); navigate(`/categories/${blogData.parentCategory}`);
}).catch(error => { }).catch(() => {
props.notificationToggler('Failed to delete blog', 'danger'); props.notificationToggler('Failed to delete blog', 'danger');
}); });
toggle() toggle()
} }
const getInfo = () => { const getInfo = () => {
EditableDataService.getData(`/data/blog/${blogID}/`).then(response => { EditableDataService.getData(`/data/blog/${blogID}/`).then(response => {
let responseData = response.data let responseData = response.data
setBlogData({ setBlogData({
"id": responseData["blog_id"], 'id': responseData['blog_id'],
"name": responseData["name"], 'name': responseData['name'],
"description": responseData["description"], 'description': responseData['description'],
"tagLine": responseData["tagline"], 'tagLine': responseData['tagline'],
"coverImage": responseData["cover_image"], 'coverImage': responseData['cover_image'],
"parentCategory": responseData["parent_category"] 'parentCategory': responseData['parent_category']
}) })
setBlogContent(responseData["content_body"]) setBlogContent(responseData['content_body'])
} });
);
} }
useEffect(() => { useEffect(() => {
getInfo() getInfo()
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -98,80 +97,74 @@ function Blog(props) {
}, [coverImage]) }, [coverImage])
if (GlobalTheme && ThemeConfig && blogData) { if (GlobalTheme && ThemeConfig && blogData) {
return ( return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<ModalComponent modalText={modalText} modalTitle={modalTitle} modal={modal} toggle={toggle} confirmAction={deleteResource}/> <ModalComponent modalText={modalText} modalTitle={modalTitle} modal={modal} toggle={toggle} confirmAction={deleteResource}/>
<MediaUpload setMedia={setCoverImage} notificationToggler={props.notificationToggler} modal={fileModal} toggle={toggleFileModal} resourceType='blog' resourceId={blogData.id}></MediaUpload> <MediaUpload setMedia={setCoverImage} notificationToggler={props.notificationToggler} modal={fileModal} toggle={toggleFileModal} resourceType='blog' resourceId={blogData.id}></MediaUpload>
<Col xs="3" className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/${blogData.parentCategory}`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col xs='3' className='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}/> <CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Row className="mb-4"> <Row className='mb-4'>
<Col className="p-0"> <Col className='p-0'>
{ {
blogData.coverImage !== "" ? blogData.coverImage !== '' ?
<img <img
src={EditableMediaService.getMedia(blogData.coverImage)} src={EditableMediaService.getMedia(blogData.coverImage)}
alt="Banner" alt='Banner'
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }} style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>:"" />:''
} }
</Col> </Col>
</Row> </Row>
<Row className="mr-2 ml-2 mb-2 mt-1 blogContent"> <Row className='mr-2 ml-2 mb-2 mt-1 blogContent'>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
<Col xs={`${window.screen.width >= 765 ? '6':''}`}> <Col xs={`${window.screen.width >= 765 ? '6':''}`}>
<Button color='danger' onClick={() => showModal()} className="mb-5">Delete Blog</Button> <Button color='danger' onClick={() => showModal()} className='mb-5'>Delete Blog</Button>
<ButtonGroup className="mb-5 ms-5"> <ButtonGroup className='mb-5 ms-5'>
<Button <Button
outline outline
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => toggleFileModal()} onClick={() => toggleFileModal()}
> >
Set Cover Image Set Cover Image
</Button> </Button>
<Button <Button
outline outline
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => setCoverImage('-')} onClick={() => setCoverImage('-')}
> >
Remove Cover Image Remove Cover Image
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<InputGroup className="mb-3"> <InputGroup className='mb-3'>
<InputGroupText> <InputGroupText>Name</InputGroupText>
Name <Input innerRef={nameField} defaultValue={blogData.name} />
</InputGroupText> </InputGroup>
<Input innerRef={nameField} defaultValue={blogData.name} /> <InputGroup className='mb-3'>
</InputGroup> <InputGroupText>Description</InputGroupText>
<InputGroup className="mb-3"> <Input innerRef={descriptionField} defaultValue={blogData.description} />
<InputGroupText> </InputGroup>
Description <InputGroup>
</InputGroupText> <InputGroupText>Tagline</InputGroupText>
<Input innerRef={descriptionField} defaultValue={blogData.description} /> <Input innerRef={tagLineField} defaultValue={blogData.tagLine} />
</InputGroup> </InputGroup>
<InputGroup> </Col>
<InputGroupText> <Col xs='3' className='d-none d-md-block'></Col>
Tagline </Row>
</InputGroupText> <Row className={`my-2 ${ThemeConfig[GlobalTheme].background}`}>
<Input innerRef={tagLineField} defaultValue={blogData.tagLine} /> <Col>
</InputGroup> <hr style={{'borderColor': `${ThemeConfig[GlobalTheme].borderColor}`}} />
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> </Row>
</Row> <Row className='mr-2 ml-2 mt-1'>
<Row className={`my-2 ${ThemeConfig[GlobalTheme].background}`}> <Col xs='3' className='d-none d-md-block'></Col>
<Col> <Col className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`} style={{marginBottom: '25px'}}>
<hr style={{"borderColor": `${ThemeConfig[GlobalTheme].borderColor}`}} /> <EditorComponent notificationToggler={props.notificationToggler} setContent={setBlogContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={blogContent} resourceType='blog' resourceId={blogData.id}/>
</Col> <Button className='mt-3 mb-2' onClick={(event) => setInfo(event)} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
</Row> </Col>
<Row className="mr-2 ml-2 mt-1"> <Col xs='3' className='d-none d-md-block'></Col>
<Col xs="3" className="d-none d-md-block"></Col> </Row>
<Col className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`} style={{marginBottom: '25px'}}> </Container>
<EditorComponent notificationToggler={props.notificationToggler} setContent={setBlogContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={blogContent} resourceType='blog' resourceId={blogData.id}/> );
<Button className='mt-3 mb-2' onClick={(event) => setInfo(event)} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
</Col>
<Col xs="3" className="d-none d-md-block"></Col>
</Row>
</Container>
);
} else { } else {
return (<Spinner />) return (<Spinner />)
} }

View File

@ -12,12 +12,9 @@ import {
Col, Col,
Container, Container,
Card, Card,
CardImg,
CardTitle, CardTitle,
CardText,
CardBody, CardBody,
Button, Button,
ButtonGroup
} from 'reactstrap'; } from 'reactstrap';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { faLeftLong } from '@fortawesome/free-solid-svg-icons'; import { faLeftLong } from '@fortawesome/free-solid-svg-icons';
@ -38,65 +35,62 @@ function Blogs(props) {
const setCategoryData = () => { const setCategoryData = () => {
EditableDataService.getData('/data/category/').then(response => { EditableDataService.getData('/data/category/').then(response => {
let responseData = response.data let responseData = response.data
let localCategoryMetadata = [] let localCategoryMetadata = []
for (let eachResponse of responseData){ for (let eachResponse of responseData){
localCategoryMetadata.push({ localCategoryMetadata.push({
"id": eachResponse["category_id"], 'id': eachResponse['category_id'],
"name": eachResponse["name"], 'name': eachResponse['name'],
"featuredBlog": eachResponse["featured_id"], 'featuredBlog': eachResponse['featured_id'],
"description": eachResponse["description"], 'description': eachResponse['description'],
"tagLine": eachResponse["tagline"], 'tagLine': eachResponse['tagline'],
"coverImage": eachResponse["cover_image"] 'coverImage': eachResponse['cover_image']
}) })
}
setCategoryMetadata(localCategoryMetadata)
} }
); setCategoryMetadata(localCategoryMetadata)
});
} }
const addToIdsToUpdate = (resourceObject) => { const addToIdsToUpdate = (resourceObject) => {
let localIdsToUpdate = {...idsToUpdate} let localIdsToUpdate = {...idsToUpdate}
localIdsToUpdate[resourceObject.id]={ localIdsToUpdate[resourceObject.id]={
"name": resourceObject.name, 'name': resourceObject.name,
"featured_blog": resourceObject.featuredBlog, 'featured_blog': resourceObject.featuredBlog,
"description": resourceObject.description, 'description': resourceObject.description,
"tagline": resourceObject.tagLine, 'tagline': resourceObject.tagLine,
"cover_image": resourceObject.coverImage 'cover_image': resourceObject.coverImage
} }
setIdsToUpdate(localIdsToUpdate) setIdsToUpdate(localIdsToUpdate)
} }
const deleteResource = (id) => { const deleteResource = (id) => {
EditableDataService.deleteData(`/data/category/delete/${id}/`).then(response => { EditableDataService.deleteData(`/data/category/delete/${id}/`).then(() => {
props.notificationToggler('Category successfully deleted') props.notificationToggler('Category successfully deleted')
setCategoryData() setCategoryData()
}).catch(error => { }).catch(() => {
props.notificationToggler('Failed to delete category', 'danger'); props.notificationToggler('Failed to delete category', 'danger');
}); });
} }
const addNewCategory = () => { const addNewCategory = () => {
EditableDataService.createData('/data/category/create/', { EditableDataService.createData('/data/category/create/', {
"name": "Enter name", 'name': 'Enter name',
"featured_id": "", 'featured_id': '',
"description": "Enter description", 'description': 'Enter description',
"tagline": "Enter tagline", 'tagline': 'Enter tagline',
"cover_image": "" 'cover_image': ''
}).then(response => { }).then(() => {
props.notificationToggler('Category created successfully') props.notificationToggler('Category created successfully')
setCategoryData() setCategoryData()}).catch(() => {
}
).catch(error => {
props.notificationToggler('Failed to add category', 'danger'); props.notificationToggler('Failed to add category', 'danger');
}); });
} }
const updateInfo = () => { const updateInfo = () => {
for (let id of Object.keys(idsToUpdate)) { for (let id of Object.keys(idsToUpdate)) {
EditableDataService.updateData(`/data/category/update/${id}/`, idsToUpdate[id]).then(response=>{ EditableDataService.updateData(`/data/category/update/${id}/`, idsToUpdate[id]).then( () =>{
props.notificationToggler('Category data updated successfully') props.notificationToggler('Category data updated successfully')
}).catch(error => { }).catch( () => {
props.notificationToggler('Failed to update category data', 'danger'); props.notificationToggler('Failed to update category data', 'danger');
}); });
} }
@ -106,32 +100,30 @@ function Blogs(props) {
if (GlobalTheme && ThemeConfig) { if (GlobalTheme && ThemeConfig) {
return ( return (
<Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}>
<Col xs="3" className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col xs='3' className='d-md-block'><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate('/')} className='ms-5 mt-5' outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col>
<Row className="justify-content-center align-items-center"> <Row className='justify-content-center align-items-center'>
<Col className="d-flex flex-column align-items-center"> <Col className='d-flex flex-column align-items-center'>
<div className="w-100"> <div className='w-100'>
<Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{width: "100%", border: "none"}}> <Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{width: '100%', border: 'none'}}>
<CardBody> <CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1"> <CardTitle style={{ display: 'grid' }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag='h1'>
{"Categories"} {'Categories'}
<Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewCategory()}>Add New</Button> <Button className='mt-2' color={ThemeConfig[GlobalTheme].buttonColor} outline onClick={() => addNewCategory()}>Add New</Button>
</CardTitle> </CardTitle>
</CardBody> </CardBody>
</Card> </Card>
</div> </div>
<div className='' style={{ width: '70%', margin: 'auto' }}>
<div className="" style={{ width: '70%', margin: 'auto' }}>
{categoryMetadata.length > 0 ? {categoryMetadata.length > 0 ?
categoryMetadata.map((item, index) => ( categoryMetadata.map((item) => (
<CardListViewer <CardListViewer
key={item.id} key={item.id}
id = {item.id} id = {item.id}
totalItems={categoryMetadata.length} totalItems={categoryMetadata.length}
addToIdsToUpdate={addToIdsToUpdate} addToIdsToUpdate={addToIdsToUpdate}
cardType={"longCard"} cardType={'longCard'}
deleteResource={deleteResource} deleteResource={deleteResource}
resourceType={"categories"} resourceType={'categories'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}
@ -140,10 +132,9 @@ function Blogs(props) {
itemObject={item} itemObject={item}
/> />
)) : <Spinner />} )) : <Spinner />}
<Button className='mt-3 mb-2' onClick={() => updateInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button> <Button className='mt-3 mb-2' onClick={() => updateInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
</div> </div>
</Col> </Col>
</Row> </Row>
</Container> </Container>
); );

View File

@ -5,7 +5,7 @@ import MediaUpload from './shared/media-upload'
import EditableMediaService from '../services/editable-media-service' import EditableMediaService from '../services/editable-media-service'
function HomePage(props) { function HomePage(props) {
const [introContent, setIntroContent] = useState("") const [introContent, setIntroContent] = useState('')
const [profilePhoto, setProfilePhoto] = useState(false) const [profilePhoto, setProfilePhoto] = useState(false)
const [nameFieldInvalid, setNameFieldInvalid] = useState(false) const [nameFieldInvalid, setNameFieldInvalid] = useState(false)
const [modal, setModal] = useState(false) const [modal, setModal] = useState(false)
@ -16,21 +16,19 @@ function HomePage(props) {
const setInfo = async () => { const setInfo = async () => {
let response = await props.setInfo('/data/shared/update/user-data/', { let response = await props.setInfo('/data/shared/update/user-data/', {
"name": nameField.current.value, 'name': nameField.current.value,
"intro_content": introContent, 'intro_content': introContent,
"profile_photo": profilePhoto ? profilePhoto !== '-' ? profilePhoto : '' : UserData.profilePhoto 'profile_photo': profilePhoto ? profilePhoto !== '-' ? profilePhoto : '' : UserData.profilePhoto
}) })
console.log(response)
if (response === 200) if (response === 200)
props.notificationToggler("Data saved successfully!") props.notificationToggler('Data saved successfully!')
if ([500, 404, 403, 400].includes(response)) if ([500, 404, 403, 400].includes(response))
props.notificationToggler("Something failed!", "danger") props.notificationToggler('Something failed!', 'danger')
} }
const toggle = () => {setModal(!modal)} const toggle = () => {setModal(!modal)}
const showError = (elementValue, fieldType) => { const showError = (elementValue, fieldType) => {
console.log(elementValue)
if (fieldType === 'nameField'){ if (fieldType === 'nameField'){
if (elementValue === '') if (elementValue === '')
setNameFieldInvalid(true) setNameFieldInvalid(true)
@ -54,54 +52,52 @@ function HomePage(props) {
return ( return (
<Container fluid className={`${ThemeConfig[GlobalTheme].textColor} ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`${ThemeConfig[GlobalTheme].textColor} ${ThemeConfig[GlobalTheme].background}`}>
<MediaUpload setMedia={setProfilePhoto} notificationToggler={props.notificationToggler} modal={modal} toggle={toggle} resourceType='homepage' resourceId='homepage' /> <MediaUpload setMedia={setProfilePhoto} notificationToggler={props.notificationToggler} modal={modal} toggle={toggle} resourceType='homepage' resourceId='homepage' />
<Row className="mb-4"> <Row className='mb-4'>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
<Col className="p-0"> <Col className='p-0'>
<Row className='d-md-block'> <Row className='d-md-block'>
<Col xs="4" className="d-none d-md-block"></Col> <Col xs='4' className='d-none d-md-block'></Col>
{UserData.profilePhoto !== "" && ( {UserData.profilePhoto !== '' && (
<center><img style={{ width: '180px', height: '180px', objectFit: 'cover' }} className="mt-5 mb-2 rounded-circle" src={EditableMediaService.getMedia(UserData.profilePhoto)} alt="Profile Photo" /></center> <center><img style={{ width: '180px', height: '180px', objectFit: 'cover' }} className='mt-5 mb-2 rounded-circle' src={EditableMediaService.getMedia(UserData.profilePhoto)} alt='Profile Photo' /></center>
)} )}
<Col xs="4" className="d-none d-md-block"></Col> <Col xs='4' className='d-none d-md-block'></Col>
</Row> </Row>
<Row> <Row>
<ButtonGroup className='mt-4'> <ButtonGroup className='mt-4'>
<Button <Button
outline outline
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => toggle()} onClick={() => toggle()}
> >
Set Profile Photo Set Profile Photo
</Button> </Button>
<Button <Button
outline outline
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => setProfilePhoto('-')} onClick={() => setProfilePhoto('-')}
> >
Remove Profile Photo Remove Profile Photo
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</Row> </Row>
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
</Row> </Row>
<Row className="mr-2 ml-2 mb-2 mt-1 blogContent"> <Row className='mr-2 ml-2 mb-2 mt-1 blogContent'>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
<Col md="6" xs="12"> <Col md='6' xs='12'>
<InputGroup className='mb-5'> <InputGroup className='mb-5'>
<InputGroupText>Name</InputGroupText> <InputGroupText>Name</InputGroupText>
<Input invalid={nameFieldInvalid} innerRef={nameField} defaultValue={UserData.name} onChange={() => showError(nameField.current.value, 'nameField')}/> <Input invalid={nameFieldInvalid} innerRef={nameField} defaultValue={UserData.name} onChange={() => showError(nameField.current.value, 'nameField')}/>
{nameFieldInvalid && <FormFeedback tooltip className="mt-1"> {nameFieldInvalid && <FormFeedback tooltip className='mt-1'>
This field cannot be empty This field cannot be empty
</FormFeedback>} </FormFeedback>}
</InputGroup> </InputGroup>
<EditorComponent notificationToggler={props.notificationToggler} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={UserData.introContent} setContent={setIntroContent} resourceType='homepage' resourceId='homepage' /> <EditorComponent notificationToggler={props.notificationToggler} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={UserData.introContent} setContent={setIntroContent} resourceType='homepage' resourceId='homepage' />
<Button className='mt-3 mb-2' onClick={() => setInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button> <Button className='mt-3 mb-2' onClick={() => setInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
</Row> </Row>
</Container> </Container>
); );
} }

View File

@ -46,11 +46,11 @@ function CardListViewer(props) {
setTaglineFieldInvalid(false) setTaglineFieldInvalid(false)
} }
props.addToIdsToUpdate({ props.addToIdsToUpdate({
"id": props.id, 'id': props.id,
"name": nameField.current.value, 'name': nameField.current.value,
"featuredBlog": "", 'featuredBlog': '',
"description": descriptionField.current.value, 'description': descriptionField.current.value,
"tagLine": taglineField.current.value, 'tagLine': taglineField.current.value,
}) })
} }
@ -71,71 +71,71 @@ function CardListViewer(props) {
if (props.resourceType === 'categories') if (props.resourceType === 'categories')
return ( return (
<> <>
<ModalComponent modalText={modalText} modalTitle={modalTitle} modal={modal} toggle={toggle} confirmAction={deleteResource}/> <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%"}}> <Card color={props.borderColor} outline className={`my-2 ${props.bgColor}`} style={{'width': props.cardType === 'smallCard' ? '18rem': '100%'}}>
<CardBody> <CardBody>
<CardTitle className={`mb-3 ${props.textColor}`} tag="h5"> <CardTitle className={`mb-3 ${props.textColor}`} tag='h5'>
<InputGroup>
<InputGroupText>
Name
</InputGroupText>
<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}`}>
<InputGroup>
<InputGroupText>
Description
</InputGroupText>
<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>
<small className={`${props.textColor}`}>
<InputGroup> <InputGroup>
<InputGroupText> <InputGroupText>
Tagline Name
</InputGroupText> </InputGroupText>
<Input defaultValue={itemObject.tagLine} invalid={taglineFieldInvalid} innerRef={taglineField} onChange={() => handleInputUpdate(taglineField.current.value, 'taglineField')} /> <Input defaultValue={itemObject.name} invalid={nameFieldInvalid} innerRef={nameField} onChange={() => handleInputUpdate(nameField.current.value, 'nameField')}/>
{taglineFieldInvalid ? <FormFeedback tooltip className="mt-1"> {nameFieldInvalid ? <FormFeedback tooltip className='mt-1'>
This field cannot be empty This field cannot be empty
</FormFeedback>:''} </FormFeedback>:''}
</InputGroup> </InputGroup>
</small> </CardTitle>
</CardText> <CardText className={`${props.textColor}`}>
<CardText> <InputGroup>
<Link className={`${props.textColor}`} to={`/${props.resourceType}/${itemObject.id}`}> <InputGroupText>
Open this resource Description
</Link> </InputGroupText>
<Button color='danger' onClick={() => showModal()} className="m-2">Delete Category</Button> <Input defaultValue={itemObject.description} invalid={descriptionFieldInvalid} innerRef={descriptionField} onChange={() => handleInputUpdate(descriptionField.current.value, 'descriptionField')}/>
</CardText> {descriptionFieldInvalid ? <FormFeedback tooltip className='mt-1'>
</CardBody> This field cannot be empty
</Card> </FormFeedback>:''}
</InputGroup>
</CardText>
<CardText>
<small className={`${props.textColor}`}>
<InputGroup>
<InputGroupText>
Tagline
</InputGroupText>
<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>
<CardText>
<Link className={`${props.textColor}`} to={`/${props.resourceType}/${itemObject.id}`}>
Open this resource
</Link>
<Button color='danger' onClick={() => showModal()} className='m-2'>Delete Category</Button>
</CardText>
</CardBody>
</Card>
</> </>
) )
else else
return ( return (
<Card color={props.borderColor} outline className={`my-2 ${props.bgColor}`} style={{"width": props.cardType === "smallCard" ? "18rem": "100%"}}> <Card color={props.borderColor} outline className={`my-2 ${props.bgColor}`} style={{'width': props.cardType === 'smallCard' ? '18rem': '100%'}}>
{itemObject.coverImage !== "" ? <CardImg src={EditableMediaService.getMedia(itemObject.coverImage)} style={{ "height": "180px", "objectFit": "cover" }} top width="100%" /> : ""} {itemObject.coverImage !== '' ? <CardImg src={EditableMediaService.getMedia(itemObject.coverImage)} style={{ 'height': '180px', 'objectFit': 'cover' }} top width='100%' /> : ''}
<CardBody> <CardBody>
<Link to={`/${props.resourceType}/${itemObject.id}`}> <Link to={`/${props.resourceType}/${itemObject.id}`}>
<CardTitle className={`${props.textColor}`} tag="h3"> <CardTitle className={`${props.textColor}`} tag='h3'>
{itemObject.name} {itemObject.name}
</CardTitle> </CardTitle>
<CardText className={`${props.textColor}`} tag="h5"> <CardText className={`${props.textColor}`} tag='h5'>
{itemObject.description} {itemObject.description}
</CardText> </CardText>
<CardText tag="h6"> <CardText tag='h6'>
<small className={`${props.textColor}`}> <small className={`${props.textColor}`}>
{itemObject.tagLine} {itemObject.tagLine}
</small> </small>
</CardText> </CardText>
</Link> </Link>
</CardBody> </CardBody>
<ButtonGroup> <ButtonGroup>
@ -158,6 +158,6 @@ function CardListViewer(props) {
} }
else else
return(<h3 className={`${props.textColor}`}>No items found in this section</h3>) return(<h3 className={`${props.textColor}`}>No items found in this section</h3>)
} }
export default CardListViewer export default CardListViewer

View File

@ -9,20 +9,20 @@ function CategoryBar(props) {
const setCategoryData = () => { const setCategoryData = () => {
EditableDataService.getData('/data/category/').then(response => { EditableDataService.getData('/data/category/').then(response => {
let responseData = response.data let responseData = response.data
let localCategoryMetadata = [] let localCategoryMetadata = []
for (let eachResponse of responseData){ for (let eachResponse of responseData){
localCategoryMetadata.push({ localCategoryMetadata.push({
"id": eachResponse["category_id"], 'id': eachResponse['category_id'],
"name": eachResponse["name"], 'name': eachResponse['name'],
"featuredBlog": eachResponse["featured_id"], 'featuredBlog': eachResponse['featured_id'],
"description": eachResponse["description"], 'description': eachResponse['description'],
"tagLine": eachResponse["tagline"], 'tagLine': eachResponse['tagline'],
"coverImage": eachResponse["cover_image"] 'coverImage': eachResponse['cover_image']
}) })
}
setCategoryMetadata(localCategoryMetadata)
} }
setCategoryMetadata(localCategoryMetadata)
}
); );
} }
useEffect(() => { useEffect(() => {
@ -39,32 +39,32 @@ function CategoryBar(props) {
const GlobalTheme = props.GlobalTheme; const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
if (GlobalTheme && ThemeConfig) if (GlobalTheme && ThemeConfig)
return ( return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<Row style={rowStyle}> <Row style={rowStyle}>
<center style={{marginTop: '1.5em', marginBottom: '1.5em'}}> <center style={{marginTop: '1.5em', marginBottom: '1.5em'}}>
<Col> <Col>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
{categoryMetadata.length > 0 ? {categoryMetadata.length > 0 ?
categoryMetadata.map((item, index) => ( categoryMetadata.map((item) => (
<Button <Button
key={item.id} key={item.id}
className="btn-lg" className='btn-lg'
color={`${ThemeConfig[GlobalTheme].buttonColor}`} color={`${ThemeConfig[GlobalTheme].buttonColor}`}
outline outline
active={props.currentPage === item.id} active={props.currentPage === item.id}
> >
<Link className="p-3" to={`/categories/${item.id}`}> <Link className='p-3' to={`/categories/${item.id}`}>
{item.name} {item.name}
</Link></Button> </Link></Button>
)) : <Spinner /> )) : <Spinner />
} }
</ButtonGroup> </ButtonGroup>
</Col> </Col>
</center> </center>
</Row> </Row>
</Container> </Container>
); );
}; }
export default CategoryBar; export default CategoryBar;

View File

@ -4,8 +4,6 @@ import EditableDataService from '../../services/editable-data-service';
function FileComponent(props) { function FileComponent(props) {
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
const [resourceType, setResourceType] = useState('');
const [resourceId, setResourceId] = useState('');
const handleFileChange = (event) => { const handleFileChange = (event) => {
setFile(event.target.files[0]); // Assuming single file upload setFile(event.target.files[0]); // Assuming single file upload
@ -20,7 +18,7 @@ function FileComponent(props) {
formData.append('resource_id', props.resourceId); formData.append('resource_id', props.resourceId);
try { try {
const response = await EditableDataService.createData('/data/upload/', formData); await EditableDataService.createData('/data/upload/', formData);
props.notificationToggler('Media uploaded successfully') props.notificationToggler('Media uploaded successfully')
} catch (error) { } catch (error) {
props.notificationToggler('Media upload failed', 'danger') props.notificationToggler('Media upload failed', 'danger')

View File

@ -4,14 +4,12 @@ import {
Container, Container,
Row, Row,
Col, Col,
Nav,
NavLink,
Spinner, Spinner,
Button, Button,
ButtonGroup ButtonGroup
} from 'reactstrap'; } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSun, faMoon, faPen, faBrush } from '@fortawesome/free-solid-svg-icons'; import { faBrush } from '@fortawesome/free-solid-svg-icons';
const Footer = (props) => { const Footer = (props) => {
const GlobalTheme = props.GlobalTheme; const GlobalTheme = props.GlobalTheme;
@ -21,67 +19,67 @@ const Footer = (props) => {
const setInfo = async (colorArea, color) => { const setInfo = async (colorArea, color) => {
let localThemeConfig = {...ThemeConfig} let localThemeConfig = {...ThemeConfig}
localThemeConfig[GlobalTheme].footer[colorArea] = `${color}` localThemeConfig[GlobalTheme].footer[colorArea] = `${color}`
let response = await props.setInfo('/data/shared/update/theme-config/', GlobalTheme === "darkTheme" ? { let response = await props.setInfo('/data/shared/update/theme-config/', GlobalTheme === 'darkTheme' ? {
"dark_theme": JSON.stringify(localThemeConfig[GlobalTheme]), 'dark_theme': JSON.stringify(localThemeConfig[GlobalTheme]),
}:{ }:{
"light_theme": JSON.stringify(localThemeConfig[GlobalTheme]), 'light_theme': JSON.stringify(localThemeConfig[GlobalTheme]),
}) })
if (response === 200) if (response === 200)
props.notificationToggler(`Color set for ${ThemeConfig[GlobalTheme].theme} successfully!`) props.notificationToggler(`Color set for ${ThemeConfig[GlobalTheme].theme} successfully!`)
if ([500, 404, 403].includes(response)) if ([500, 404, 403].includes(response))
props.notificationToggler("Something failed!", "danger") props.notificationToggler('Something failed!', 'danger')
} }
return ( return (
<footer className={`footer p-4 ${ThemeConfig ? ThemeConfig[GlobalTheme].footer['background'] + ' ' + ThemeConfig[GlobalTheme].footer['text'] : ""}`} id="site-footer"> <footer className={`footer p-4 ${ThemeConfig ? ThemeConfig[GlobalTheme].footer['background'] + ' ' + ThemeConfig[GlobalTheme].footer['text'] : ''}`} id='site-footer'>
<Container className='p-1'> <Container className='p-1'>
<Row> <Row>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}>
<Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<FontAwesomeIcon icon={faBrush} /> Set color <FontAwesomeIcon icon={faBrush} /> Set color
</Button> </Button>
<Button <Button
color='primary' color='primary'
onClick={() => setInfo('background', 'bg-primary')}/> onClick={() => setInfo('background', 'bg-primary')}/>
<Button <Button
color='secondary' color='secondary'
onClick={() => setInfo('background', 'bg-secondary')}/> onClick={() => setInfo('background', 'bg-secondary')}/>
<Button <Button
color='success' color='success'
onClick={() => setInfo('background', 'bg-success')}/> onClick={() => setInfo('background', 'bg-success')}/>
<Button <Button
color='danger' color='danger'
onClick={() => setInfo('background', 'bg-danger')}/> onClick={() => setInfo('background', 'bg-danger')}/>
<Button <Button
color='warning' color='warning'
onClick={() => setInfo('background', 'bg-warning')}/> onClick={() => setInfo('background', 'bg-warning')}/>
<Button <Button
color='info' color='info'
onClick={() => setInfo('background', 'bg-info')}/> onClick={() => setInfo('background', 'bg-info')}/>
<Button <Button
color='light' color='light'
onClick={() => setInfo('background', 'bg-light')}/> onClick={() => setInfo('background', 'bg-light')}/>
<Button <Button
color='dark' color='dark'
onClick={() => setInfo('background', 'bg-dark')}/> onClick={() => setInfo('background', 'bg-dark')}/>
</ButtonGroup> </ButtonGroup>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
<Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<FontAwesomeIcon icon={faBrush} /> Set footer text color <FontAwesomeIcon icon={faBrush} /> Set footer text color
</Button> </Button>
<Button <Button
color='light' color='light'
onClick={() => setInfo('text', 'text-white')}>White</Button> onClick={() => setInfo('text', 'text-white')}>White</Button>
<Button <Button
color='dark' color='dark'
onClick={() => setInfo('text', 'text-black')}>Black</Button> onClick={() => setInfo('text', 'text-black')}>Black</Button>
</ButtonGroup> </ButtonGroup>
<Col md="12"> <Col md='12'>
<div className="blogContent text-center text-md-left mt-3"> <div className='blogContent text-center text-md-left mt-3'>
{new Date().getFullYear()}, <a className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="/">{ UserData ? UserData.name : <Spinner> Loading... </Spinner> }</a> {new Date().getFullYear()}, <a className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href='/'>{ UserData ? UserData.name : <Spinner> Loading... </Spinner> }</a>
<br /> <br />
<div className='m-2'> <div className='m-2'>
{ UserData.builtWith ? <span>Built with <a target="_blank" className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="https://github.com/barunespadhy/rangolio">Rangolio</a></span>:""} { UserData.builtWith ? <span>Built with <a target='_blank' className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href='https://github.com/barunespadhy/rangolio' rel="noreferrer">Rangolio</a></span>:''}
</div> </div>
</div> </div>
</Col> </Col>

View File

@ -27,7 +27,7 @@ function MediaLister(props) {
props.notificationToggler('Media deleted') props.notificationToggler('Media deleted')
fetchMedia() fetchMedia()
}) })
.catch(error => { .catch(() => {
props.notificationToggler('Error deleting media', 'danger') props.notificationToggler('Error deleting media', 'danger')
}); });
}; };

View File

@ -21,9 +21,7 @@ function Header(props) {
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
const UserData = props.UserData; const UserData = props.UserData;
const [collapseClasses, setCollapseClasses] = useState('');
const [themeSelected, setThemeSelected] = useState('lightTheme'); const [themeSelected, setThemeSelected] = useState('lightTheme');
const [defaultThemeConfig, setDefaultThemeConfig] = useState('lightTheme');
const [modal, setModal] = useState(false) const [modal, setModal] = useState(false)
const toggle = () => {setModal(!modal)} const toggle = () => {setModal(!modal)}
@ -34,17 +32,17 @@ function Header(props) {
if (colorArea && color) if (colorArea && color)
localThemeConfig[GlobalTheme].navBar[colorArea] = `${color}` localThemeConfig[GlobalTheme].navBar[colorArea] = `${color}`
let response = await props.setInfo('/data/shared/update/theme-config/', GlobalTheme === "darkTheme" ? { let response = await props.setInfo('/data/shared/update/theme-config/', GlobalTheme === 'darkTheme' ? {
"default_theme": defaultThemeConfig, 'default_theme': defaultThemeConfig,
"dark_theme": JSON.stringify(localThemeConfig[GlobalTheme]), 'dark_theme': JSON.stringify(localThemeConfig[GlobalTheme]),
}:{ }:{
"default_theme": defaultThemeConfig, 'default_theme': defaultThemeConfig,
"light_theme": JSON.stringify(localThemeConfig[GlobalTheme]), 'light_theme': JSON.stringify(localThemeConfig[GlobalTheme]),
}) })
if (response === 200) if (response === 200)
props.notificationToggler(`Color set for ${ThemeConfig[GlobalTheme].theme} successfully!`) props.notificationToggler(`Color set for ${ThemeConfig[GlobalTheme].theme} successfully!`)
if ([500, 404, 403].includes(response)) if ([500, 404, 403].includes(response))
props.notificationToggler("Something failed!", "danger") props.notificationToggler('Something failed!', 'danger')
} }
useEffect(() => { useEffect(() => {
@ -53,124 +51,123 @@ function Header(props) {
useEffect(() => { useEffect(() => {
setThemeSelected(props.ThemeConfig.defaultTheme) setThemeSelected(props.ThemeConfig.defaultTheme)
setDefaultThemeConfig(props.ThemeConfig.defaultTheme)
}, []) }, [])
if (GlobalTheme && ThemeConfig && UserData) if (GlobalTheme && ThemeConfig && UserData)
return ( return (
<header className="header-global" id="site-header"> <header className='header-global' id='site-header'>
<Navbar className={`navbar-horizontal ${ThemeConfig[GlobalTheme].navBar['navBarTheme']} ${ThemeConfig[GlobalTheme].navBar['background']}`} <Navbar className={`navbar-horizontal ${ThemeConfig[GlobalTheme].navBar['navBarTheme']} ${ThemeConfig[GlobalTheme].navBar['background']}`}
expand="lg"> expand='lg'>
<Container> <Container>
<Publish notificationToggler={props.notificationToggler} modal={modal} toggle={toggle} /> <Publish notificationToggler={props.notificationToggler} modal={modal} toggle={toggle} />
<NavbarBrand> <NavbarBrand>
<Link to="/"> <Link to='/'>
{ {
UserData.profilePhoto !== "" ? UserData.profilePhoto !== '' ?
<img <img
style={{ width: '40px', height: '40px', objectFit: 'cover', 'marginRight': '10px' }} style={{ width: '40px', height: '40px', objectFit: 'cover', 'marginRight': '10px' }}
className="rounded-circle" className='rounded-circle'
src={EditableMediaService.getMedia(UserData.profilePhoto)} src={EditableMediaService.getMedia(UserData.profilePhoto)}
/> : "" /> : ''
} }
<Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} size="lg"> <Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`} size='lg'>
{ UserData ? UserData.name : <Spinner> Loading... </Spinner> } { UserData ? UserData.name : <Spinner> Loading... </Spinner> }
</Button> </Button>
</Link> </Link>
</NavbarBrand> </NavbarBrand>
<Nav className="ml-lg-auto" navbar> <Nav className='ml-lg-auto' navbar>
<NavItem> <NavItem>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}>
<Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<Link to="/categories"> <Link to='/categories'>
<FontAwesomeIcon icon={faPen} /> Blogs <FontAwesomeIcon icon={faPen} /> Blogs
</Link> </Link>
</Button> </Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => {setThemeSelected('lightTheme')}} onClick={() => {setThemeSelected('lightTheme')}}
active={themeSelected === 'lightTheme'} active={themeSelected === 'lightTheme'}
> >
<FontAwesomeIcon icon={faSun} /> Light Theme <FontAwesomeIcon icon={faSun} /> Light Theme
</Button> </Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => {setThemeSelected('darkTheme')}} onClick={() => {setThemeSelected('darkTheme')}}
active={themeSelected === 'darkTheme'} active={themeSelected === 'darkTheme'}
> >
<FontAwesomeIcon icon={faMoon}/> Dark Theme <FontAwesomeIcon icon={faMoon}/> Dark Theme
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px', marginRight: '15px'}}>
<Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<FontAwesomeIcon icon={faBrush} /> Set color <FontAwesomeIcon icon={faBrush} /> Set color
</Button> </Button>
<Button <Button
color='primary' color='primary'
onClick={() => setInfo('background', 'bg-primary', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-primary', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='secondary' color='secondary'
onClick={() => setInfo('background', 'bg-secondary', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-secondary', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='success' color='success'
onClick={() => setInfo('background', 'bg-success', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-success', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='danger' color='danger'
onClick={() => setInfo('background', 'bg-danger', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-danger', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='warning' color='warning'
onClick={() => setInfo('background', 'bg-warning', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-warning', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='info' color='info'
onClick={() => setInfo('background', 'bg-info', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-info', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='light' color='light'
onClick={() => setInfo('background', 'bg-light', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-light', ThemeConfig['defaultTheme'])}/>
<Button <Button
color='dark' color='dark'
onClick={() => setInfo('background', 'bg-dark', ThemeConfig['defaultTheme'])}/> onClick={() => setInfo('background', 'bg-dark', ThemeConfig['defaultTheme'])}/>
</ButtonGroup> </ButtonGroup>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
<Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<FontAwesomeIcon icon={faBrush} /> Set button color <FontAwesomeIcon icon={faBrush} /> Set button color
</Button> </Button>
<Button <Button
color='light' color='light'
onClick={() => setInfo('buttonColor', 'light', ThemeConfig['defaultTheme'])}>White</Button> onClick={() => setInfo('buttonColor', 'light', ThemeConfig['defaultTheme'])}>White</Button>
<Button <Button
color='dark' color='dark'
onClick={() => setInfo('buttonColor', 'black', ThemeConfig['defaultTheme'])}>Black</Button> onClick={() => setInfo('buttonColor', 'black', ThemeConfig['defaultTheme'])}>Black</Button>
</ButtonGroup> </ButtonGroup>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
<Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button disabled color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<FontAwesomeIcon icon={faBrush} /> Default Theme <FontAwesomeIcon icon={faBrush} /> Default Theme
</Button> </Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => setInfo(null, null, 'lightTheme')}>Light Theme</Button> onClick={() => setInfo(null, null, 'lightTheme')}>Light Theme</Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => setInfo(null, null, 'darkTheme')}>Dark Theme</Button> onClick={() => setInfo(null, null, 'darkTheme')}>Dark Theme</Button>
</ButtonGroup> </ButtonGroup>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
className='ms-5' className='ms-5'
outline outline
onClick={() => toggle()} onClick={() => toggle()}
> >
Publish Data Publish Data
</Button> </Button>
</NavItem> </NavItem>
</Nav> </Nav>
</Container> </Container>
</Navbar> </Navbar>
</header> </header>
); );
} }
export default Header; export default Header;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Collapse, Button, CardBody, Card, Alert } from 'reactstrap'; import { Collapse, CardBody, Card, Alert } from 'reactstrap';
function Notification(props) { function Notification(props) {
return ( return (

View File

@ -3,77 +3,68 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Spinner } from 'rea
import EditableDataService from '../../services/editable-data-service'; import EditableDataService from '../../services/editable-data-service';
function Publish(props) { function Publish(props) {
const [publishMethods, setPublishMethods] = useState(null) const [publishMethods, setPublishMethods] = useState(null)
const [publishSpinner, setPublishSpinner] = useState(false) const [publishSpinner, setPublishSpinner] = useState(false)
useEffect(() => { useEffect(() => {
fetchPublishMethods() fetchPublishMethods()
}, []) }, [])
const fetchPublishMethods = async () => { const fetchPublishMethods = async () => {
try { try {
const response = await EditableDataService.getData('/data/publish/methods/'); const response = await EditableDataService.getData('/data/publish/methods/');
let publishMethods = [] let publishMethods = []
Object.entries(response.data).map(([key, value]) => ( Object.entries(response.data).map(([key, value]) => (
publishMethods.push({ publishMethods.push({
"key_name": key, 'key_name': key,
"name": value["name"] 'name': value['name']
}) })
)) ))
setPublishMethods(publishMethods) setPublishMethods(publishMethods)
} catch (error) { } catch (error) {
props.notificationToggler('Error fetching methods', 'danger') props.notificationToggler('Error fetching methods', 'danger')
} }
}; };
const publishData = async (deploy_method) => { const publishData = async (deploy_method) => {
try { try {
setPublishSpinner(true) setPublishSpinner(true)
const response = await EditableDataService.getData(`/data/publish/${deploy_method}/`); await EditableDataService.getData(`/data/publish/${deploy_method}/`);
props.notificationToggler('Deployment Sucess') props.notificationToggler('Deployment Sucess')
setPublishSpinner(false) setPublishSpinner(false)
} catch (error) { } catch (error) {
props.notificationToggler('Deployment Failed', 'danger') props.notificationToggler('Deployment Failed', 'danger')
setPublishSpinner(false) setPublishSpinner(false)
} }
}; };
return (
return ( <div>
<div> <Modal isOpen={props.modal} toggle={props.toggle}>
<Modal isOpen={props.modal} toggle={props.toggle}> <ModalHeader toggle={props.toggle}>Publish Website</ModalHeader>
<ModalHeader toggle={props.toggle}>Publish Website</ModalHeader> <ModalBody>
<ModalBody> <h4>Select a publish method</h4>
<h4> {
Select a publish method publishMethods ?
</h4> publishMethods.map((item) =>
{ <div key={item.key_name} className='mb-3'>
publishMethods ? <Button
publishMethods.map((item) => color='danger'
<div className='mb-3'> onClick={() => publishData(item.key_name)}
<Button >
key={item.key_name} {item.name}
color='danger' </Button>
onClick={() => publishData(item.key_name)} </div>):''
> }
{item.name} { publishSpinner ?
</Button> <h5>Publishing <Spinner /></h5> : ''
</div> }
):"" </ModalBody>
} <ModalFooter>
{ publishSpinner ? <Button color='secondary' onClick={props.toggle}>Cancel</Button>
<h5> </ModalFooter>
Publishing <Spinner /> </Modal>
</h5> : "" </div>
} );
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={props.toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
);
} }
export default Publish; export default Publish;

View File

@ -1,7 +1,5 @@
/* /*
extension credits: Angelika Tyborska: https://angelika.me/2023/02/26/how-to-add-editing-image-alt-text-tiptap/ extension credits: Angelika Tyborska: https://angelika.me/2023/02/26/how-to-add-editing-image-alt-text-tiptap/
*/ */
import Image from '@tiptap/extension-image' import Image from '@tiptap/extension-image'
@ -32,7 +30,7 @@ function ImageNode(props) {
<span>!</span> <span>!</span>
} }
{ alt ? { alt ?
<span className="text">Alt text: "{alt}".</span>: <span className="text">Alt text: &ldquo;{alt}&ldquo;.</span>:
<span className="text">Alt text missing.</span> <span className="text">Alt text missing.</span>
} }
<Button className="edit" type="button" onClick={onEditAlt}> <Button className="edit" type="button" onClick={onEditAlt}>

View File

@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react'
import { import { Button, ButtonGroup } from 'reactstrap';
Button, ButtonGroup, Label, Input } from 'reactstrap';
import { Color } from '@tiptap/extension-color' import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item' import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style' import TextStyle from '@tiptap/extension-text-style'
@ -13,14 +12,14 @@ import { EditorProvider, useCurrentEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBold, faItalic, import { faBold, faItalic,
faUnderline, faAlignLeft, faUnderline, faAlignLeft,
faAlignCenter, faAlignRight, faAlignCenter, faAlignRight,
faAlignJustify, faHighlighter, faAlignJustify, faHighlighter,
faStrikethrough, faCode, faStrikethrough, faCode,
faListUl, faLink, faListUl, faLink,
faListOl, faQuoteLeft, faListOl, faQuoteLeft,
faQuoteRight, faRulerHorizontal, faQuoteRight, faRulerHorizontal,
faRotateLeft, faRotateRight, faImage } from '@fortawesome/free-solid-svg-icons'; faRotateLeft, faRotateRight, faImage } from '@fortawesome/free-solid-svg-icons';
import CustomImageExtension from './tiptap-custom-extensions/custom-image-extension.jsx' import CustomImageExtension from './tiptap-custom-extensions/custom-image-extension.jsx'
@ -83,57 +82,55 @@ const MenuBar = (props) => {
return ( return (
<> <>
<ButtonGroup className='mt-2'> <ButtonGroup className='mt-2'>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().setTextAlign('left').run()} onClick={() => editor.chain().focus().setTextAlign('left').run()}
outline outline active={editor.isActive('left')}
active={editor.isActive('left')} >
>
<FontAwesomeIcon icon={faAlignLeft}/> <FontAwesomeIcon icon={faAlignLeft}/>
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().setTextAlign('center').run()} onClick={() => editor.chain().focus().setTextAlign('center').run()}
outline outline
active={editor.isActive('center')} active={editor.isActive('center')}
> >
<FontAwesomeIcon icon={faAlignCenter}/> <FontAwesomeIcon icon={faAlignCenter}/>
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().setTextAlign('right').run()} onClick={() => editor.chain().focus().setTextAlign('right').run()}
outline outline active={editor.isActive('right')}
active={editor.isActive('right')} >
>
<FontAwesomeIcon icon={faAlignRight}/> <FontAwesomeIcon icon={faAlignRight}/>
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().setTextAlign('justify').run()} onClick={() => editor.chain().focus().setTextAlign('justify').run()}
outline outline
active={editor.isActive('justify')} active={editor.isActive('justify')}
> >
<FontAwesomeIcon icon={faAlignJustify}/> <FontAwesomeIcon icon={faAlignJustify}/>
</Button> </Button>
</ButtonGroup > </ButtonGroup>
<ButtonGroup className='mt-2' style={{marginLeft: '10px'}}> <ButtonGroup className='mt-2' style={{marginLeft: '10px'}}>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().toggleBold().run()} onClick={() => editor.chain().focus().toggleBold().run()}
disabled={ disabled={
!editor.can() !editor.can()
.chain() .chain()
.focus() .focus()
.toggleBold() .toggleBold()
.run() .run()
} }
outline outline
active={editor.isActive('bold')} active={editor.isActive('bold')}
> >
<FontAwesomeIcon icon={faBold}/> <FontAwesomeIcon icon={faBold}/>
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().toggleItalic().run()} onClick={() => editor.chain().focus().toggleItalic().run()}
disabled={ disabled={
@ -179,15 +176,15 @@ const MenuBar = (props) => {
> >
<FontAwesomeIcon icon={faStrikethrough}/> <FontAwesomeIcon icon={faStrikethrough}/>
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<Button <Button
className='mt-2' className='mt-2'
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={setLink} onClick={setLink}
style={{marginLeft: '10px'}} style={{marginLeft: '10px'}}
outline outline
active={editor.isActive('link')} active={editor.isActive('link')}
> >
<FontAwesomeIcon icon={faLink}/> <FontAwesomeIcon icon={faLink}/>
</Button> </Button>
<Button <Button
@ -209,7 +206,7 @@ const MenuBar = (props) => {
> >
p p
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
outline outline
@ -257,9 +254,9 @@ const MenuBar = (props) => {
> >
h6 h6
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<ButtonGroup className='mt-2' style={{marginLeft: '10px'}}> <ButtonGroup className='mt-2' style={{marginLeft: '10px'}}>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().toggleBulletList().run()} onClick={() => editor.chain().focus().toggleBulletList().run()}
outline outline
@ -275,7 +272,7 @@ const MenuBar = (props) => {
> >
<FontAwesomeIcon icon={faListOl}/> <FontAwesomeIcon icon={faListOl}/>
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<Button <Button
className='mt-2' className='mt-2'
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
@ -289,39 +286,39 @@ const MenuBar = (props) => {
<Button <Button
className='mt-2' className='mt-2'
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().setHorizontalRule().run()} onClick={() => editor.chain().focus().setHorizontalRule().run()}
outline outline
style={{marginLeft: '10px'}} style={{marginLeft: '10px'}}
> >
<FontAwesomeIcon icon={faRulerHorizontal}/> <FontAwesomeIcon icon={faRulerHorizontal}/>
</Button> </Button>
<ButtonGroup className='mt-2' style={{marginLeft: '10px'}}> <ButtonGroup className='mt-2' style={{marginLeft: '10px'}}>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().undo().run()} onClick={() => editor.chain().focus().undo().run()}
disabled={ disabled={
!editor.can() !editor.can()
.chain() .chain()
.focus() .focus()
.undo() .undo()
.run() .run()
} }
> >
<FontAwesomeIcon icon={faRotateLeft}/> <FontAwesomeIcon icon={faRotateLeft}/>
</Button> </Button>
<Button <Button
color={ThemeConfig[GlobalTheme].buttonColor} color={ThemeConfig[GlobalTheme].buttonColor}
onClick={() => editor.chain().focus().redo().run()} onClick={() => editor.chain().focus().redo().run()}
disabled={ disabled={
!editor.can() !editor.can()
.chain() .chain()
.focus() .focus()
.redo() .redo()
.run() .run()
} }
> >
<FontAwesomeIcon icon={faRotateRight}/> <FontAwesomeIcon icon={faRotateRight}/>
</Button> </Button>
</ButtonGroup> </ButtonGroup>
<Button <Button
className='mt-2 ms-2' className='mt-2 ms-2'
@ -353,13 +350,13 @@ const extensions = [
Blockquote, Blockquote,
CustomImageExtension, CustomImageExtension,
TextAlign.configure({ TextAlign.configure({
types: ['heading', 'paragraph'], types: ['heading', 'paragraph'],
}), }),
Highlight, Highlight,
Link.configure({ Link.configure({
openOnClick: false, openOnClick: false,
autolink: true, autolink: true,
}), }),
] ]
export default (props) => { export default (props) => {
@ -370,9 +367,9 @@ export default (props) => {
const toggle = () => setModal(!modal); const toggle = () => setModal(!modal);
if (props.content && GlobalTheme && ThemeConfig) if (props.content && GlobalTheme && ThemeConfig)
return ( return (
<> <>
<EditorProvider slotBefore={<MenuBar resourceType={props.resourceType} resourceId={props.resourceId} modal={modal} toggle={toggle} notificationToggler={props.notificationToggler} setContent={props.setContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>} extensions={extensions} content={props.content}></EditorProvider> <EditorProvider slotBefore={<MenuBar resourceType={props.resourceType} resourceId={props.resourceId} modal={modal} toggle={toggle} notificationToggler={props.notificationToggler} setContent={props.setContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>} extensions={extensions} content={props.content}></EditorProvider>
</> </>
) )
} }

View File

@ -1,4 +1,4 @@
import React, { Suspense, lazy } from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import './index.css' import './index.css'
import App from './App' import App from './App'

View File

@ -1,7 +0,0 @@
import axios from 'axios'
const getData = (endPoint) => {
return axios.get(`/data/${endPoint}.json`)
}
export default { getData }

View File

@ -1,5 +0,0 @@
const getMedia = (mediaPath) => {
return `/data/${mediaPath}`;
}
export default { getMedia };

View File

@ -1,9 +1,10 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import eslint from 'vite-plugin-eslint';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react(), eslint()],
base: '/static/', base: '/static/',
server: { server: {
proxy: { proxy: {

View File

@ -0,0 +1,34 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"indent": [
"error",
2
],
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"react/display-name": "off",
"quotes": [
"error",
"single"
],
"no-console": "error"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -46,9 +46,11 @@
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-react-refresh": "^0.4.6",
"vite": "^5.2.0" "vite": "^5.2.0",
"vite-plugin-eslint": "^1.8.1"
} }
} }

View File

@ -22,9 +22,9 @@ import DataService from './services/data-service'
function App() { function App() {
const [userData, setUserData] = useState(null); const [userData, setUserData] = useState(null);
const [themeConfig, setThemeConfig] = useState(null); const [themeConfig, setThemeConfig] = useState(null);
const [globalTheme, setGlobalTheme] = useState("lightTheme"); const [globalTheme, setGlobalTheme] = useState('lightTheme');
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [notificationMessage, setNotificationMessage] = useState("") const [notificationMessage, setNotificationMessage] = useState('')
const notificationToggler = (message) => { const notificationToggler = (message) => {
setIsOpen(true) setIsOpen(true)
@ -36,15 +36,13 @@ function App() {
useEffect(() => { useEffect(() => {
DataService.getData('shared/user-data').then( response =>{ DataService.getData('shared/user-data').then( response =>{
setUserData(response.data) setUserData(response.data)
document.title = response.data.name document.title = response.data.name
} })
)
DataService.getData('shared/theme-config').then( response =>{ DataService.getData('shared/theme-config').then( response =>{
setThemeConfig(response.data) setThemeConfig(response.data)
setGlobalTheme(response.data.defaultTheme) setGlobalTheme(response.data.defaultTheme)
} })
)
},[]) },[])
const themeSwitcher = (theme) => { const themeSwitcher = (theme) => {
@ -52,24 +50,24 @@ function App() {
} }
if (themeConfig) if (themeConfig)
return ( return (
<div className="app-container"> <div className='app-container'>
<Router> <Router>
<Header className="header" ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} /> <Header className='header' ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} />
<div className={`p-0 ${themeConfig[globalTheme].background}`}> <div className={`p-0 ${themeConfig[globalTheme].background}`}>
<Notification isOpen={isOpen} message={notificationMessage} /> <Notification isOpen={isOpen} message={notificationMessage} />
<Routes> <Routes>
<Route path="/" element={<Home GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} />} /> <Route path='/' element={<Home GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} />} />
<Route path="/categories" element={<CategoryList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/categories' element={<CategoryList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
<Route path="/categories/:categoryID" element={<BlogList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/categories/:categoryID' element={<BlogList notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
<Route path="/blog/:blogID" element={<Blog notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} /> <Route path='/blog/:blogID' element={<Blog notificationToggler={notificationToggler} GlobalTheme={globalTheme} ThemeConfig={themeConfig} />} />
<Route path="*" element={<NotFound validRoutes={['categories', 'blog']} GlobalTheme={globalTheme} ThemeConfig={themeConfig}/>} /> <Route path='*' element={<NotFound validRoutes={['categories', 'blog']} GlobalTheme={globalTheme} ThemeConfig={themeConfig}/>} />
</Routes> </Routes>
</div> </div>
<Footer className="footer" ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} /> <Footer className='footer' ThemeSwitcher={themeSwitcher} GlobalTheme={globalTheme} ThemeConfig={themeConfig} UserData={userData} />
</Router> </Router>
</div> </div>
); );
} }
export default App; export default App;

View File

@ -28,57 +28,55 @@ function BlogList(props) {
const [categoryData, setCategoryData] = useState(null); const [categoryData, setCategoryData] = useState(null);
const [featuredBlogData, setFeaturedBlogData] = useState('loading'); const [featuredBlogData, setFeaturedBlogData] = useState('loading');
const [currentPage, setCurrentPage] = useState('loading');
useEffect(() => { useEffect(() => {
DataService.getData(`category/${categoryID}/category-data`).then(response =>{ DataService.getData(`category/${categoryID}/category-data`).then(response =>{
setCategoryData(response.data); setCategoryData(response.data);
setFeaturedBlogData(response.data.blogMetadata.find(blog => blog.id === response.data.featuredBlog)) setFeaturedBlogData(response.data.blogMetadata.find(blog => blog.id === response.data.featuredBlog))
document.title = 'Blogs in ' + response.data.name document.title = 'Blogs in ' + response.data.name
} });
);
}, [categoryID]); }, [categoryID]);
if (GlobalTheme && ThemeConfig) { if (GlobalTheme && ThemeConfig) {
return ( return (
<Container fluid className={`mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`mb-2 p-0 ${ThemeConfig[GlobalTheme].background}`}>
<Col className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col className="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} /> <CategoryBar currentPage={categoryID} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} />
<Row className="justify-content-center align-items-center"> <Row className="justify-content-center align-items-center">
<Col className="d-flex flex-column align-items-center"> <Col className="d-flex flex-column align-items-center">
<div className="w-100"> <div className="w-100">
<Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{ width: "100%", border: "none" }}> <Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{ width: '100%', border: 'none' }}>
<CardBody> <CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1"> <CardTitle style={{ display: 'grid' }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag='h1'>
{categoryData ? `Blogs in ${categoryData.name}`:`Loading blogs ${<Spinner/>}`} {categoryData ? `Blogs in ${categoryData.name}`:`Loading blogs ${<Spinner/>}`}
</CardTitle> </CardTitle>
</CardBody> </CardBody>
</Card> </Card>
</div> </div>
<div className="container"> <div className='container'>
{ {
featuredBlogData ? featuredBlogData ?
<CardListViewer <CardListViewer
key={featuredBlogData.id} key={featuredBlogData.id}
totalItems={featuredBlogData === 'nodata' ? 0 : 1} totalItems={featuredBlogData === 'nodata' ? 0 : 1}
cardType={"longCard"} cardType={'longCard'}
resourceType={"blog"} resourceType={'blog'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}
itemObject={featuredBlogData} itemObject={featuredBlogData}
/> : '' /> : ''
} }
<Row> <Row>
{categoryData ? {categoryData ?
categoryData.blogMetadata.map((item, index) => ( categoryData.blogMetadata.map((item) => (
<Col key={item.blog_id}> <Col key={item.blog_id}>
<div className={`p-2 ml-2 ${ThemeConfig[GlobalTheme].textColor}`}> <div className={`p-2 ml-2 ${ThemeConfig[GlobalTheme].textColor}`}>
<CardListViewer <CardListViewer
totalItems={categoryData.blogMetadata.length} totalItems={categoryData.blogMetadata.length}
featuredBlog={categoryData.featuredBlog} featuredBlog={categoryData.featuredBlog}
cardType={"smallCard"} cardType={'smallCard'}
resourceType={"blog"} resourceType={'blog'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}

View File

@ -34,7 +34,7 @@ function Blog(props) {
node.attribs.target = '_blank'; node.attribs.target = '_blank';
} }
if (node.name === 'img') { if (node.name === 'img') {
const newClasses = `img-fluid mt-2 mb-2 rounded mx-auto d-block`; const newClasses = 'img-fluid mt-2 mb-2 rounded mx-auto d-block';
const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; const existingClasses = node.attribs.class ? `${node.attribs.class} ` : '';
node.attribs.class = `${existingClasses}${newClasses}`; node.attribs.class = `${existingClasses}${newClasses}`;
} }
@ -43,12 +43,11 @@ function Blog(props) {
useEffect(() => { useEffect(() => {
DataService.getData(`blog/${blogID}/blog-data`).then(response =>{ DataService.getData(`blog/${blogID}/blog-data`).then(response =>{
setBlogData(response.data) setBlogData(response.data)
const parsedContent = parse(response.data.contentBody, { replace }); const parsedContent = parse(response.data.contentBody, { replace });
setBlogContent(parsedContent); setBlogContent(parsedContent);
document.title = response.data.name document.title = response.data.name
} });
);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -59,105 +58,105 @@ function Blog(props) {
}, [GlobalTheme]) }, [GlobalTheme])
if (GlobalTheme && ThemeConfig) { if (GlobalTheme && ThemeConfig) {
return ( return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<Col xs="3" className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/categories/${blogData.parentCategory}`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col xs="3" className="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}/> <CategoryBar currentPage={blogData.parentCategory} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>
<Row className="mb-4"> <Row className="mb-4">
<Col className="p-0"> <Col className="p-0">
{ {
blogData.coverImage ? blogData.coverImage ?
<img <img
src={MediaService.getMedia(blogData.coverImage)} src={MediaService.getMedia(blogData.coverImage)}
alt="Banner" alt="Banner"
style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }} style={{ width: '100%', height: 'auto', maxHeight: '20vh', objectFit: 'cover' }}
/>:"" />:''
} }
</Col> </Col>
</Row> </Row>
<Row className="mr-2 ml-2 mb-2 mt-1 blogContent"> <Row className="mr-2 ml-2 mb-2 mt-1 blogContent">
<Col xs="3" className="d-none d-md-block"></Col> <Col xs="3" className="d-none d-md-block"></Col>
<Col xs={`${window.screen.width >= 765 ? '6':''}`}> <Col xs={`${window.screen.width >= 765 ? '6':''}`}>
<h1 className={`${ThemeConfig[GlobalTheme].textColor}`}>{blogData.name}</h1> <h1 className={`${ThemeConfig[GlobalTheme].textColor}`}>{blogData.name}</h1>
<h4 className={`${ThemeConfig[GlobalTheme].textColor}`}>{blogData.description}</h4> <h4 className={`${ThemeConfig[GlobalTheme].textColor}`}>{blogData.description}</h4>
<div> <div>
<Button <Button
color="primary" color="primary"
id="toggler" id="toggler"
style={{ style={{
marginBottom: '1rem' marginBottom: '1rem'
}} }}
> >
Share Share
</Button> </Button>
<UncontrolledCollapse toggler="#toggler"> <UncontrolledCollapse toggler="#toggler">
<Card style={{overflowX: 'auto'}}> <Card style={{overflowX: 'auto'}}>
<CardBody> <CardBody>
<ButtonGroup <ButtonGroup
vertical vertical
className="my-2" className="my-2"
> >
<Button outline> <Button outline>
<Link className="p-3" to="#" onClick={(e) => { <Link className="p-3" to="#" onClick={(e) => {
e.preventDefault(); e.preventDefault();
navigator.clipboard.writeText(window.location.href).then(() => { navigator.clipboard.writeText(window.location.href).then(() => {
props.notificationToggler("Link copied") props.notificationToggler('Link copied')
}) })
return false; return false;
}}> }}>
<FontAwesomeIcon icon={faCopy}/> Copy Link <FontAwesomeIcon icon={faCopy}/> Copy Link
</Link> </Link>
</Button> </Button>
<Button outline> <Button outline>
<Link className="p-3" to="#" onClick={(e) => { <Link className="p-3" to="#" onClick={(e) => {
e.preventDefault(); e.preventDefault();
window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(window.location.href)}`, 'facebook-share-dialog', 'width=800,height=600'); window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(window.location.href)}`, 'facebook-share-dialog', 'width=800,height=600');
return false; return false;
}}> }}>
<FontAwesomeIcon icon={faFacebook}/> Facebook <FontAwesomeIcon icon={faFacebook}/> Facebook
</Link> </Link>
</Button> </Button>
<Button outline> <Button outline>
<Link className="p-3" to="#" onClick={(e) => { <Link className="p-3" to="#" onClick={(e) => {
e.preventDefault(); e.preventDefault();
window.open(`https://www.reddit.com/submit?url=${window.location.href}&title=${blogData.name}`, 'facebook-share-dialog', 'width=800,height=600'); window.open(`https://www.reddit.com/submit?url=${window.location.href}&title=${blogData.name}`, 'facebook-share-dialog', 'width=800,height=600');
return false; return false;
}}> }}>
<FontAwesomeIcon icon={faReddit}/> Reddit <FontAwesomeIcon icon={faReddit}/> Reddit
</Link> </Link>
</Button> </Button>
<Button outline> <Button outline>
<Link className="p-3" to="#" onClick={(e) => { <Link className="p-3" to="#" onClick={(e) => {
e.preventDefault(); e.preventDefault();
window.open(`https://twitter.com/intent/tweet?text=Check%20out%20this%20article!&url=${window.location.href}`, 'facebook-share-dialog', 'width=800,height=600'); window.open(`https://twitter.com/intent/tweet?text=Check%20out%20this%20article!&url=${window.location.href}`, 'facebook-share-dialog', 'width=800,height=600');
return false; return false;
}}> }}>
<FontAwesomeIcon icon={faXTwitter}/> <FontAwesomeIcon icon={faXTwitter}/>
</Link> </Link>
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</CardBody> </CardBody>
</Card> </Card>
</UncontrolledCollapse> </UncontrolledCollapse>
</div> </div>
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs="3" className="d-none d-md-block"></Col>
</Row> </Row>
<Row className={`my-2 ${ThemeConfig[GlobalTheme].background}`}> <Row className={`my-2 ${ThemeConfig[GlobalTheme].background}`}>
<Col> <Col>
<hr style={{"borderColor": `${ThemeConfig[GlobalTheme].borderColor}`}} /> <hr style={{'borderColor': `${ThemeConfig[GlobalTheme].borderColor}`}} />
</Col> </Col>
</Row> </Row>
<Row className="mr-2 ml-2 mt-1"> <Row className="mr-2 ml-2 mt-1">
<Col xs="3" className="d-none d-md-block"></Col> <Col xs="3" className="d-none d-md-block"></Col>
<Col style={{marginBottom: '25px'}}> <Col style={{marginBottom: '25px'}}>
<div className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`}>{blogContent}</div> <div className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`}>{blogContent}</div>
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs="3" className="d-none d-md-block"></Col>
</Row> </Row>
</Container> </Container>
); );
} else { } else {
return (<Spinner />) return (<Spinner />)
} }

View File

@ -12,9 +12,7 @@ import {
Col, Col,
Container, Container,
Card, Card,
CardImg,
CardTitle, CardTitle,
CardText,
CardBody, CardBody,
Button Button
} from 'reactstrap'; } from 'reactstrap';
@ -40,28 +38,28 @@ function Blogs(props) {
return ( return (
<Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`p-0 mb-2 ${ThemeConfig[GlobalTheme].background}`}>
<Row className="justify-content-center align-items-center"> <Row className='justify-content-center align-items-center'>
<Col className="d-flex flex-column align-items-center"> <Col className='d-flex flex-column align-items-center'>
{/* Top Section - Categories */} {/* Top Section - Categories */}
<div className="w-100"> <div className='w-100'>
<Col xs="3" className="d-md-block"><Button color={ThemeConfig[GlobalTheme].buttonColor} onClick={() => navigate(`/`)} className="ms-5 mt-5" outline><FontAwesomeIcon icon={faLeftLong}/></Button></Col> <Col xs='3' className='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"}}> <Card className={`my-2 ${ThemeConfig[GlobalTheme].background}`} style={{width: '100%', border: 'none'}}>
<CardBody> <CardBody>
<CardTitle style={{ display: "grid" }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag="h1"> <CardTitle style={{ display: 'grid' }} className={`${ThemeConfig[GlobalTheme].textColor} justify-content-center`} tag='h1'>
{"Categories"} {'Categories'}
</CardTitle> </CardTitle>
</CardBody> </CardBody>
</Card> </Card>
</div> </div>
{/* Bottom Section - Category Metadata or Spinner */} {/* Bottom Section - Category Metadata or Spinner */}
<div className="" style={{ width: '70%', margin: 'auto' }}> <div className='' style={{ width: '70%', margin: 'auto' }}>
{categoryMetadata ? {categoryMetadata ?
categoryMetadata.length > 0 ? categoryMetadata.map((item, index) => ( categoryMetadata.length > 0 ? categoryMetadata.map((item) => (
<CardListViewer <CardListViewer
key={item.id} key={item.id}
totalItems={categoryMetadata.length} totalItems={categoryMetadata.length}
cardType={"longCard"} cardType={'longCard'}
resourceType={"categories"} resourceType={'categories'}
textColor={ThemeConfig[GlobalTheme].textColor} textColor={ThemeConfig[GlobalTheme].textColor}
bgColor={ThemeConfig[GlobalTheme].background} bgColor={ThemeConfig[GlobalTheme].background}
borderColor={ThemeConfig[GlobalTheme].borderColor} borderColor={ThemeConfig[GlobalTheme].borderColor}

View File

@ -15,7 +15,7 @@ function HomePage(props) {
node.attribs.target = '_blank'; node.attribs.target = '_blank';
} }
if (node.name === 'img') { if (node.name === 'img') {
const newClasses = `img-fluid mt-2 mb-2 rounded mx-auto d-block`; const newClasses = 'img-fluid mt-2 mb-2 rounded mx-auto d-block';
const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; const existingClasses = node.attribs.class ? `${node.attribs.class} ` : '';
node.attribs.class = `${existingClasses}${newClasses}`; node.attribs.class = `${existingClasses}${newClasses}`;
} }
@ -29,29 +29,29 @@ function HomePage(props) {
const UserData = props.UserData ? props.UserData : <Spinner> Loading... </Spinner> const UserData = props.UserData ? props.UserData : <Spinner> Loading... </Spinner>
const GlobalTheme = props.GlobalTheme; const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
const introContent = props.UserData ? parse(props.UserData.introContent, { replace }) : "" const introContent = props.UserData ? parse(props.UserData.introContent, { replace }) : ''
if (GlobalTheme && ThemeConfig) if (GlobalTheme && ThemeConfig)
return ( return (
<Container fluid className={`p-0 mt-5 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`p-0 mt-5 ${ThemeConfig[GlobalTheme].background}`}>
<div className="d-flex flex-column justify-content-center align-items-center min-vh-82"> <div className='d-flex flex-column justify-content-center align-items-center min-vh-82'>
<Row className="mb-4"> <Row className='mb-4'>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
<Col className="p-0"> <Col className='p-0'>
{UserData.profilePhoto !== "" ? <img style={{ width: '180px', height: '180px', objectFit: 'cover' }} className="rounded-circle" src={MediaService.getMedia(UserData.profilePhoto)} /> : ""} {UserData.profilePhoto !== '' ? <img style={{ width: '180px', height: '180px', objectFit: 'cover' }} className='rounded-circle' src={MediaService.getMedia(UserData.profilePhoto)} /> : ''}
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
</Row> </Row>
<Row className={`mb-5 mt-2 ${ThemeConfig[GlobalTheme].textColor}`}> <Row className={`mb-5 mt-2 ${ThemeConfig[GlobalTheme].textColor}`}>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
<Col className="p-4 blogContent"> <Col className='p-4 blogContent'>
<div className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`}>{introContent}</div> <div className={`blogContent ${ThemeConfig[GlobalTheme].textColor}`}>{introContent}</div>
</Col> </Col>
<Col xs="3" className="d-none d-md-block"></Col> <Col xs='3' className='d-none d-md-block'></Col>
</Row> </Row>
</div> </div>
</Container> </Container>
); );
} }
export default HomePage; export default HomePage;

View File

@ -10,33 +10,31 @@ import { Link } from 'react-router-dom';
function CardListViewer(props) { function CardListViewer(props) {
const itemObject = props.itemObject const itemObject = props.itemObject
console.log(itemObject)
if (props.totalItems > 0 && itemObject && Object.keys(itemObject).length !== 0) if (props.totalItems > 0 && itemObject && Object.keys(itemObject).length !== 0)
return ( return (
<Card color={props.borderColor} outline className={`my-2 ${props.bgColor}`} style={{"width": props.cardType === "smallCard" ? "18rem": "100%"}}> <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%" /> : ""} {itemObject.coverImage ? <CardImg src={MediaService.getMedia(itemObject.coverImage)} style={{ 'height': '180px', 'objectFit': 'cover' }} top width='100%' /> : ''}
<CardBody> <CardBody>
<Link to={`/${props.resourceType}/${itemObject.id}`}> <Link to={`/${props.resourceType}/${itemObject.id}`}>
<CardTitle className={`${props.textColor}`} tag="h5"> <CardTitle className={`${props.textColor}`} tag='h5'>
{itemObject.name} {itemObject.name}
</CardTitle> </CardTitle>
<CardText className={`${props.textColor}`}> <CardText className={`${props.textColor}`}>
{itemObject.description} {itemObject.description}
</CardText> </CardText>
<CardText> <CardText>
<small className={`${props.textColor}`}> <small className={`${props.textColor}`}>
{itemObject.tagLine} {itemObject.tagLine}
</small> </small>
</CardText> </CardText>
</Link> </Link>
</CardBody> </CardBody>
</Card> </Card>
) )
else else
return(<h3 className={`${props.textColor}`}>No items found in this section</h3>) return(<h3 className={`${props.textColor}`}>No items found in this section</h3>)
} }
export default CardListViewer export default CardListViewer

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import DataService from '../../services/data-service'; import DataService from '../../services/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, ButtonGroup } from 'reactstrap';
function CategoryBar(props) { function CategoryBar(props) {
@ -23,32 +23,32 @@ function CategoryBar(props) {
const GlobalTheme = props.GlobalTheme; const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
if (GlobalTheme && ThemeConfig) if (GlobalTheme && ThemeConfig)
return ( return (
<Container fluid className={`${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`${ThemeConfig[GlobalTheme].background}`}>
<Row style={rowStyle}> <Row style={rowStyle}>
<center style={{marginTop: '1.5em', marginBottom: '1.5em'}}> <center style={{marginTop: '1.5em', marginBottom: '1.5em'}}>
<Col> <Col>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
{categoryMetadata.length > 0 ? {categoryMetadata.length > 0 ?
categoryMetadata.map((item, index) => ( categoryMetadata.map((item) => (
<Button <Button
key={item.id} key={item.id}
className="btn-lg" className="btn-lg"
color={`${ThemeConfig[GlobalTheme].buttonColor}`} color={`${ThemeConfig[GlobalTheme].buttonColor}`}
outline outline
active={props.currentPage === item.id} active={props.currentPage === item.id}
> >
<Link className="p-3" to={`/categories/${item.id}`}> <Link className="p-3" to={`/categories/${item.id}`}>
{item.name} {item.name}
</Link></Button> </Link></Button>
)) : <Spinner /> )) : <Spinner />
} }
</ButtonGroup> </ButtonGroup>
</Col> </Col>
</center> </center>
</Row> </Row>
</Container> </Container>
); );
}; }
export default CategoryBar; export default CategoryBar;

View File

@ -13,23 +13,23 @@ const Footer = (props) => {
const UserData = props.UserData; const UserData = props.UserData;
if (UserData) if (UserData)
return ( return (
<footer className={`footer p-4 ${ThemeConfig ? ThemeConfig[GlobalTheme].footer['background'] + ' ' + ThemeConfig[GlobalTheme].footer['text'] : ""}`} id="site-footer"> <footer className={`footer p-4 ${ThemeConfig ? ThemeConfig[GlobalTheme].footer['background'] + ' ' + ThemeConfig[GlobalTheme].footer['text'] : ''}`} id="site-footer">
<Container className='p-1'> <Container className='p-1'>
<Row> <Row>
<Col md="12"> <Col md="12">
<div className="blogContent text-center text-md-left mt-3"> <div className="blogContent text-center text-md-left mt-3">
{new Date().getFullYear()}, <a className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="/">{ UserData ? UserData.name : <Spinner> Loading... </Spinner> }</a> {new Date().getFullYear()}, <a className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="/">{ UserData ? UserData.name : <Spinner> Loading... </Spinner> }</a>
<br /> <br />
<div className='m-2'> <div className='m-2'>
{ UserData.builtWith ? <span>Built with <a target="_blank" className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="https://github.com/barunespadhy/rangolio">Rangolio</a></span>:""} { UserData.builtWith ? <span>Built with <a target="_blank" className={`${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`} href="https://github.com/barunespadhy/rangolio" rel="noreferrer">Rangolio</a></span>:''}
</div>
</div> </div>
</div> </Col>
</Col> </Row>
</Row> </Container>
</Container> </footer>
</footer> );
); }
};
export default Footer; export default Footer;

View File

@ -2,15 +2,11 @@
import { import {
Navbar, Navbar,
NavbarBrand, NavbarBrand,
UncontrolledCollapse,
Row,
Col,
Nav, Nav,
NavItem, NavItem,
NavLink,
Container, Container,
Spinner, Spinner,
Button, ButtonGroup, Label, Input Button, ButtonGroup
} from 'reactstrap'; } from 'reactstrap';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import MediaService from '../../services/media-service' import MediaService from '../../services/media-service'
@ -23,8 +19,6 @@ function Header(props) {
const GlobalTheme = props.GlobalTheme; const GlobalTheme = props.GlobalTheme;
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
const UserData = props.UserData; const UserData = props.UserData;
const [collapseClasses, setCollapseClasses] = useState('');
const [themeSelected, setThemeSelected] = useState('lightTheme'); const [themeSelected, setThemeSelected] = useState('lightTheme');
useEffect(() => { useEffect(() => {
@ -36,57 +30,57 @@ function Header(props) {
}, []) }, [])
if (GlobalTheme && ThemeConfig && UserData) if (GlobalTheme && ThemeConfig && UserData)
return ( return (
<header className="header-global" id="site-header"> <header className='header-global' id='site-header'>
<Navbar className={`navbar-horizontal ${ThemeConfig[GlobalTheme].navBar['navBarTheme']} ${ThemeConfig[GlobalTheme].navBar['background']}`} <Navbar className={`navbar-horizontal ${ThemeConfig[GlobalTheme].navBar['navBarTheme']} ${ThemeConfig[GlobalTheme].navBar['background']}`}
expand="lg"> expand='lg'>
<Container> <Container>
<NavbarBrand> <NavbarBrand>
<Link to="/"> <Link to='/'>
{ {
UserData.profilePhoto !== "" ? UserData.profilePhoto !== '' ?
<img <img
style={{ width: '40px', height: '40px', objectFit: 'cover', 'marginRight': '10px' }} style={{ width: '40px', height: '40px', objectFit: 'cover', 'marginRight': '10px' }}
className="rounded-circle" className='rounded-circle'
src={MediaService.getMedia(UserData.profilePhoto)} src={MediaService.getMedia(UserData.profilePhoto)}
/> : "" /> : ''
} }
<Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} size="lg"> <Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`} size='lg'>
{ UserData ? UserData.name : <Spinner> Loading... </Spinner> } { UserData ? UserData.name : <Spinner> Loading... </Spinner> }
</Button> </Button>
</Link> </Link>
</NavbarBrand> </NavbarBrand>
<Nav className="ml-lg-auto" navbar> <Nav className='ml-lg-auto' navbar>
<NavItem> <NavItem>
<ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}> <ButtonGroup style={{marginTop: '15px', marginBottom: '15px'}}>
<Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`}> <Button color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}>
<Link to="/categories"> <Link to='/categories'>
<FontAwesomeIcon icon={faPen} /> Blogs <FontAwesomeIcon icon={faPen} /> Blogs
</Link> </Link>
</Button> </Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => {setThemeSelected('lightTheme')}} onClick={() => {setThemeSelected('lightTheme')}}
active={themeSelected === 'lightTheme'} active={themeSelected === 'lightTheme'}
> >
<FontAwesomeIcon icon={faSun} /> Light Theme <FontAwesomeIcon icon={faSun} /> Light Theme
</Button> </Button>
<Button <Button
color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ""}`} color={`${ThemeConfig ? ThemeConfig[GlobalTheme].navBar['buttonColor'] : ''}`}
outline outline
onClick={() => {setThemeSelected('darkTheme')}} onClick={() => {setThemeSelected('darkTheme')}}
active={themeSelected === 'darkTheme'} active={themeSelected === 'darkTheme'}
> >
<FontAwesomeIcon icon={faMoon}/> Dark Theme <FontAwesomeIcon icon={faMoon}/> Dark Theme
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</NavItem> </NavItem>
</Nav> </Nav>
</Container> </Container>
</Navbar> </Navbar>
</header> </header>
); );
} }
export default Header; export default Header;

View File

@ -11,17 +11,17 @@ function NotFound(props) {
const ThemeConfig = props.ThemeConfig; const ThemeConfig = props.ThemeConfig;
if (GlobalTheme && ThemeConfig) if (GlobalTheme && ThemeConfig)
return ( return (
<Container fluid className={`p-0 mt-5 ${ThemeConfig[GlobalTheme].background}`}> <Container fluid className={`p-0 mt-5 ${ThemeConfig[GlobalTheme].background}`}>
<div className="d-flex flex-column justify-content-center align-items-center min-vh-82"> <div className="d-flex flex-column justify-content-center align-items-center min-vh-82">
<Row className="mb-4"> <Row className="mb-4">
<h1 className={`${ThemeConfig[GlobalTheme].textColor}`}> <h1 className={`${ThemeConfig[GlobalTheme].textColor}`}>
404 Page not found 404 Page not found
</h1> </h1>
</Row> </Row>
</div> </div>
</Container> </Container>
); );
} }
export default NotFound; export default NotFound;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Collapse, Button, CardBody, Card, Alert } from 'reactstrap'; import { Collapse, CardBody, Card, Alert } from 'reactstrap';
function Notification(props) { function Notification(props) {
return ( return (

View File

@ -1,4 +1,4 @@
import React, { Suspense, lazy } from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import './index.css' import './index.css'
import App from './App.jsx' import App from './App.jsx'

View File

@ -1,17 +1,18 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import eslint from 'vite-plugin-eslint';
console.log(process.env.BUILD_ENV)
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
react(), react(),
eslint(),
process.env.BUILD_ENV === 'ghpages' ? { process.env.BUILD_ENV === 'ghpages' ? {
name: 'inject-ghpages-fix', name: 'inject-ghpages-fix',
transformIndexHtml(html) { transformIndexHtml(html) {
return html.replace( return html.replace(
'<div id="root"></div>', '<div id="root"></div>',
"<div id='root'></div><script type='text/javascript'>(function(l) {if (l.search[1] === '/' ) {var decoded = l.search.slice(1).split('&').map(function(s) {return s.replace(/~and~/g, '&')}).join('?');window.history.replaceState(null, null,l.pathname.slice(0, -1) + decoded + l.hash);}}(window.location))</script>" '<div id="root"></div><script type="text/javascript">(function(l) {if (l.search[1] === "/" ) {var decoded = l.search.slice(1).split("&").map(function(s) {return s.replace(/~and~/g, "&")}).join("?");window.history.replaceState(null, null,l.pathname.slice(0, -1) + decoded + l.hash);}}(window.location))</script>'
); );
} }
} : '' } : ''