Created
August 5, 2021 00:14
-
-
Save jerome-diver/aa4432216636e8ce464061563945a10b to your computer and use it in GitHub Desktop.
Container UI design part
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* CRUD for container collection to call from server API | |
at /api/containers address */ | |
import { TAG, HOST, SERVER_PORT } from '../../Views/helpers/config' | |
const host = TAG + HOST + ":" + SERVER_PORT | |
const giveMe = (url, successCBK, failedCBK, finalCBK, isMounted) => { | |
fetch(url) | |
.then( response => response.json() ) | |
.then( response => { (isMounted) ? successCBK(response) : null } ) | |
.catch( error => { (isMounted) ? failedCBK( { state: true, content: error } ) : null } ) | |
.finally( () => { (isMounted) ? finalCBK(false) : null } ) | |
} | |
const getContainer = (id, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/' + id | |
giveMe(url, successCBK, failedCBK, finalCBK, isMounted) | |
} | |
const getChildrenContainersOf = (id, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/children_of/' + id | |
giveMe(url, successCBK, failedCBK, finalCBK, isMounted) | |
} | |
const getChildrenIDof = (id, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/children_ids_of/' + id | |
giveMe(url, successCBK, failedCBK, finalCBK, isMounted) | |
} | |
const getContainersOfType = (type_name, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/type/' + type_name | |
giveMe(url, successCBK, failedCBK, finalCBK, isMounted) | |
} | |
const getContainersIDofType = (type_name, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/type_id/' + type_name | |
giveMe(url, successCBK, failedCBK, finalCBK, isMounted) | |
} | |
const createContainer = (data, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/' | |
fetch(url, { | |
method: 'POST', | |
credentials: 'include', | |
headers: { 'Accept': 'application/json', | |
'Content-Type': 'application/json' }, | |
body: JSON.stringify( { data } ) }) | |
.then( response => response.json() ) | |
.then( response => { (isMounted) ? successCBK(response) : null } ) | |
.catch( error => { (isMounted) ? failedCBK( { state: true, content: error } ) : null } ) | |
.finally( () => { (isMounted) ? finalCBK(false) : null } ) | |
} | |
const updateContainer = (data, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/' + data.id + '/update' | |
console.log("Go to update a container for url:", url) | |
fetch(url, { | |
method: 'PUT', | |
credentials: 'include', | |
headers: { 'Accept': 'application/json', | |
'Content-Type': 'application/json' }, | |
body: JSON.stringify( data.body ) }) | |
.then( response => response.json() ) | |
.then( response => { (isMounted) ? successCBK(response) : null } ) | |
.catch( error => { (isMounted) ? failedCBK( { state: true, content: error } ) : null } ) | |
.finally( () => { (isMounted) ? finalCBK(false) : null } ) | |
} | |
const deleteContainer = (id, successCBK, failedCBK, finalCBK, isMounted) => { | |
const url = host + '/api/containers/' + id | |
fetch(url, { | |
method: 'DELETE', | |
credentials: 'include', | |
headers: { 'Accept': 'application/json', | |
'Content-Type': 'application/json' } }) | |
.then( response => response.json() ) | |
.then( response => { (isMounted) ? successCBK(response) : null } ) | |
.catch( error => { (isMounted) ? failedCBK( { state: true, content: error } ) : null } ) | |
.finally( () => { (isMounted) ? finalCBK(false) : null } ) | |
} | |
const crud_caller = { | |
$getContainer: getContainer, | |
$getContainersOfType: getContainersOfType, | |
$getContainersIDofType: getContainersIDofType, | |
$getChildrenIDof: getChildrenIDof, | |
$getChildrenContainersOf: getChildrenContainersOf, | |
$createContainer: createContainer, | |
$updateContainer: updateContainer, | |
$deleteContainer: deleteContainer | |
} | |
const crud_list = ['getContainer', 'getChildrenContainersOf', 'getContainersOfType','getContainersIDofType' , | |
'getChildrenIDof', 'updateContainer', 'createContainer', 'deleteContainer'] | |
export { crud_caller, crud_list, getContainer, getContainersOfType, getChildrenContainersOf, | |
createContainer, updateContainer, deleteContainer } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* HOC to Compose with Containers.component for ACTIONS */ | |
import React, { useState, useRef, useEffect, useReducer } from 'react' | |
import { crud_caller, crud_list } from '../../../../Controllers/container/action-CRUD' | |
import { useTranslation } from 'react-i18next' | |
/* Component to use with ACTIONS (Private) */ | |
const useFetch = (crud_name, data, triggers) => { | |
const [ loading, setLoading ] = useState(true) | |
const [ error, setError ] = useState({state: false, content: ""}) | |
const [ response, setResponse ] = useState({}) | |
const isMounted = useRef(null) | |
useEffect(() => { | |
isMounted.current = true | |
console.log("==> useFetch for CRUD's container function name and content:", {crud_name, data}) | |
if (crud_list.includes(crud_name)) { | |
crud_caller['$' + crud_name](data, setResponse, setError, setLoading, isMounted.current) | |
} | |
return () => (isMounted.current = false) | |
}, triggers ) | |
return { loading, error, response } | |
} | |
const dataReducer = (state, action) => { | |
console.log("dispatch for", action) | |
switch (action.type) { | |
case 'update': | |
return { crud: 'updateContainer', data: action.reference } | |
case 'delete': | |
return { crud: 'deleteContainer', data: action.reference } | |
case 'get': | |
return { crud: 'getContainer', data: action.reference } | |
} | |
} | |
/* Public ACTIONS HOC */ | |
const actionsContainerLinks = UI => { | |
const actions = (props) => { | |
const edit = () => { props.callback('edit') } | |
const remove = (content, type) => { console.log("DELETE") } | |
const cancel = () => { props.callback('normal') } | |
props = { ...props, edit, remove, cancel } | |
return <UI {...props} /> | |
} | |
return actions | |
} | |
const actionsContainer = UI => { | |
const actions = (props) => { | |
const { i18n } = useTranslation() | |
const [ validated, setValidated ] = useState(false) | |
const [ mode, setMode ] = useState('normal') | |
const [ state, dispatch ] = useReducer(dataReducer, {crud: 'getContainer', data: props.id}) | |
const { loading, error, response } = useFetch(state.crud, state.data, [i18n.language, state]) | |
const [ data, setData ] = useState({}) | |
const [ form, setForm ] = useState({}) | |
useEffect(()=>{ | |
setForm({ title: { fr: response.title, | |
en: response.title_en }, | |
content: { fr: response.content, | |
en: response.content_en } }) | |
setData (response) | |
}, [response]) | |
const change = target => value => { | |
if (target == 'title') setForm({...form, title: { [i18n.language]: value} }) | |
else setForm({...form, content: { [i18n.language]: value } }) | |
setData({title: data.title, title_en: data.title_en, content: data.content, content_en: data.content_en, | |
type_name: data.type_name, parent_id: data.parent_id, enable: data.enable, [target]: value}) | |
} | |
const update = (container) => e => { | |
e.preventDefault(); | |
const form_to_submit = e.currentTarget; | |
if (form_to_submit.checkValidity() === false) { | |
e.stopPropagation(); | |
} else { | |
dispatch({ type: 'update', reference: { id: container.id, body: data } }) | |
setValidated(true) | |
setMode('normal') | |
} | |
} | |
props = {...props, i18n, form, mode, setMode, | |
update, change, validated, loading, error, response, dispatch} | |
return <UI {...props} /> | |
} | |
return actions | |
} | |
export { useFetch, actionsContainerLinks, actionsContainer } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* it is all about a MongoDB Container linked with a Type | |
Containers component there is the main Component to use | |
to adapt return back for each situation depending on route and options | |
*/ | |
import React from 'react' | |
import { string, bool, object, func, exact, number } from 'prop-types' | |
import { Loading, Error } from './Printers.component' | |
import { useTranslation } from "react-i18next" | |
import parse from 'html-react-parser' | |
import { useParams } from 'react-router-dom' | |
import { trContainer } from '../../helpers/config' | |
import { Card, CardGroup, Jumbotron, Badge, Button, Form, InputGroup, Image, Figure } from 'react-bootstrap' | |
import loadable from '@loadable/component' | |
import { actionsContainerLinks, actionsContainer, useFetch } from './compositions/containers.actions' | |
import { statesContainerLinks, statesHeadContainer, statesContainer } from './compositions/containers.states' | |
const Editor = loadable(() => import('for-editor')) | |
/* Pure UI components with HOC to compose with states and actions components. | |
-> ContainerLinks (with both edit and normal mode) show buttons for action on own Container | |
-> HeadContainer (and both normal and edit mode) show Top head container (root of tree called) | |
-> Container (and both normal and edit mode) to show a container of the children list content | |
-> Containers UI design to organize the full page to show containers content from specific call scenari | |
*/ | |
/* Buttons actions links UI design for normal and edit mode */ | |
const ContainerLinksUInormal = ( { t, data, type, edit, remove, cancel } ) => ( | |
<> | |
<Button onClick={edit} variant="warning"> | |
{ t('containers.content.edit', { content: data.title }) } | |
</Button> | |
<Button onClick={() => remove(content, type)} variant="danger"> | |
{ t('containers.content.delete', { content: data.content }) } | |
</Button> | |
<Button onClick={cancel}>{ t('containers.content.cancel') }</Button> | |
</> | |
) | |
const ContainerLinksUIedit = ( { t, cancel } ) => ( | |
<> | |
<Button type='submit' variant='warning'> | |
{t('containers.button_submit')} | |
</Button> | |
<Button onClick={cancel}>{ t('containers.content.cancel') }</Button> | |
</> | |
) | |
/* Head Containers UI design for normal and edit mode */ | |
const HeadContainerUInormal = ({t, i18n, type_to_translate, | |
container, setMode, mode}) => ( | |
<> | |
<Jumbotron id='head-container'> | |
<h1 id='head-container-title'> | |
{trContainer(i18n.language, container).title} | |
<Badge variant='info'>{t(type_to_translate)}</Badge> | |
</h1> | |
<Figure> | |
<Figure.Image rounded fluid src={`/uploads/${container.image_link}`} /> | |
<Figure.Caption> | |
{parse(trContainer(i18n.language, container).content)} | |
</Figure.Caption> | |
</Figure> | |
<br/> | |
<ContainerLinks data={container} type={container.type_name} callback={setMode} mode={mode}/> | |
</Jumbotron> | |
</> | |
) | |
const HeadContainerUIedit = ( {t, i18n, type_to_translate, | |
validated, update, change, form, | |
container, setMode, mode}) => ( | |
<> | |
<Jumbotron id='edit-container'> | |
<Badge variant='warning'>{t('containers.edit', {type: container.type_name})}</Badge> | |
<Form onSubmit={update(container)} noValidate validated={validated}> | |
<Form.Group controlId="formBasicText"> | |
<Form.Label>Title</Form.Label> | |
<InputGroup> | |
<Form.Control type='text' | |
name="formContainerTitle" | |
onChange={change('title')} | |
value={form.title[i18n.language]}/> | |
<Badge variant='info'>{t(type_to_translate)}</Badge> | |
<Form.Control.Feedback type="invalid">Please update the title.</Form.Control.Feedback> | |
</InputGroup> | |
<Form.Text className="text-muted">{t('containers.helper.title')}</Form.Text> | |
</Form.Group> | |
<Form.Group controlId="formBasicText"> | |
<Form.Label>Content this:</Form.Label> | |
<InputGroup> | |
<Editor value={form.content[i18n.language]} | |
onChange={change('content')} | |
language='en' | |
preview={true} /> | |
<Form.Control.Feedback type="invalid">Please update the content.</Form.Control.Feedback> | |
</InputGroup> | |
<Form.Text className="text-muted">{t('containers.helper.content')}</Form.Text> | |
</Form.Group> | |
<ContainerLinks data={container} type={container.type_name} callback={setMode} mode={mode}/> | |
</Form> | |
</Jumbotron> | |
</> | |
) | |
/* Container UI design for mode edit and normal */ | |
const ContainerUInormal = ( { t, i18n, type, container_type, | |
index, container, setMode, mode } ) => ( | |
<> | |
<Card id={type+'_'+index}> | |
<Card.Img variant="top" src={`/uploads/${container.image_link}`} /> | |
<Card.Body> | |
<Card.Title> | |
{trContainer(i18n.language, container).title} | |
<Badge variant='primary'>{t(container_type)}</Badge> | |
</Card.Title> | |
<Card.Text as="div"> | |
{parse(trContainer(i18n.language, container).content)} | |
</Card.Text> | |
<Card.Link href={`/${container.type_name}/${container.id}`}> | |
{t('containers.link', { type, title: trContainer(i18n.language, container).title})} | |
</Card.Link> | |
<br/> | |
<ContainerLinks data={container} type={container_type} callback={setMode} mode={mode}/> | |
</Card.Body> | |
</Card> | |
</> | |
) | |
const ContainerUIedit = ( { t, i18n, type, container_type, | |
validated, update, change, form, | |
index, container, setMode, mode } ) => ( | |
<> | |
<Card id={type+'_'+index}> | |
<Form onSubmit={update(container)} noValidate validated={validated}> | |
<Card.Img variant="top" src={`/uploads/${container.image_link}`} /> | |
<Card.Body> | |
<Card.Title> | |
<Form.Group controlId="formBasicText"> | |
<Form.Label>Title</Form.Label> | |
<InputGroup> | |
<Form.Control type='text' | |
name="formContainerTitle" | |
onChange={change('title')} | |
value={form.title[i18n.language]}/> | |
<Badge variant='primary'>{t(container_type)}</Badge> | |
<Form.Control.Feedback type="invalid">Please update the title.</Form.Control.Feedback> | |
</InputGroup> | |
<Form.Text className="text-muted">{t('containers.helper.title')}</Form.Text> | |
</Form.Group> | |
</Card.Title> | |
<Card.Text as="div"> | |
<Form.Group controlId="formBasicText"> | |
<Form.Label>Content this:</Form.Label> | |
<InputGroup> | |
<Editor value={form.content[i18n.language]} | |
onChange={change('content')} | |
language='en' | |
preview={true} /> | |
<Form.Control.Feedback type="invalid">Please update the content.</Form.Control.Feedback> | |
</InputGroup> | |
<Form.Text className="text-muted">{t('containers.helper.content')}</Form.Text> | |
</Form.Group> | |
<ContainerLinks container={container} type={container.type_name} callback={setMode} mode={mode}/> | |
</Card.Text> | |
<Card.Link href={`/${container.type_name}/${container.id}`}>{t('containers.link', | |
{ type, title: trContainer(i18n.language, container).title})}</Card.Link> | |
<br/> | |
</Card.Body> | |
</Form> | |
</Card> | |
</> | |
) | |
/* Group of Containers */ | |
const GroupContainer = ( props ) => { | |
const { i18n, t } = useTranslation() | |
const crud_mode = (props.children) ? 'getChildrenIDof' : 'getContainersIDofType' | |
const reference = (props.children) ? props.id : props.type | |
const { loading, error, response } = useFetch(crud_mode, reference, []) | |
if (loading) return <><Loading /></> | |
else if (error.state) return <><Error title={t('error:home.title')} | |
name={error.content.name} | |
message={error.content.message} | |
open={true} /></> | |
else { | |
return <CardGroup> | |
{ response.map( (data, index) => { | |
if (data.enabled) return <Container id={data.id} key={index} index={index} /> | |
}) } | |
</CardGroup> | |
} | |
} | |
const CardTree = ({ data, link, text }) => ( | |
<> | |
</> | |
) | |
/* type is a string for type name for containers list to print for, | |
children is an Object with two keys: {other, same} with boolean values | |
to show children for same and other types. | |
head is a boolean value for show params :id container content on head first. */ | |
const Containers = ({ type, children }) => { | |
const { id } = useParams() | |
const { i18n, t } = useTranslation() | |
if (children == undefined) { // print containers list only | |
return ( | |
<> | |
<h1>{t('containers.list', {type})}</h1> | |
<hr/> | |
<GroupContainer type={type} children={false} /> | |
</> | |
) | |
} else if (!children.same && children.other) { | |
return ( | |
<> | |
<HeadContainer id={id} /> | |
<GroupContainer id={id} children={true} /> | |
</> | |
) | |
} else if (children.same && !children.other) { | |
} else if (!children.same && !children.other) { } | |
} | |
/* Compose UI with actions and states to provide each Component */ | |
const ContainerLinks = actionsContainerLinks(statesContainerLinks(ContainerLinksUInormal, ContainerLinksUIedit)) | |
const HeadContainer = actionsContainer(statesHeadContainer(HeadContainerUInormal, HeadContainerUIedit)) | |
const Container = actionsContainer(statesContainer(ContainerUInormal, ContainerUIedit)) | |
/* Props Types checking part... */ | |
Containers.propTypes = { | |
type: string.isRequired, | |
children: exact({ | |
same: bool.isRequired, | |
other: bool.isRequired }), | |
} | |
Container.propTypes = { | |
id: string.isRequired, | |
index: number.isRequired | |
} | |
GroupContainer.propTypes = { | |
type: string, | |
children: bool | |
} | |
GroupContainer.defaultProps = { | |
children: false | |
} | |
ContainerLinks.propTypes = { | |
type: string.isRequired, | |
callback: func.isRequired | |
} | |
HeadContainer.propTypes = { | |
id: string.isRequired, | |
} | |
export default Containers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* HOC to Compose with Containers.components STATES */ | |
import React, { useState } from 'react' | |
import { useTranslation } from 'react-i18next' | |
import { colorType } from '../../../helpers/config' | |
import { useAuthenticate, itsMine, canModify } from '../../../../Controllers/context/authenticate' | |
import { Error, Loading } from '../Printers.component' | |
const statesContainerLinks = (UInormal, UIedit) => { | |
const states = (props) => { | |
const { t } = useTranslation() | |
const { getUser, getRole } = useAuthenticate() | |
const user = getUser() | |
const role = getRole() | |
props = { ...props, t} | |
if (canModify(role, props.type) || itsMine(user, props.data)) { | |
switch(props.mode) { | |
case 'normal': | |
return <UInormal {...props} /> | |
case 'edit': | |
return <UIedit {...props} /> | |
} | |
} else return null | |
} | |
return states | |
} | |
const statesHeadContainer = (UInormal, UIedit) => { | |
const states = (props) => { | |
const { t } = useTranslation() | |
const type_to_translate = "containers." + props.response.type_name | |
props = {...props, t, type_to_translate} | |
if (props.loading) return <><Loading /></> | |
if(props.error.state) return <><Error title={t('error:home.title')} | |
name={props.error.content.name} | |
message={props.error.content.message} | |
open={true} /></> | |
else { | |
switch (props.mode) { | |
case 'edit': | |
return <> | |
<style type='text/css'>{` | |
#edit-container-title h1 { display: inline-block; } | |
#edit-container-text { font-family: 'Santana'; } | |
.badge { | |
vertical-align: middle; | |
font-family: 'Source Code Pro'; } | |
#edit-container { | |
background-image: linear-gradient(to bottom left, rgb(199,19,99), rgb(44,32,22)); | |
background-color: rgba(199,2,2,0.75) } | |
`}</style> | |
<UIedit {...props} container={props.response} /> | |
</> | |
case 'normal': | |
return <> | |
<style type='text/css'>{` | |
#head-container-title h1 { display: inline-block; } | |
#head-container-text { font-family: 'Santana'; } | |
.badge { | |
vertical-align: middle; | |
font-family: 'Source Code Pro'; } | |
#head-container { | |
background-image: linear-gradient(to bottom left, rgb(99,99,99), rgb(44,32,22)); | |
background-color: rgba(99,99,99,0.75) } | |
`}</style> | |
<UInormal {...props} container={props.response} /> | |
</> | |
} | |
} | |
} | |
return states | |
} | |
const statesContainer = (UInormal, UIedit) => { | |
const states = (props) => { | |
const { t } = useTranslation() | |
const type = props.response.type_name | |
const container_type = "containers." + type | |
props = {...props, t, type, container_type} | |
if (props.loading) return <><Loading /></> | |
if(props.error.state) return <><Error title={t('error:home.title')} | |
name={props.error.content.name} | |
message={props.error.content.message} | |
open={true} /></> | |
else { | |
switch (props.mode) { | |
case 'edit': | |
return <> | |
<style type='text/css'>{` | |
#${type+'_'+props.index} { | |
margin: 5px; | |
min-width: 520px; | |
max-width: 600px; | |
border: 1px solid ${colorType(type)}; } | |
.badge { | |
vertical-align: middle; | |
font-family: 'Source Code Pro';} | |
#${type+'_'+props.index} .card-body { | |
background-color: rgba(55, 44, 44, 0.85); | |
background-image: linear-gradient(to bottom left, rgb(199,19,99), rgb(44,32,22)); } | |
#${type+'_'+props.index} .card-title .h5 { display: inline; } | |
`}</style> | |
<UIedit {...props} container={props.response} /> | |
</> | |
case 'normal': | |
return <> | |
<style type='text/css'>{` | |
#${type+'_'+props.index} { | |
margin: 5px; | |
min-width: 520px; | |
max-width: 600px; | |
border: 1px solid ${colorType(type)}; } | |
.badge { | |
vertical-align: middle; | |
font-family: 'Source Code Pro';} | |
#${type+'_'+props.index} .card-body { | |
background-color: rgba(55, 44, 44, 0.85); | |
background-image: linear-gradient(to bottom left, rgb(99,99,99), rgb(44,32,22)); } | |
#${type+'_'+props.index} .card-title .h5 { display: inline; } | |
`}</style> | |
<UInormal {...props} container={props.response} /> | |
</> | |
} | |
} | |
} | |
return states | |
} | |
export { statesContainerLinks, statesHeadContainer, statesContainer } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment