: null}\n \n {(menu.items?.length) ?\n
\n \n \n {[...menuItems.map((m, k)=>)]}\n
\n
\n \n :\n null\n }\n >\n )\n\n }\n\n return null;\n\n}\n\nconst GenerateMenu = React.forwardRef((props, ref) => {\n return
;\n});\n\nfunction Menu(props) {\n\n const context = useContext(WappContext);\n const appContext = useContext(AppContext);\n\n const {wapp} = context;\n const {/*template, */storage} = appContext;\n const store = storage();\n\n wapp.styles.use(style);\n\n const {\n parentRoute = \"\",\n materialStyle = {},\n list,\n effect,\n menuKey = \"menu\",\n MoreIcon = MaterialMoreIcon\n } = props;\n\n const menuProperties = {...(props.menuProperties) ? props.menuProperties : {}, appContext, context};\n\n const [anchorEl, setAnchorEl] = useState(null);\n\n const [menu, setMenu] = useState(props.menu || []);\n\n function handleMenuOpen (event) {\n setAnchorEl(event.currentTarget);\n }\n\n function handleMenuClose () {\n setAnchorEl(null);\n }\n\n async function onClick(e, menu) {\n\n const target = menu.target || \"self\";\n const href = (typeof menu.href == \"function\") ? menu.href(menuProperties) : (menu.href) ? menu.href : \"\";\n const disableParentRoute = menu.disableParentRoute;\n const inner = !(target === \"_blank\" || (href && href.slice(0,7) === \"http://\") || (href && href.slice(0,8) === \"https://\"));\n\n if (inner) {\n e.preventDefault();\n }\n\n if (menu.onClickBefore){\n await menu.onClickBefore(menuProperties);\n }\n\n if (menu.onClick){\n if (anchorEl !== null) {\n await setAnchorEl(null);\n }\n return menu.onClick(e, menuProperties);\n }\n\n if (inner){\n\n wapp.client.history.push({\n search:\"\",\n hash:\"\",\n ...wapp.client.history.parsePath((disableParentRoute) ? href : parentRoute + href)\n });\n\n if (anchorEl !== null) {\n setAnchorEl(null);\n }\n\n }\n }\n\n const actions = {\n close: handleMenuClose,\n setMenu: setMenu\n };\n\n useEffect(function () {\n if (effect){\n effect({\n actions\n })\n }\n });\n\n const featuredMenus = [...menu.filter(function (menu) {return !!(menu.featured && !list)})].sort(defaultSort);\n const showFeaturedMenu = [...featuredMenus.filter(function (menu) {return (menu.role) ? menu.role(menuProperties) : true})];\n\n const moreMenus = [...menu.filter(function (menu) {return !(menu.featured && !list)})].sort(defaultSort);\n const showMoreMenu = [...moreMenus.filter(function (menu) {return (menu.role) ? menu.role(menuProperties) : true})];\n\n const MenuComponent = (list) ? List : MaterialMenu;\n const ItemComponent = (list) ? ListItem : MenuItem;\n const menuComponentProps = (list) ? {} : {\n anchorEl,\n keepMounted: true,\n open:Boolean(anchorEl),\n onClose:handleMenuClose\n };\n\n const generateMenuProps = {ItemComponent, menuComponentProps, parentRoute, menuProperties, onClick, style, materialStyle, appContext, context, store};\n\n return (\n <>\n {\n (showFeaturedMenu.length) ?\n [...featuredMenus.map(function (menu, key) {\n const target = menu.target || \"self\";\n const href = (typeof menu.href == \"function\") ? menu.href(menuProperties) : (menu.href) ? menu.href : \"\";\n const Icon = menu.Icon;\n const show = (menu.role) ? menu.role(menuProperties) : true;\n const onlyIcon = Icon && menu.onlyIcon;\n const inner = !(target === \"_blank\" || (href && href.slice(0,7) === \"http://\") || (href && href.slice(0,8) === \"https://\"));\n const Element = menu.Element;\n const name = (typeof menu.name == \"function\") ? menu.name(menuProperties) : menu.name;\n const disableParentRoute = menu.disableParentRoute;\n\n if (show) {\n\n if (Element){\n return (\n
\n )\n }\n\n if (onlyIcon){\n\n return (\n
\n {(Icon) ? : null}\n \n )\n\n }\n\n return (\n
: null}\n >\n {name}\n \n )\n\n }\n\n return null;\n })]\n :\n null\n }\n {\n (showMoreMenu.length > 0) ?\n <>\n {(!list) ?\n
\n \n \n : null\n }\n \n >\n :\n null\n }\n >\n )\n\n}\n\nconst StyledComponent = withMaterialStyles(materialStyle, Menu);\n\nexport default React.memo(\n StyledComponent,\n (prevProps, nextProps)=> {\n let changes = (Object.keys(prevProps).join(\"|\") !== Object.keys(nextProps).join(\"|\"));\n if (!changes && prevProps.menuProperties && nextProps.menuProperties) {\n changes = (Object.keys(prevProps.menuProperties).join(\"|\") !== Object.keys(nextProps.menuProperties).join(\"|\"));\n if (!changes) {\n changes = !!(Object.keys(prevProps.menuProperties).filter((key) => {\n return prevProps.menuProperties[key] !== nextProps.menuProperties[key];\n }).length)\n }\n }\n if (prevProps.menuKey !== nextProps.menuKey ||\n prevProps.menu !== nextProps.menu ||\n prevProps.list !== nextProps.list ||\n prevProps.parentRoute !== nextProps.parentRoute ||\n prevProps.effect !== nextProps.effect ) {\n changes = true;\n }\n return !changes;\n }\n)\n","export default function makeMaterialStyle(theme) {\n\n return {\n menu: {\n \"& $listItem\": {\n color: theme.palette.text.primary\n },\n \"& $listItemIcon\": {\n color: theme.palette.text.primary,\n }\n },\n listItem: {},\n listItemIcon: {}\n }\n}\n","import capitalize from \"./capitalize\";\n\nexport default function getStatus(p = {}) {\n\n const {user, post, appContext, /*statusManager, */name = \"post\"} = p;\n const N = capitalize(name);\n\n const {/*userStatusManager, */titles} = appContext;\n\n const isNotDeleted = post && post._status_isNotDeleted;\n const isBanned = post && post._status_isBanned;\n const isValidated = post && post._status_isValidated;\n const isApproved = post && post._status_isApproved;\n const isFeatured = post && post._status_isFeatured;\n const author = post?._author?._id || post?._author;\n const isAuthor = (user?._id && user?._id === author);\n const isAdmin = user && user._status_isFeatured;\n const isAuthorOrAdmin = !!(isAuthor || isAdmin);\n const authorIsNotDeleted = post._author_status_isNotDeleted;\n\n if (isAuthorOrAdmin){\n\n const s = (isBanned && isAdmin) ?\n titles[\"statusBanned\"+N+\"Title\"] :\n (!isNotDeleted) ?\n titles[\"statusDeleted\"+N+\"Title\"] :\n (!isValidated) ?\n titles[\"statusMissingData\"+N+\"Title\"] :\n (isFeatured && isAdmin) ?\n titles[\"statusFeatured\"+N+\"Title\"] :\n (isApproved && isAdmin) ?\n titles[\"statusApproved\"+N+\"Title\"] :\n (isAdmin) ? titles[\"statusCreated\"+N+\"Title\"] : null;\n\n return ((!authorIsNotDeleted && isAdmin && post._id !== author)) ? s + \" (\" + titles[\"statusAuthorDeleted\"+N+\"Title\"] + \")\" : s;\n\n }\n\n return null;\n}\n","import React, {useContext} from \"react\";\n\nimport clsx from \"clsx\";\n\nimport {WappContext, withWapp} from \"wapplr-react/dist/common/Wapp\";\n//import getUtils from \"wapplr-react/dist/common/Wapp/getUtils\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\n\nimport MaterialAvatar from \"@material-ui/core/Avatar\";\nimport {useTheme} from \"@material-ui/core\";\n\nimport materialStyle from \"./materialStyle\";\nimport style from \"./style.css\";\n\nfunction Avatar(props) {\n\n const context = useContext(WappContext);\n //const utils = getUtils(context);\n const {subscribe, materialStyle, user, size = \"list\", color, ...rest} = props;\n\n const {wapp} = context;\n\n wapp.styles.use(style);\n\n const src = user?.thumb || \"\";\n const firstName = user?.name?.first || \"User\";\n const aLetter = firstName.slice(0,1);\n\n const materialTheme = useTheme();\n\n return (\n
\n \n
\n )\n}\n\nconst WappComponent = withWapp(Avatar);\n\nconst StyledComponent = withMaterialStyles(materialStyle, WappComponent);\n\nexport default StyledComponent;\n","export default function makeMaterialStyle(theme) {\n return {\n root: {\n \"& $avatar\": {},\n \"& $small\": {\n width: theme.spacing(3),\n height: theme.spacing(3),\n },\n \"& $list\": {\n width: theme.spacing(4),\n height: theme.spacing(4),\n },\n \"& $big\": {\n width: theme.spacing(7),\n height: theme.spacing(7),\n },\n },\n avatar: {},\n small: {},\n list: {},\n big: {},\n }\n}\n","import React, {useEffect, useState, useContext, useRef} from \"react\";\n\nimport {WappContext} from \"wapplr-react/dist/common/Wapp\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\n\nimport MaterialDialog from \"@material-ui/core/Dialog\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogContentText from \"@material-ui/core/DialogContentText\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport Button from \"@material-ui/core/Button\";\nimport Snackbar from \"@material-ui/core/Snackbar\";\n\nimport clsx from \"clsx\";\n\nimport materialStyle from \"./materialStyle\";\nimport style from \"./style.css\";\n\nfunction Dialog(props) {\n\n const context = useContext(WappContext);\n\n const {wapp} = context;\n\n wapp.styles.use(style);\n\n const {\n cancelText = \"Cancel\",\n submitText = \"Submit\",\n dialogTitle = \"Alert\",\n dialogContent = \"Are you sure?\",\n maxWidth,\n fullWidth,\n className,\n paperClassName\n } = props;\n\n const [open, setOpen] = useState(false);\n const [show, setShow] = useState(true);\n const [snackMessage, setSnackMessage] = useState(\"\");\n const [dialogProps, setDialogProps] = useState({cancelText, submitText, dialogTitle, dialogContent});\n\n const scrollElement = useRef();\n\n const Form = dialogProps.Form;\n\n const form = useRef();\n\n const formSubmit = async function (e, formData) {\n return await handleSubmit(e, formData);\n };\n\n const handleOpen = async (dialogProps) => {\n if (dialogProps){\n await setDialogProps(dialogProps)\n }\n await setOpen(true);\n };\n\n const handleClose = async () => {\n await setOpen(false);\n };\n\n const handleCancel = (e) => {\n setOpen(false);\n if (dialogProps.onCancel){\n dialogProps.onCancel(e)\n }\n };\n\n const handleSubmit = async (e, formData) => {\n if (!formData && form.current){\n e.preventDefault();\n return await form.current.onSubmit(e);\n }\n if (e){\n e.preventDefault();\n }\n if (dialogProps.onSubmit){\n\n const response = await dialogProps.onSubmit(e, formData);\n\n if ((response && response.error) || (response && response.errors)){\n\n if (form.current){\n return response;\n } else {\n setOpen(false);\n }\n\n const message = response.error?.message || response.errors?.[\"0\"]?.message;\n\n if (message && snackMessage !== message){\n setSnackMessage(message);\n }\n\n } else if (response){\n if (dialogProps.successMessage){\n setSnackMessage(dialogProps.successMessage);\n }\n setOpen(false);\n }\n\n } else {\n setOpen(false);\n }\n };\n\n const handleCloseSnackbar = function (/*e, reason*/) {\n if (snackMessage) {\n setSnackMessage(\"\")\n }\n };\n\n const handleShow = async () => {\n if (!show) {\n await setShow(true);\n }\n };\n\n const handleHide = async () => {\n if (show) {\n await setShow(false);\n }\n };\n\n useEffect(function () {\n\n const actions = {\n open: handleOpen,\n close: handleClose,\n cancel: handleCancel,\n submit: handleSubmit,\n show: handleShow,\n hide: handleHide,\n getScrollElement: ()=>{\n return scrollElement.current\n }\n };\n\n if (props.effect) {\n props.effect({\n actions\n })\n }\n });\n\n return (\n <>\n
\n {(dialogProps.dialogTitle) ? {dialogProps.dialogTitle} : null}\n \n \n {dialogProps.dialogContent}\n \n {(Form) ?\n \n : null\n }\n \n {(dialogProps.cancelText || dialogProps.submitText) ?\n \n {(dialogProps.cancelText) ?\n \n {dialogProps.cancelText}\n : null}\n {(dialogProps.submitText) ?\n \n {dialogProps.submitText}\n : null}\n : null}\n \n
\n >\n )\n}\n\nexport default withMaterialStyles(materialStyle, Dialog);\n","export default function makeMaterialStyle(/*theme*/) {\r\n return {}\r\n}\r\n","export default function makeMaterialStyle() {\n return {}\n}\n","import React, {useContext, useState, useEffect} from \"react\";\nimport getUtils from \"wapplr-react/dist/common/Wapp/getUtils\";\nimport {WappContext, withWapp} from \"wapplr-react/dist/common/Wapp\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\n\nimport style from \"./style.css\";\nimport materialStyle from \"./materialStyle\";\nimport clsx from \"clsx\";\nimport Button from \"@material-ui/core/Button\";\n\nimport NavigateNextIcon from \"@material-ui/icons/NavigateNext\";\nimport NavigateBeforeIcon from \"@material-ui/icons/NavigateBefore\";\n\nfunction Pagination(props) {\n\n const context = useContext(WappContext);\n const {materialStyle, subscribe, data = {}} = props;\n const {wapp} = context;\n const {hasNextPage, hasPreviousPage, currentPage = 1} = data;\n const utils = getUtils(context);\n\n wapp.styles.use(style);\n\n const [url, setUrl] = useState(utils.getRequestUrl());\n\n function getBaseUrl(url) {\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const pagePart = urlWithoutSearch.match(/\\/page\\/\\d/);\n return {\n pathname: pagePart ? urlWithoutSearch.split(pagePart).join(\"\") : urlWithoutSearch,\n search: url.split(\"?\")[1]\n };\n }\n\n async function onClick(e, {action}) {\n\n e.preventDefault();\n\n const {pathname, search} = getBaseUrl(url);\n const page = (action === \"prev\") ? currentPage - 1 : (action === \"next\") ? currentPage + 1 : currentPage;\n const newUrl = search ? pathname + \"/page/\" + page + \"?\" + search : pathname + \"/page/\" + page;\n\n if (props.onClick){\n await props.onClick(e, {action, newUrl, pathname, search, page})\n } else {\n wapp.client.history.push({\n search:\"\",\n hash:\"\",\n ...wapp.client.history.parsePath(newUrl)\n });\n }\n }\n\n useEffect(function (){\n function onLocationChange(newUrl){\n if (url !== newUrl){\n setUrl(newUrl);\n }\n }\n const unsub = subscribe.locationChange(onLocationChange);\n return function useUnsubscribe(){\n unsub();\n }\n }, [subscribe, url]);\n\n return (\n
\n {(hasPreviousPage || hasNextPage) ?\n
\n {(hasPreviousPage) ?\n
\n onClick(e, {action: \"prev\"})}\n startIcon={ }\n >\n {currentPage-1}\n \n
\n : null\n }\n {(hasNextPage) ?\n
\n onClick(e, {action: \"next\"})}\n endIcon={ }\n >\n {currentPage+1}\n \n
\n : null\n }\n
\n : null\n }\n
\n )\n}\n\nconst WappComponent = withWapp(Pagination);\n\nexport default withMaterialStyles(materialStyle, WappComponent);\n","import React, {useContext, useState, useMemo} from \"react\";\n\nimport {WappContext, withWapp} from \"wapplr-react/dist/common/Wapp\";\n\nimport InputBase from \"@material-ui/core/InputBase\";\nimport SearchIcon from \"@material-ui/icons/Search\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\nimport AppContext from \"../App/context\";\n\nimport style from \"./style.css\";\nimport materialStyle from \"./materialStyle\";\n\nconst DefaultFormElement = React.memo(\n (props)=>
,\n (prevProps, nextProps)=>{\n return !((prevProps.children !== nextProps.children) ||\n prevProps.onSubmit !== nextProps.onSubmit);\n }\n);\n\nexport function Search(props) {\n\n const appContext = useContext(AppContext);\n const context = useContext(WappContext);\n const {wapp, req} = context;\n const query = req.wappRequest.query;\n\n const FormElement = useMemo(()=>props.FormElement || DefaultFormElement, [props.FormElement]);\n\n wapp.styles.use(style);\n\n const [searchText, setSearchText] = useState(typeof props.searchText == \"string\" ? props.searchText : typeof query.search == \"string\" ? query.search : \"\");\n\n const getBaseUrl = useMemo(()=>(url)=>{\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const sortPart = urlWithoutSearch.match(/\\/sort\\/\\w+/);\n const pagePart = urlWithoutSearch.match(/\\/page\\/\\d/);\n return {\n pathname: sortPart || pagePart ? urlWithoutSearch.split(sortPart).join(\"\").split(pagePart).join(\"\") : urlWithoutSearch,\n search: url.split(\"?\")[1],\n };\n }, []);\n\n const onChange = props.onChange;\n\n const onSubmit = useMemo(()=>(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (onChange){\n onChange({searchText});\n } else {\n\n const {pathname} = getBaseUrl(req.wappRequest.path);\n\n wapp.client.history.push({\n pathname: (searchText) ? pathname+\"/sort/TEXTSCORE\" : pathname,\n search:(searchText) ? \"?search=\"+searchText : \"\",\n hash:\"\"\n })\n }\n }, [getBaseUrl, onChange, req.wappRequest.path, searchText, wapp.client?.history]);\n\n const onBlur = useMemo(()=>(e) => {\n e.preventDefault();\n if (onChange){\n onChange({searchText});\n } else {\n const querySearch = query.search || \"\";\n if (searchText !== querySearch) {\n\n const {pathname} = getBaseUrl(req.wappRequest.path);\n\n wapp.client.history.push({\n pathname: (searchText) ? pathname+\"/sort/TEXTSCORE\" : pathname,\n search: (searchText) ? \"?search=\" + searchText : \"\",\n hash: \"\"\n })\n }\n }\n },[getBaseUrl, onChange, query.search, req.wappRequest.path, searchText, wapp.client?.history]);\n\n return (\n
\n
\n \n \n
\n {setSearchText(e.target.value)}}\n onBlur={onBlur}\n value={searchText}\n />\n \n
\n )\n}\n\nconst WappComponent = withWapp(Search);\n\nexport default withMaterialStyles(materialStyle, WappComponent);\n","import React, {useContext, useMemo, useState, useEffect} from \"react\";\n\nimport {WappContext, withWapp} from \"wapplr-react/dist/common/Wapp\";\nimport getUtils from \"wapplr-react/dist/common/Wapp/getUtils\";\n\nimport Select from \"@material-ui/core/Select\";\nimport FormControl from \"@material-ui/core/FormControl\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\n\nimport Search from \"./Search\";\nimport AppContext from \"../App/context\";\n\nimport style from \"./style.css\";\nimport materialStyle from \"./materialStyle\";\n\n\nfunction ListTools(props) {\n\n const context = useContext(WappContext);\n const appContext = useContext(AppContext);\n const {/*materialStyle, */\n data = {},\n disableSearch,\n disableSort,\n searchOnChange,\n name = \"post\",\n subscribe,\n SearchFormElement\n } = props;\n\n const ns = (name.endsWith(\"y\")) ? name.slice(0,-1)+\"ies\" : name+\"s\";\n\n const {wapp, req} = context;\n const utils = getUtils(context);\n const query = req.wappRequest.query;\n\n wapp.styles.use(style);\n\n const getBaseUrlForSort = useMemo(()=>(url)=>{\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const sortPart = urlWithoutSearch.match(/\\/sort\\/\\w+/);\n const pagePart = urlWithoutSearch.match(/\\/page\\/\\d/);\n return {\n pathname: sortPart || pagePart ? urlWithoutSearch.split(sortPart).join(\"\").split(pagePart).join(\"\") : urlWithoutSearch,\n search: url.split(\"?\")[1]\n };\n }, []);\n\n const getSortFromUrl = useMemo(()=>(url)=>{\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const sortPart = urlWithoutSearch.match(/\\/sort\\/\\w+/);\n return (sortPart && sortPart[0].split(\"/\")[2]) || \"\";\n }, []);\n\n const getBaseUrlForPerPage = useMemo(()=>(url)=>{\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const perPagePart = urlWithoutSearch.match(/\\/limit\\/\\w+/);\n const pagePart = urlWithoutSearch.match(/\\/page\\/\\d/);\n return {\n pathname: perPagePart || pagePart ? urlWithoutSearch.split(perPagePart).join(\"\").split(pagePart).join(\"\") : urlWithoutSearch,\n search: url.split(\"?\")[1]\n };\n }, []);\n\n const getPerPageFromUrl = useMemo(()=>(url)=>{\n let urlWithoutSearch = url.split(\"?\")[0];\n if (urlWithoutSearch.slice(-1) === \"/\") {\n urlWithoutSearch = urlWithoutSearch.slice(0,-1);\n }\n const sortPart = urlWithoutSearch.match(/\\/limit\\/\\w+/);\n return (sortPart && sortPart[0].split(\"/\")[2]) || \"\";\n }, []);\n\n const listData = utils.getGlobalState(\"res.graphql.query.\"+name+\"FindMany.listData\") || [];\n let listDataSort = listData.sort || [];\n\n const searchText = typeof props.searchText == \"string\" ? props.searchText : typeof query.search == \"string\" ? query.search : \"\";\n\n let defaultSort = (listDataSort[0]?.key) || \"\";\n\n if (searchText){\n listDataSort = [{key: \"TEXTSCORE\", listData: {title: {label: appContext.labels[ns+\"SortRelevance\"]}}}, ...listDataSort];\n if (!defaultSort){\n defaultSort = (listDataSort[0]?.key) || \"\";\n }\n }\n\n const [url, setUrl] = useState(utils.getRequestUrl());\n\n const sortFromUrl = getSortFromUrl(url);\n const [sort, setSort] = useState(props.sort || sortFromUrl || defaultSort);\n\n const perPageFromUrl = getPerPageFromUrl(url);\n const listDataPerPage = listData.perPage;\n const defaultPerPage = listDataPerPage.default || 20;\n const limitPerPage = listDataPerPage.limit || 100;\n const stepCaPerPage = Math.floor(limitPerPage/defaultPerPage) > 1 ? Math.floor(limitPerPage/defaultPerPage) : 1;\n const minPerPage = (limitPerPage/stepCaPerPage) >= 1 && (limitPerPage/stepCaPerPage) <= defaultPerPage && (limitPerPage/stepCaPerPage) <= limitPerPage ? limitPerPage/stepCaPerPage : 1;\n\n function getNumberOrNull(n){\n return (n && !isNaN(Number(n))) ? Number(n) : null;\n }\n const [perPage, setPerPage] = useState(getNumberOrNull(props.perPage) || getNumberOrNull(data.perPage) || getNumberOrNull(perPageFromUrl) || getNumberOrNull(defaultPerPage));\n\n const perPageOptionsLength = Math.floor(limitPerPage/minPerPage) > 1 ? Math.floor(limitPerPage/minPerPage) : 1;\n const perPageOptionsStep = Math.floor((limitPerPage-minPerPage)/perPageOptionsLength);\n const perPageOptions = [\n perPage,\n minPerPage,\n defaultPerPage,\n limitPerPage,\n ...[...Array(perPageOptionsLength).keys()].map((_, i)=>minPerPage+(perPageOptionsStep*(i+1)))\n ]\n .map((n)=>n > 10 ? Math.round(n/10)*10 : Math.round(n))\n .filter((n)=> n >= minPerPage && n <= limitPerPage)\n .filter((value, index, self)=>self.indexOf(value) === index)\n .filter((value)=>value > 1)\n .sort((a, b)=>{return a > b ? 1 : -1});\n\n const {currentPage = 1, itemCount = 0} = data;\n const startItem = ((currentPage-1) * perPage) + 1;\n const endItem = Math.min(currentPage * perPage, itemCount);\n const allItem = itemCount;\n\n useEffect(function (){\n\n function onLocationChange(newUrl){\n if (url !== newUrl){\n setUrl(newUrl);\n }\n }\n\n const unsub = subscribe.locationChange(onLocationChange);\n return function useUnsubscribe(){\n unsub();\n }\n }, [subscribe, url]);\n\n const sortOnChange = useMemo(()=>async (e)=>{\n e.preventDefault();\n const newSort = e.target.value || \"\";\n\n if (sort !== newSort) {\n\n const {pathname, search} = getBaseUrlForSort(url);\n const newUrl = search ? pathname + \"/sort/\" + newSort + \"?\" + search : pathname + \"/sort/\" + newSort;\n\n if (props.sortOnChange){\n await setSort(newSort);\n props.sortOnChange({sort: newSort, pathname, search});\n } else {\n wapp.client.history.push({\n search:\"\",\n hash:\"\",\n ...wapp.client.history.parsePath(newUrl)\n });\n }\n }\n }, [getBaseUrlForSort, props, sort, url, wapp.client?.history]);\n\n const perPageOnChange = useMemo(()=>async (e)=>{\n e.preventDefault();\n const newPerPage = e.target.value || \"\";\n\n if (perPage !== newPerPage) {\n\n const {pathname, search} = getBaseUrlForPerPage(url);\n const newUrl = search ? pathname + \"/limit/\" + newPerPage + \"?\" + search : pathname + \"/limit/\" + newPerPage;\n\n if (props.perPageOnChange){\n await setPerPage(newPerPage);\n props.perPageOnChange({perPage: newPerPage, pathname, search});\n } else {\n wapp.client.history.push({\n search:\"\",\n hash:\"\",\n ...wapp.client.history.parsePath(newUrl)\n });\n }\n }\n }, [perPage, getBaseUrlForPerPage, url, props, wapp.client?.history]);\n\n return (\n
\n {(!disableSearch) ?
: null}\n {(!disableSort && listDataSort.length) || (allItem > 1) ?\n
\n {(!disableSort && listDataSort.length) ?\n \n \n {(listDataSort.map((sortData, i)=>{\n const label = Object.keys(sortData.listData).filter((key)=>sortData.listData[key].label).map((key)=>sortData.listData[key].label).join(\", \") || sortData.key;\n return (\n \n {label}\n \n )\n }))}\n \n \n : null\n }\n {(allItem > 1) ?\n \n \n \n {`${startItem} - ${endItem} of ${allItem}`}\n \n {(perPageOptions.map((n, i)=>{\n return (\n \n {n.toString()}\n \n )\n }))}\n \n \n :\n null\n }\n
\n : null\n }\n
\n )\n}\n\nconst WappComponent = withWapp(ListTools);\n\nexport default withMaterialStyles(materialStyle, WappComponent);\n","import React from \"react\";\n\nconst PostContext = React.createContext({\n name:\"post\",\n user:null,\n post:null,\n parentRoute:null,\n statusManager: null,\n});\n\nexport default PostContext;\n","import React from \"react\";\n\nconst AccountContext = React.createContext({\n user:null,\n parentRoute:null,\n name:\"user\",\n page:\"\",\n statusManager:null,\n\n documentName: \"document\",\n termsSlug: \"terms\",\n privacySlug: \"privacy\",\n acceptTermsPropertyName: \"acceptTerms\",\n acceptPrivacyPropertyName: \"acceptPrivacy\",\n\n});\n\nexport default AccountContext;\n","import React, {useContext, useMemo, useState, useEffect, useRef} from \"react\";\n\nimport {WappContext} from \"wapplr-react/dist/common/Wapp\";\nimport getUtils from \"wapplr-react/dist/common/Wapp/getUtils\";\n\nimport ClearIcon from \"@material-ui/icons/Clear\";\nimport Fab from \"@material-ui/core/Fab\";\nimport AddIcon from \"@material-ui/icons/Add\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ChangeCircleOutlinedIcon from \"@material-ui/icons/ChangeCircleOutlined\";\n\nimport capitalize from \"../../utils/capitalize\";\n\nimport AppContext from \"../App/context\";\nimport PostContext from \"../Post/context\";\nimport AccountContext from \"../Account/context\";\nimport Dialog from \"../Dialog\";\n\nimport PostsList from \"../Posts\";\n\nimport style from \"./style.css\";\nimport clsx from \"clsx\";\n\nfunction Posts(props) {\n\n const {\n value,\n label,\n refPostType,\n multiple,\n onChange,\n disabled,\n clickIsOpen,\n thereAreNoEntriesMessage,\n enableNew,\n NewComponent,\n accept,\n disableFindByAuthor,\n type,\n initialMaxPerPage,\n disablePageInfo,\n disableTable,\n disableFab,\n helperText,\n error,\n selectDisabled,\n effect,\n getStageProps = ()=>({}),\n getDialogProps = ()=>({}),\n } = props;\n\n const name = refPostType;\n\n const n = name;\n const N = capitalize(n);\n const Ns = (N.endsWith(\"y\")) ? N.slice(0,-1)+\"ies\" : N+\"s\";\n\n const context = useContext(WappContext);\n const appContext = useContext(AppContext);\n const postContext = useContext(PostContext);\n const accountContext = useContext(AccountContext);\n\n const {wapp, req, res} = context;\n const utils = getUtils(context);\n\n wapp.styles.use(style);\n\n const user = postContext.user || accountContext.user;\n const post = postContext.post || accountContext.user;\n\n const defaultValue = typeof value === \"object\" && value && typeof value.length === \"number\" ? [...value] : (value && value._id) ? [value] : [];\n const [posts, setPosts] = useState(defaultValue);\n\n const postsIdsString = useMemo(()=>posts.map((post)=>post._id).join(\"\"), [posts]);\n\n const dialog = useMemo(()=>{return {}}, []);\n const dialogEffect = useMemo(() => ({actions}) => {dialog.actions = actions;}, [dialog]);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const selectedIdsForPosts = useMemo(()=>posts.map((post)=>post._id), [postsIdsString]);\n\n const NewPage = useMemo(()=>(enableNew && NewComponent) || null, [enableNew, NewComponent]);\n\n const onDeleteClick = useMemo(()=> async (e, post)=> {\n e.preventDefault();\n const np = posts.filter((p)=>p._id !== post._id);\n const value = (multiple) ? [...np.filter((p)=>p._id)] : (np[np.length-1]) ? np[np.length-1] : null;\n await setPosts((value) ? (value._id) ? [value] : value : []);\n onChange({...e, target: {...e.target, value}})\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [multiple, onChange, postsIdsString]);\n\n const onSuccess = useMemo(()=> async function onSuccess(e, newPosts) {\n const oldPosts = (type === \"simpleCheckboxes\") ? [] : posts;\n const np = [...oldPosts, ...(newPosts && newPosts.length) ? newPosts : []];\n const uniqueIds = [...new Set([...oldPosts.map((p)=>p._id), ...(newPosts && newPosts.length) ? newPosts.map((p)=>p._id) : []])];\n const up = uniqueIds.map((id)=>np.find((p)=>id===p._id));\n const value = (multiple) ? up : (up[up.length-1] ) ? up[up.length-1] : null;\n await setPosts((value) ? (value._id) ? [value] : value : []);\n onChange({...e, target: {...e.target, value}});\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [multiple, onChange, postsIdsString]);\n\n const initialAuthor = useMemo(()=>props.author || post?._author || user, [post, user, props.author]);\n const [authorId, setAuthorId] = useState( (initialAuthor?._id) ? initialAuthor?._id : (typeof initialAuthor === \"string\") ? initialAuthor : null);\n\n useEffect(()=>{\n if (props.author?._id && props.author._id !== authorId){\n setAuthorId(props.author._id);\n }\n }, [props.author, authorId]);\n\n const getPostsAndPageInfo = useMemo(()=>function getPostsAndPageInfo(response){\n if (response && response[n+\"FindMany\"]){\n response = response[n+\"FindMany\"];\n }\n const posts = response?.items || [];\n const pageInfo = response?.pageInfo || {};\n return {posts, pageInfo};\n }, [n]);\n\n const postsRef = useMemo(()=>({actions: {}}), []);\n\n const postType = wapp.getTargetObject().postTypes.findPostType({name: n});\n const postStatusManager = postType.statusManager;\n\n const createRequestParams = useMemo(()=>function createRequestParams(params = {}) {\n\n const {paginationNumber, searchText} = params;\n\n const defaultFilter = {\n ...(disableFindByAuthor) ? {} : {_author: authorId},\n _operators:{\n _status: {gt: postStatusManager.getMinStatus() - 1},\n },\n };\n\n const listData = utils.getGlobalState(\"res.graphql.query.\"+name+\"FindMany.listData\") || {};\n const listDataSort = listData.sort || [];\n\n if (searchText) {\n listDataSort.push({key: \"TEXTSCORE\"})\n }\n\n const defaultSort = listDataSort[0]?.key || \"\";\n\n let sort = (params.sort && listDataSort && listDataSort.map((p)=>p.key).find((key)=>key === params.sort)) || defaultSort;\n\n if (sort === \"TEXTSCORE\") {\n sort = \"\";\n }\n\n const perPageFormData = listData.perPage;\n const limitPerPage = perPageFormData.limit || 100;\n const defaultPerPage = (initialMaxPerPage) ? limitPerPage : (perPageFormData.default || 20);\n\n const perPage = (params.perPage && !isNaN(Number(params.perPage)) && Number(params.perPage) <= limitPerPage && Number(params.perPage) > 1) ? Number(params.perPage) : defaultPerPage;\n\n const defaultArgs = {\n ...(sort) ? {sort} : {},\n page: (typeof paginationNumber !== \"undefined\") ? paginationNumber : 1,\n perPage\n };\n\n return {\n requestName: n+\"FindMany\",\n args: accept ? {\n filter: {\n ...(searchText) ? {search: searchText} : {},\n OR: accept.split(\",\").map((mimeType)=>{\n return {\n ...defaultFilter,\n mimeType\n }\n })\n },\n ...defaultArgs\n } : {\n filter: {\n ...(searchText) ? {search: searchText} : {},\n ...defaultFilter\n },\n ...defaultArgs\n },\n req,\n res\n }\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [disableFindByAuthor, authorId, postStatusManager, name, n, accept, req, res]);\n\n const open = useMemo(()=> (disabled && type === \"simpleCheckboxes\") ? async ()=>null : async (e) => {\n\n e.preventDefault();\n\n if (dialog.actions){\n\n let searchText = \"\";\n let perPage;\n let sort;\n\n const response = await wapp.requests.send(createRequestParams({paginationNumber: 1, searchText}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n\n dialog.actions.open({\n dialogTitle: (multiple) ? appContext.titles[\"select\"+Ns] : appContext.titles[\"select\"+N],\n dialogContent: <>\n {(NewPage) ?\n
{\n const newFiles = files && files.length && files.filter((file)=>file && file._id);\n if (newFiles.length) {\n await new Promise((resolve)=>setTimeout(resolve, 500));\n await onSuccess(e, newFiles);\n }\n }}\n dropZone={false}\n multiple={multiple}\n accept={accept}\n args={(authorId) ? {_author: authorId} : {}}\n />\n : null\n }\n {\n if (searchText !== p.searchText) {\n searchText = p.searchText;\n sort = \"TEXTSCORE\";\n const response = await wapp.requests.send(createRequestParams({paginationNumber: 1, searchText, sort, perPage}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n await postsRef.actions.setPosts(posts);\n await postsRef.actions.setPageInfo(pageInfo);\n await postsRef.actions.scrollToTop();\n }\n }}\n sortOnChange={async (p)=>{\n if (sort !== p.sort) {\n sort = p.sort;\n const response = await wapp.requests.send(createRequestParams({paginationNumber: 1, searchText, sort, perPage}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n await postsRef.actions.setPosts(posts);\n await postsRef.actions.setPageInfo(pageInfo);\n await postsRef.actions.scrollToTop();\n }\n }}\n perPageOnChange={async (p)=>{\n if (perPage !== p.perPage) {\n perPage = p.perPage;\n const response = await wapp.requests.send(createRequestParams({paginationNumber: 1, searchText, sort, perPage}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n await postsRef.actions.setPosts(posts);\n await postsRef.actions.setPageInfo(pageInfo);\n await postsRef.actions.scrollToTop();\n }\n }}\n getSearchText={()=>searchText}\n getPerPage={()=>perPage}\n getSort={()=>sort}\n selectable={true}\n multiple={multiple}\n getMenu={()=>[]}\n onClickIsSelect={true}\n effect={({actions})=>{\n dialog.actions.getSelectedPosts = actions.getSelectedPosts;\n postsRef.actions = actions;\n }}\n disableAvatars={true}\n selectDisabled={({post, selected})=>{\n const disabled = (multiple && selectedIdsForPosts.indexOf(post._id) > -1) || (!multiple && selected);\n if (!disabled && selectDisabled){\n return selectDisabled({post, selected});\n }\n return disabled;\n }}\n selected={selectedIdsForPosts}\n paginationOnClick={async (e, {page})=>{\n const response = await wapp.requests.send(createRequestParams({paginationNumber:page, searchText, sort, perPage}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n await postsRef.actions.setPosts(posts);\n await postsRef.actions.setPageInfo(pageInfo);\n await postsRef.actions.scrollToTop();\n }}\n getScrollElement={()=>dialog.actions.getScrollElement()}\n posts={posts}\n pageInfo={pageInfo}\n disablePageInfo={disablePageInfo}\n disableTable={(disableTable === true || disableTable === \"dialog\")}\n {...getDialogProps()}\n />\n >,\n cancelText: appContext.labels[\"cancel\"+N+\"Text\"],\n submitText: (multiple) ? appContext.labels[\"add\"+N+\"Text\"] : appContext.labels[\"select\"+N+\"Text\"],\n onSubmit: async (e)=>{\n e.preventDefault();\n const selectedPosts = [...(dialog.actions.getSelectedPosts) ? dialog.actions.getSelectedPosts(true) : []];\n await onSuccess(e, selectedPosts);\n await dialog.actions.close();\n },\n })\n\n }\n }, [disabled, type, dialog.actions, wapp.requests, createRequestParams, getPostsAndPageInfo, multiple, appContext.titles, appContext.labels, Ns, N, NewPage, name, accept, authorId, thereAreNoEntriesMessage, selectedIdsForPosts, disablePageInfo, disableTable, onSuccess, postsRef, getDialogProps, selectDisabled]);\n\n const container = useRef();\n\n const simpleCheckboxesChildren = useMemo(()=>{\n return (type === \"simpleCheckboxes\") ?\n : null\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [type, wapp, name, thereAreNoEntriesMessage, disabled, createRequestParams, getPostsAndPageInfo, multiple, postsIdsString, onSuccess, disablePageInfo, disableTable]);\n\n const defaultRemoveMenu = {\n name: \"Remove item\",\n onClick: (clickIsOpen) ? open : (e) => onDeleteClick(e, post),\n Icon: (clickIsOpen) ? ChangeCircleOutlinedIcon : ClearIcon,\n onlyIcon: true,\n featured: true\n };\n\n useEffect(()=>{\n if (effect){\n effect({actions:{getPosts: ()=>posts}})\n }\n return ()=>{\n if (effect){\n effect({actions:{getPosts: ()=>posts}})\n }\n }\n });\n\n return (\n \n {(label) ?
{label}
: null}\n
\n {(type === \"simpleCheckboxes\") ?\n simpleCheckboxesChildren :\n
null : (clickIsOpen) ? open : onDeleteClick}\n thereAreNoEntriesClick={(disableFab && !disabled) ? open : null}\n getMenu={\n (!disabled) ? ({post}) => [{\n name: \"Remove item\",\n onClick: (clickIsOpen) ? open : (e) => onDeleteClick(e, post),\n Icon: (clickIsOpen) ? ChangeCircleOutlinedIcon : ClearIcon,\n onlyIcon: true,\n featured: true\n }]\n : () => []\n }\n {...getStageProps({defaultRemoveMenu})}\n />\n }\n {(!disabled && type !== \"simpleCheckboxes\" && !disableFab) ?\n \n : null\n }\n {(!disabled) ? : null}\n \n \n {label} \n \n \n \n {\n helperText ?\n
\n {helperText}\n \n : null\n }\n
\n )\n}\n\nfunction SimpleCheckboxes(props) {\n\n const {\n wapp,\n name,\n thereAreNoEntriesMessage,\n disabled,\n createRequestParams,\n getPostsAndPageInfo,\n multiple,\n container,\n onSelect,\n disablePageInfo,\n disableTable,\n getStageProps\n } = props;\n\n const defaultRequestProps = createRequestParams();\n\n const propsSelectedPostsIds = useMemo(()=>(props.selectedPosts?.length) ? props.selectedPosts.map((post)=>post._id) : [], [props.selectedPosts]);\n\n const [requestPropsAndSelectedPosts, setRequestPropsAndSelectedPosts] = useState({\n searchText: \"\",\n paginationNumber:1,\n perPage: defaultRequestProps.args.perPage,\n sort: defaultRequestProps.args.sort,\n selectedPosts: propsSelectedPostsIds\n });\n\n const [postsAndPageInfo, _setPostsAndPageInfo] = useState({});\n\n let setPostsAndPageInfo = async function (v) {\n await _setPostsAndPageInfo(v)\n };\n\n const {posts, pageInfo} = postsAndPageInfo;\n\n const {\n searchText,\n paginationNumber,\n perPage,\n sort,\n selectedPosts\n } = requestPropsAndSelectedPosts;\n\n const propsSelectedPostsIdsString = propsSelectedPostsIds.join(\"\");\n const selectedPostsString = selectedPosts.join(\"\");\n\n useEffect(()=>{\n if (propsSelectedPostsIdsString !== selectedPostsString){\n setRequestPropsAndSelectedPosts({\n searchText,\n paginationNumber,\n perPage,\n sort,\n selectedPosts: propsSelectedPostsIds\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [propsSelectedPostsIdsString, selectedPostsString]);\n\n const request = useMemo(()=>async function request() {\n const response = await wapp.requests.send(createRequestParams({searchText, perPage, paginationNumber, sort}));\n const {posts, pageInfo} = getPostsAndPageInfo(response);\n await setPostsAndPageInfo({posts, pageInfo});\n }, [createRequestParams, getPostsAndPageInfo, paginationNumber, perPage, searchText, sort, wapp.requests]);\n\n useEffect(()=>{\n request();\n return () =>{\n setPostsAndPageInfo = async function (v) {};\n }\n }, [request]);\n\n const postsRef = useMemo(()=>({actions: {}}), []);\n\n return (\n disabled}\n getMenu={()=>[]}\n name={name}\n disableSearch={false}\n searchOnChange={async (p)=>{\n if (searchText !== p.searchText) {\n const searchText = p.searchText;\n const sort = \"TEXTSCORE\";\n const paginationNumber = 1;\n const selectedPosts = postsRef.actions.getSelectedIds(true);\n await setRequestPropsAndSelectedPosts({paginationNumber, searchText, sort, perPage, selectedPosts});\n }\n }}\n sortOnChange={async (p)=>{\n if (sort !== p.sort) {\n const sort = p.sort;\n const paginationNumber = 1;\n const selectedPosts = postsRef.actions.getSelectedIds(true);\n await setRequestPropsAndSelectedPosts({paginationNumber, searchText, sort, perPage, selectedPosts});\n }\n }}\n perPageOnChange={async (p)=>{\n if (perPage !== p.perPage) {\n const perPage = p.perPage;\n const paginationNumber = 1;\n const selectedPosts = postsRef.actions.getSelectedIds(true);\n await setRequestPropsAndSelectedPosts({paginationNumber, searchText, sort, perPage, selectedPosts});\n }\n }}\n getSearchText={()=>searchText}\n getPerPage={()=>perPage}\n getSort={()=>sort}\n selectable={true}\n multiple={multiple}\n onClickIsSelect={true}\n effect={({actions})=>{\n postsRef.actions = actions;\n }}\n selected={selectedPosts}\n paginationOnClick={async (e, {page})=>{\n const paginationNumber = page;\n const selectedPosts = postsRef.actions.getSelectedIds(true);\n await setRequestPropsAndSelectedPosts({paginationNumber, searchText, sort, perPage, selectedPosts});\n }}\n getScrollElement={()=>container.current}\n posts={posts}\n pageInfo={pageInfo}\n onSelect={async (e, actions)=>{\n postsRef.actions = actions;\n if (onSelect) {\n const selectedPosts = postsRef.actions.getSelectedPosts(true);\n await onSelect(e, selectedPosts);\n } else {\n const selectedPosts = postsRef.actions.getSelectedIds(true);\n await setRequestPropsAndSelectedPosts({paginationNumber, searchText, sort, perPage, selectedPosts});\n }\n }}\n SearchFormElement={\n (props)=>\n {\n if (e.key === \"Enter\"){\n e.preventDefault();\n if (props.onSubmit){\n props.onSubmit(e);\n }\n }\n }}\n {...props}\n onSubmit={undefined}\n children={undefined}\n >\n {props.children}\n
\n }\n disablePageInfo={disablePageInfo}\n disableTable={disableTable}\n {...getStageProps()}\n />\n )\n\n}\n\nconst PostsWithMemo = React.memo(Posts, (prevProps, nextProps)=>{\n return !(\n (prevProps.value && nextProps.value && JSON.stringify(prevProps.value) !== JSON.stringify(nextProps.value)) ||\n (prevProps.value && !nextProps.value) ||\n (!prevProps.value && nextProps.value) ||\n (prevProps.error !== nextProps.error) ||\n (prevProps.helperText !== nextProps.helperText) ||\n (prevProps.author?._id !== nextProps.author?._id)\n );\n});\n\nexport default PostsWithMemo;\n","import React, {useContext, useState, useEffect} from \"react\";\nimport {withWapp, WappContext} from \"wapplr-react/dist/common/Wapp\";\nimport {copyObject} from \"wapplr/dist/common/utils\";\n\nimport TextField from \"@material-ui/core/TextField\";\nimport Button from \"@material-ui/core/Button\";\nimport Snackbar from \"@material-ui/core/Snackbar\";\nimport CircularProgress from \"@material-ui/core/CircularProgress\";\nimport DoneIcon from \"@material-ui/icons/Done\";\n\nimport {withMaterialStyles} from \"../Template/withMaterial\";\n\nimport Posts from \"./Posts\";\nimport Checkbox from \"./Checkbox\";\nimport NumberField from \"./NumberField\";\nimport Select from \"./Select\";\n\nimport style from \"./style.css\";\nimport materialStyle from \"./materialStyle\";\nimport PostContext from \"../Post/context\";\nimport AccountContext from \"../Account/context\";\nimport AppContext from \"../App/context\";\n\nexport const defaultComponents = {\n Button: {\n props: {\n type: \"submit\",\n variant: \"contained\",\n color: \"secondary\",\n children: \"Submit\",\n disabled: false,\n startIcon: null\n },\n Component: (props)=>{\n\n const {submitRef, effect, disabled, startIcon, ...rest} = props;\n const [progress, setProgress] = useState(false);\n const [disabledState, setDisabled] = useState(disabled || false);\n const [done, setDone] = useState(false);\n\n useEffect(()=>{\n if (effect){\n effect({\n actions: {\n setProgress,\n setDisabled,\n setDone\n }\n })\n }\n return ()=>{\n if (effect){\n effect({\n actions: {\n setProgress: async ()=>null,\n setDisabled: async ()=>null,\n setDone: async ()=>null,\n }\n })\n }\n }\n }, [effect, progress, disabled, done]);\n\n return : (progress) ? : startIcon}/>\n }\n },\n Posts: {\n props: {\n label: \"\",\n thereAreNoEntriesMessage: \"There are not entries\",\n value: \"\",\n refPostType: \"post\",\n multiple: false,\n disabled: false,\n enableNew: false,\n NewComponent: null,\n accept: \"\",\n disableFindByAuthor: false,\n type: \"\",\n initialMaxPerPage: false,\n disablePageInfo: false,\n disableTable: false,\n error: false,\n helperText: \"\",\n author: null\n },\n Component: Posts\n },\n TextField: {\n props: {\n type: \"text\",\n label: \"\",\n value: \"\",\n error: false,\n helperText: \"\",\n variant: \"outlined\",\n autoComplete: \"on\",\n disabled: false,\n multiline: false,\n minRows: null,\n maxRows: null\n },\n Component: TextField\n },\n Checkbox: {\n props: {\n label: \"\",\n value: \"\",\n error: false,\n helperText: \"\",\n },\n Component: Checkbox\n },\n Select: {\n props: {\n label: \"\",\n value: \"\",\n error: false,\n helperText: \"\",\n options: [],\n multiple: false,\n required: false,\n defaultValue: \"\"\n },\n Component: Select\n },\n NumberField: {\n props: {\n type: \"text\",\n label: \"\",\n value: \"\",\n error: false,\n helperText: \"\",\n variant: \"outlined\",\n autoComplete: \"on\",\n disabled: false,\n multiline: false,\n minRows: null,\n maxRows: null\n },\n Component: NumberField\n },\n};\n\nfunction getComponentName({data, key}) {\n if (data.componentName){\n return data.componentName;\n }\n if (key === \"submit\"){\n return \"Button\";\n }\n let componentName = \"TextField\";\n if (data.schemaType){\n switch(data.schemaType) {\n case \"String\":\n componentName = \"TextField\";\n break;\n case \"Number\":\n componentName = \"NumberField\";\n break;\n case \"Boolean\":\n componentName = \"Checkbox\";\n break;\n case \"Float\":\n componentName = \"NumberField\";\n break;\n case \"MongoID\":\n componentName = \"Posts\";\n break;\n default:\n componentName = \"TextField\";\n }\n }\n if (typeof data.options === \"object\" && data.options.length){\n componentName = \"Select\"\n }\n return componentName;\n}\n\nfunction generatePropsAndSelectComponent({components, formData, key, onSubmit, onChange, submitRef}) {\n\n const data = {...formData[key]};\n\n const componentName = getComponentName({data, key});\n\n const Component = components[componentName]?.Component || TextField;\n const defaultProps = {...components[componentName]?.props || {}};\n\n if (componentName === \"Button\" && data.label && typeof data.children == \"undefined\"){\n data.children = data.label;\n delete data.label;\n }\n\n const props = Object.keys(defaultProps).reduce(function (a, key) {\n a[key] = (typeof data[key] !== \"undefined\") ? data[key] : defaultProps[key];\n return a;\n }, {});\n\n if (props.type === \"submit\") {\n props.onClick = onSubmit;\n props.effect = ({actions})=>{submitRef.actions = actions}\n } else if (componentName) {\n props.onChange = function (e) {\n return onChange(e, key);\n };\n }\n\n if (\n typeof defaultProps.defaultValue !== \"undefined\" &&\n typeof data.defaultValue === \"undefined\" &&\n typeof data.default !== \"undefined\"\n ){\n props.defaultValue = data.default;\n }\n\n if (!props.id){\n props.id = (key.startsWith(\"record.\")) ? key.split(\"record.\")[1] : key\n }\n\n if (!props.label){\n props.label = key.slice(0,1).toUpperCase() + key.slice(1)\n }\n\n if (data.required && !data.requiredAsteriskDisableShowOnLabel && props.label && typeof props.label === \"string\" && props.label.slice(-2) !== \" *\") {\n props.label = props.label + \" *\";\n }\n\n props.key = key;\n\n return {props, Component};\n\n}\n\nfunction FormItem(props) {\n\n const {formData, fKey, components, onSubmit, onChange, submitRef, post, isAdmin, isAuthorOrAdmin} = props;\n\n const writeCondition = formData[fKey].writeCondition;\n const canWriteAdmin = (writeCondition === \"admin\");\n const canWriteAuthorOrAdmin = (post?._id && !canWriteAdmin);\n const canWriteEverybody = (!post?._id && !canWriteAdmin);\n\n const go = (\n (canWriteEverybody) ||\n (canWriteAuthorOrAdmin && isAuthorOrAdmin) ||\n (canWriteAdmin && isAdmin)\n );\n\n if (!formData[fKey].hidden && go) {\n\n const componentAndProps = generatePropsAndSelectComponent({\n components,\n formData,\n key: fKey,\n onSubmit,\n onChange,\n submitRef\n });\n\n const Component = componentAndProps.Component;\n const componentProps = componentAndProps.props;\n\n return \n\n }\n\n return null;\n\n}\n\nfunction Container(props) {\n const {container = {}, ...rest} = props;\n\n return (\n <>{(container.items) ?\n (Object.keys(container.items).length) ? \n {(container.label) ?
{container.label}
: null}\n {Object.keys(container.items).map((key)=>{\n const fKey = (container.key) ? container.key+\".\"+key : key;\n return (\n
\n )\n })}\n
: null\n :\n \n }>\n )\n}\n\nclass Form extends React.Component {\n constructor(props, context) {\n\n super(props, context);\n\n this.state = {\n snackMessage: \"\",\n formData: {...copyObject(props.formData, {keep: [\"label\", \"startIcon\"]}) || {}},\n };\n\n if (props.initialState){\n this.state = props.initialState(this.state, props, context);\n }\n\n this.onChange = this.onChange.bind(this);\n this.onSubmit = this.onSubmit.bind(this);\n this.handleCloseSnackbar = this.handleCloseSnackbar.bind(this);\n\n this.addStyle = this.addStyle.bind(this);\n this.removeStyle = null;\n this.submitRef = {};\n this.progress = false;\n this.done = false;\n this.isUnmounted = false;\n\n const {wapp} = context;\n if (wapp.target === \"node\"){\n this.addStyle();\n }\n\n }\n componentDidUpdate(prevProps) {\n if (this.props.formData !== prevProps.formData) {\n this.setState({\n formData: {...copyObject(this.props.formData, {keep: [\"label\", \"startIcon\"]}) || {}}\n })\n }\n }\n componentDidMount() {\n this.addStyle();\n this.isUnmounted = false;\n }\n componentWillUnmount() {\n if (this.removeStyle){\n this.removeStyle();\n }\n this.isUnmounted = true;\n }\n addStyle() {\n if (this.removeStyle){\n this.removeStyle();\n }\n const {wapp} = this.context;\n this.removeStyle = wapp.styles.add(style);\n }\n onChange(e, key) {\n const {onChange} = this.props;\n const formData = this.state.formData;\n const data = {...formData[key]};\n\n if (e.target.value !== data.value) {\n data.value = e.target.value;\n data.helperText = \"\";\n data.error = false;\n data.errors = [];\n const newFormData = {\n ...formData,\n [key] : data\n };\n this.setState({\n formData: {\n ...formData,\n [key] : data\n },\n snackMessage: \"\"\n });\n if (onChange){\n onChange(e, key, newFormData)\n }\n }\n }\n handleCloseSnackbar(/*e, reason*/) {\n if (this.state.snackMessage) {\n this.setState({\n snackMessage: \"\"\n })\n }\n }\n async onSubmit(e) {\n\n e.preventDefault();\n\n const progress = this.progress;\n const done = this.done;\n\n if (progress || done){\n return null;\n }\n\n this.progress = true;\n\n if (this.submitRef.actions) {\n await this.submitRef.actions.setProgress(true);\n }\n\n const {successMessage} = this.props;\n\n const {\n onSubmit = async function onSubmit(/*e, formData*/) {\n return new Promise(async function (resolve) {\n return resolve(response);\n })\n }\n } = this.props;\n\n const newState = {...this.state};\n newState.formData = {...newState.formData};\n\n const props = this.props;\n\n const {\n accountContext,\n postContext\n } = this.props;\n\n const user = postContext.user || accountContext.user;\n const post = postContext.post || accountContext.user;\n const author = post?._author?._id || post?._author;\n const isAuthor = (user?._id && user?._id === author);\n const isAdmin = user && user._status_isFeatured;\n const isAuthorOrAdmin = !!(isAuthor || isAdmin);\n\n const {\n formData,\n snackMessage\n } = newState;\n\n const sendData = Object.keys(formData).reduce(function (a, key) {\n\n const writeCondition = formData[key].writeCondition;\n const canWriteAdmin = (writeCondition === \"admin\");\n const canWriteAuthorOrAdmin = (post?._id && !canWriteAdmin);\n const canWriteEverybody = (!post?._id && !canWriteAdmin);\n\n const go = (\n (canWriteEverybody) ||\n (canWriteAuthorOrAdmin && isAuthorOrAdmin) ||\n (canWriteAdmin && isAdmin)\n );\n\n if (go){\n\n const data = copyObject(formData[key], {keep: [\"label\", \"startIcon\"]});\n const componentName = getComponentName({data, key});\n if (key !== \"submit\" && data.type !== \"submit\") {\n\n let parent = a;\n let lastKey = key;\n\n if (key.match(\".\")){\n const names = key.split(\".\");\n if (names.length > 1) {\n names.forEach(function (name, i) {\n if (!parent[name]){\n parent[name] = {};\n }\n if (i < names.length-1) {\n parent = parent[name];\n }\n lastKey = name;\n })\n }\n }\n\n if (componentName.match(\"Posts\") && data.multiple && data.value && !data.value.length){\n data.value = null;\n }\n\n if (componentName === \"Checkbox\" && !data.value) {\n data.value = false;\n }\n\n parent[lastKey] = (\n data.value ||\n (data.value === 0 && data.schemaType === \"Number\") ||\n (data.value === 0 && data.schemaType === \"Float\") ||\n (data.value === false && data.schemaType === \"Boolean\")\n ) ?\n data.value :\n (data.required) ?\n props.formData[key].default\n : null;\n\n if (componentName.match(\"Posts\")) {\n if (parent[lastKey]){\n if (typeof parent[lastKey] === \"object\" && typeof parent[lastKey].length === \"number\"){\n parent[lastKey] = parent[lastKey].map((p)=>p._id)\n } else if (parent[lastKey]._id){\n parent[lastKey] = parent[lastKey]._id;\n }\n }\n }\n\n }\n\n }\n\n return a;\n\n }, {});\n\n const response = await onSubmit(e, sendData);\n\n if ((response && response.error) || (response && response.errors)){\n\n const message = (response.error?.message) || (response.errors && response.errors[0] && response.errors[0].message) || \"Error\";\n const errors = response.error?.errors || response.errors;\n\n let shouldSetState = false;\n\n if (errors && errors.length){\n\n Object.keys(formData).forEach(function (path) {\n if (formData[path].errors) {\n formData[path].errors = [];\n }\n });\n\n errors.forEach(function (error) {\n const message = error.message;\n const path = Array.isArray(error.path) ? error.path.slice(1).join(\".\") : error.path;\n const dotParts = (typeof path === \"string\" && path) ? path.split(\".\") : [];\n if (!isNaN(Number(dotParts[dotParts.length-1])) && !formData[path]){\n const errorPath = Number(dotParts[dotParts.length-1]);\n const formPath = path.split(\".\"+dotParts[dotParts.length-1])[0];\n if (formData[formPath]){\n if (!formData[formPath].errors) {\n formData[formPath].errors = [];\n }\n formData[formPath].errors.push({path: errorPath, message});\n if (!formData[formPath].error){\n formData[formPath].error = true;\n }\n if (!formData[formPath].helperText) {\n formData[formPath].helperText = message;\n }\n shouldSetState = true;\n }\n } else if (formData[path] && formData[path].helperText !== message){\n formData[path].helperText = message;\n formData[path].error = !!(formData[path].helperText);\n shouldSetState = true;\n }\n })\n }\n\n if (message && snackMessage !== message){\n newState.snackMessage = message;\n shouldSetState = true;\n }\n\n if (shouldSetState){\n await this.setState(newState)\n }\n\n if (this.submitRef.actions) {\n await this.submitRef.actions.setProgress(false);\n }\n\n await new Promise((resolve)=>setTimeout(resolve, 1000));\n this.progress = false;\n\n } else if (response){\n\n this.done = true;\n this.progress = false;\n\n if (!this.isUnmounted) {\n\n if (this.submitRef.actions) {\n await this.submitRef.actions.setProgress(false);\n await this.submitRef.actions.setDisabled(true);\n await this.submitRef.actions.setDone(true);\n }\n\n if (successMessage) {\n newState.snackMessage = successMessage;\n await this.setState(newState);\n }\n\n }\n\n }\n\n return response;\n\n }\n async setNewFormData(formData) {\n await this.setState({formData: {...copyObject(formData, {keep: [\"label\", \"startIcon\"]}) || {}}})\n }\n render() {\n\n const onSubmit = this.onSubmit;\n const onChange = this.onChange;\n const handleCloseSnackbar = this.handleCloseSnackbar;\n\n const {\n formData,\n snackMessage\n } = this.state;\n\n const {\n components = defaultComponents,\n postContext,\n accountContext,\n appContext\n } = this.props;\n\n //const {materialStyle} = this.props;\n\n const user = postContext.user || accountContext.user;\n const post = postContext.post || accountContext.user;\n const author = post?._author?._id || post?._author;\n const isAuthor = (user?._id && user?._id === author);\n const isAdmin = user && user._status_isFeatured;\n const isAuthorOrAdmin = !!(isAuthor || isAdmin);\n\n const root = {\n key: \"\",\n items: {}\n };\n\n Object.keys(formData).sort((a, b)=>{\n const aO = formData[a].order || 0;\n const bO = formData[b].order || 0;\n if (a === \"submit\") {\n return 1;\n }\n return (aO > bO) ? 1 : (aO < bO) ? -1 : 0;\n }).forEach((key)=> {\n const dotParts = key.split(\".\");\n if (dotParts.length) {\n const name = postContext.parentRoute ? postContext.name : accountContext.name;\n dotParts.reduce((o, part, i)=>{\n if (!o[part] && i !== dotParts.length-1){\n o[part] = {\n key: dotParts.slice(0,i+1).join(\".\"),\n label: appContext.labels[name+\"ContainerLabel_\"+dotParts.slice(0,i+1).join(\"_\")],\n items: {}\n }\n } else if (i === dotParts.length-1){\n if (!formData[key].hidden) {\n o[part] = formData[key]\n }\n }\n return (o[part] && o[part].items) ? o[part].items : o[part];\n }, root.items)\n }\n }\n );\n\n const submitRef = this.submitRef;\n\n return (\n \n \n \n\n
\n )\n }\n}\n\nForm.contextType = WappContext;\n\nconst FormWithContext = React.forwardRef((props, ref) => {\n\n const postContext = useContext(PostContext);\n const accountContext = useContext(AccountContext);\n const appContext = useContext(AppContext);\n\n return (\n \n )\n}\n\nfunction ListItem(props) {\n\n const {\n i,\n refs,\n style,\n materialStyle,\n showThumbnails,\n showAvatars,\n showSubtitles,\n showTitles,\n post,\n onClick,\n avatarClick,\n status,\n appContext,\n menu,\n user,\n name,\n menuActions,\n isSelected,\n getTitle,\n selectDisabled,\n tableData,\n tableProps,\n statusManager,\n } = props;\n\n const [selected, setSelected] = useState(isSelected && isSelected(post._id));\n\n const onSelect = useMemo(()=> (e, post)=>{\n if (selectDisabled && selectDisabled({post, selected})){\n return;\n }\n setSelected(!selected);\n props.onSelect(e, post);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selected, props.onSelect]);\n\n useEffect(()=> {\n if (refs) {\n refs[post._id] = {\n deselect: () => setSelected(false),\n select: () => setSelected(true),\n toggle: () => setSelected(!selected)\n };\n }\n return ()=>{\n if (refs) {\n refs[post._id] = {\n deselect: () => () => null,\n select: () => () => null,\n toggle: () => () => null\n };\n }\n }\n }, [selected, refs, post._id]);\n\n return (\n