@compill/admin 1.0.100 → 1.0.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +50 -0
- package/{src → dist}/lib/SectionTitle.d.ts +0 -1
- package/dist/lib/SectionTitle.jsx +3 -0
- package/{src → dist}/lib/breadcrumbs/BreadCrumbs.d.ts +0 -1
- package/dist/lib/breadcrumbs/BreadCrumbs.jsx +43 -0
- package/dist/lib/buttons/DialogButton.jsx +17 -0
- package/{src → dist}/lib/buttons/InvalidateButton.d.ts +0 -1
- package/dist/lib/buttons/InvalidateButton.jsx +10 -0
- package/{src → dist}/lib/buttons/NavigateButton.d.ts +0 -1
- package/dist/lib/buttons/NavigateButton.jsx +13 -0
- package/{src → dist}/lib/buttons/PublishButton.d.ts +0 -1
- package/dist/lib/buttons/PublishButton.jsx +18 -0
- package/{src → dist}/lib/buttons/UpdateButton.d.ts +0 -1
- package/dist/lib/buttons/UpdateButton.jsx +12 -0
- package/{src → dist}/lib/buttons/ViewButton.d.ts +1 -2
- package/dist/lib/buttons/ViewButton.jsx +14 -0
- package/{src → dist}/lib/cells/OrderCell.d.ts +0 -1
- package/dist/lib/cells/OrderCell.jsx +35 -0
- package/{src → dist}/lib/json/DetailsView.d.ts +0 -1
- package/dist/lib/json/DetailsView.jsx +96 -0
- package/{src → dist}/lib/json/EditItemView.d.ts +0 -1
- package/dist/lib/json/EditItemView.jsx +5 -0
- package/{src → dist}/lib/json/MultiQueryWrapper.d.ts +0 -1
- package/dist/lib/json/MultiQueryWrapper.jsx +60 -0
- package/{src → dist}/lib/json/QueryWrapper.d.ts +0 -1
- package/dist/lib/json/QueryWrapper.jsx +28 -0
- package/{src → dist}/lib/json/ScreenRenderer.d.ts +0 -1
- package/dist/lib/json/ScreenRenderer.jsx +18 -0
- package/{src → dist}/lib/json/ScreenTopBar.d.ts +0 -1
- package/dist/lib/json/ScreenTopBar.jsx +39 -0
- package/{src → dist}/lib/json/TabbedView.d.ts +0 -1
- package/dist/lib/json/TabbedView.jsx +28 -0
- package/{src → dist}/lib/json/buttons/ActionButton.d.ts +0 -1
- package/dist/lib/json/buttons/ActionButton.jsx +13 -0
- package/{src → dist}/lib/json/buttons/ConfirmationActionButton.d.ts +0 -1
- package/dist/lib/json/buttons/ConfirmationActionButton.jsx +37 -0
- package/{src → dist}/lib/json/dialog/DialogRenderer.d.ts +0 -1
- package/dist/lib/json/dialog/DialogRenderer.jsx +13 -0
- package/dist/lib/json/dialog/ItemDeleteDialog.jsx +36 -0
- package/dist/lib/json/dialog/ItemEditDialog.jsx +124 -0
- package/{src → dist}/lib/json/dialog/MultiQueryWrapperDialog.d.ts +0 -1
- package/dist/lib/json/dialog/MultiQueryWrapperDialog.jsx +22 -0
- package/{src → dist}/lib/json/dialog/QueryWrapperDialog.d.ts +0 -1
- package/dist/lib/json/dialog/QueryWrapperDialog.jsx +19 -0
- package/{src → dist}/lib/json/table/RefreshButton.d.ts +0 -1
- package/dist/lib/json/table/RefreshButton.jsx +12 -0
- package/{src → dist}/lib/json/table/TableRowActionsView.d.ts +0 -1
- package/dist/lib/json/table/TableRowActionsView.jsx +45 -0
- package/{src → dist}/lib/json/table/TableRowPublishPostButton.d.ts +0 -1
- package/dist/lib/json/table/TableRowPublishPostButton.jsx +21 -0
- package/{src → dist}/lib/json/table/TableView.d.ts +0 -1
- package/dist/lib/json/table/TableView.jsx +100 -0
- package/dist/lib/json/table/TableViewContext.jsx +57 -0
- package/dist/lib/json/table/useTableProps.jsx +56 -0
- package/{src → dist}/lib/json/types/DetailsView.d.ts +0 -1
- package/dist/lib/json/types/DetailsView.js +1 -0
- package/dist/lib/json/types/EditItemDialog.js +1 -0
- package/dist/lib/json/types/MultiQueryWrapper.js +1 -0
- package/dist/lib/json/types/MultiQueryWrapperDialog.js +1 -0
- package/dist/lib/json/types/QueryWrapper.js +1 -0
- package/dist/lib/json/types/QueryWrapperDialog.js +1 -0
- package/dist/lib/json/types/ScreenConfig.js +1 -0
- package/{src → dist}/lib/json/types/TabbedView.d.ts +0 -1
- package/dist/lib/json/types/TabbedView.js +1 -0
- package/{src → dist}/lib/json/types/TableView.d.ts +0 -1
- package/dist/lib/json/types/TableView.js +1 -0
- package/{src → dist}/lib/layout/AdminLayout.d.ts +0 -1
- package/dist/lib/layout/AdminLayout.jsx +24 -0
- package/{src → dist}/lib/layout/ButtonBar.d.ts +1 -1
- package/dist/lib/layout/ButtonBar.jsx +29 -0
- package/{src → dist}/lib/layout/Content.d.ts +0 -1
- package/dist/lib/layout/Content.jsx +10 -0
- package/{src → dist}/lib/layout/PageTitleBar.d.ts +0 -1
- package/dist/lib/layout/PageTitleBar.jsx +12 -0
- package/{src → dist}/lib/layout/Sidebar.d.ts +0 -1
- package/dist/lib/layout/Sidebar.jsx +24 -0
- package/{src → dist}/lib/layout/menu/Menu.d.ts +0 -1
- package/dist/lib/layout/menu/Menu.jsx +30 -0
- package/{src → dist}/lib/layout/menu/MenuButton.d.ts +0 -1
- package/dist/lib/layout/menu/MenuButton.jsx +7 -0
- package/dist/lib/layout/menu/MenuConfig.js +1 -0
- package/{src → dist}/lib/layout/menu/MenuItem.d.ts +0 -1
- package/dist/lib/layout/menu/MenuItem.jsx +20 -0
- package/{src → dist}/lib/layout/menu/NextMenuItem.d.ts +0 -1
- package/dist/lib/layout/menu/NextMenuItem.jsx +20 -0
- package/{src → dist}/lib/layout/menu/SelectedIndicator.d.ts +0 -1
- package/dist/lib/layout/menu/SelectedIndicator.jsx +3 -0
- package/{src → dist}/lib/layout/menu/UserBlock.d.ts +1 -2
- package/dist/lib/layout/menu/UserBlock.jsx +55 -0
- package/{src → dist}/lib/modal/AttachDialog.d.ts +0 -1
- package/dist/lib/modal/AttachDialog.jsx +109 -0
- package/dist/lib/modal/FormActionDialog.jsx +46 -0
- package/{src → dist}/lib/page/PageContainer.d.ts +0 -1
- package/dist/lib/page/PageContainer.jsx +4 -0
- package/{src → dist}/lib/page/PageContentEditor.d.ts +0 -1
- package/dist/lib/page/PageContentEditor.jsx +11 -0
- package/{src → dist}/lib/page/PageMain.d.ts +0 -1
- package/dist/lib/page/PageMain.jsx +4 -0
- package/dist/lib/page/PageQueryStateContainer.jsx +15 -0
- package/{src → dist}/lib/page/PageSectionTitle.d.ts +0 -1
- package/dist/lib/page/PageSectionTitle.jsx +3 -0
- package/{src → dist}/lib/page/PageSidebar.d.ts +0 -1
- package/dist/lib/page/PageSidebar.jsx +3 -0
- package/{src → dist}/lib/page/PageSidebarSection.d.ts +0 -1
- package/dist/lib/page/PageSidebarSection.jsx +7 -0
- package/dist/lib/page/PageStateContainer.jsx +9 -0
- package/{src → dist}/lib/page/PageSubSectionTitle.d.ts +0 -1
- package/dist/lib/page/PageSubSectionTitle.jsx +3 -0
- package/{src → dist}/lib/page/PageTitle.d.ts +0 -1
- package/dist/lib/page/PageTitle.jsx +3 -0
- package/dist/lib/page/PageTopBar.jsx +92 -0
- package/{src → dist}/lib/status/StatusBadge.d.ts +0 -1
- package/dist/lib/status/StatusBadge.jsx +20 -0
- package/dist/lib/table/TableColumnButton.d.ts +1 -0
- package/dist/lib/table/TableColumnButton.jsx +53 -0
- package/{src → dist}/lib/table/TableContainer.d.ts +0 -1
- package/dist/lib/table/TableContainer.jsx +11 -0
- package/dist/lib/table/TableContainerContext.jsx +39 -0
- package/{src → dist}/lib/table/TableCreateButton.d.ts +0 -1
- package/dist/lib/table/TableCreateButton.jsx +9 -0
- package/{src → dist}/lib/table/TableFilterButton.d.ts +0 -1
- package/dist/lib/table/TableFilterButton.jsx +16 -0
- package/{src → dist}/lib/table/TableFilters.d.ts +0 -1
- package/dist/lib/table/TableFilters.jsx +46 -0
- package/{src → dist}/lib/table/TableMassActions.d.ts +0 -1
- package/dist/lib/table/TableMassActions.jsx +15 -0
- package/{src → dist}/lib/table/TableRowActionBar.d.ts +0 -1
- package/dist/lib/table/TableRowActionBar.jsx +14 -0
- package/{src → dist}/lib/table/TableRowActionButton.d.ts +0 -1
- package/dist/lib/table/TableRowActionButton.jsx +7 -0
- package/{src → dist}/lib/table/TableRowActionDialogButton.d.ts +1 -2
- package/dist/lib/table/TableRowActionDialogButton.jsx +8 -0
- package/{src → dist}/lib/table/TableRowDeleteButton.d.ts +0 -1
- package/dist/lib/table/TableRowDeleteButton.jsx +5 -0
- package/{src → dist}/lib/table/TableRowEditButton.d.ts +0 -1
- package/dist/lib/table/TableRowEditButton.jsx +7 -0
- package/{src → dist}/lib/table/TableRowNavigateButton.d.ts +0 -1
- package/dist/lib/table/TableRowNavigateButton.jsx +13 -0
- package/{src → dist}/lib/table/TableRowPublishPostButton.d.ts +0 -1
- package/dist/lib/table/TableRowPublishPostButton.jsx +18 -0
- package/{src → dist}/lib/table/TableRowViewButton.d.ts +0 -1
- package/dist/lib/table/TableRowViewButton.jsx +12 -0
- package/{src → dist}/lib/table/TableTopBar.d.ts +1 -2
- package/dist/lib/table/TableTopBar.jsx +17 -0
- package/package.json +60 -4
- package/README.md +0 -7
- package/index.cjs.d.ts +0 -1
- package/index.cjs.js +0 -3710
- package/index.esm.d.ts +0 -1
- package/index.esm.js +0 -3662
- package/src/lib/table/TableColumnButton.d.ts +0 -0
- /package/{src → dist}/index.d.ts +0 -0
- /package/{src → dist}/lib/buttons/DialogButton.d.ts +0 -0
- /package/{src → dist}/lib/json/dialog/ItemDeleteDialog.d.ts +0 -0
- /package/{src → dist}/lib/json/dialog/ItemEditDialog.d.ts +0 -0
- /package/{src → dist}/lib/json/table/TableViewContext.d.ts +0 -0
- /package/{src → dist}/lib/json/table/useTableProps.d.ts +0 -0
- /package/{src → dist}/lib/json/types/EditItemDialog.d.ts +0 -0
- /package/{src → dist}/lib/json/types/MultiQueryWrapper.d.ts +0 -0
- /package/{src → dist}/lib/json/types/MultiQueryWrapperDialog.d.ts +0 -0
- /package/{src → dist}/lib/json/types/QueryWrapper.d.ts +0 -0
- /package/{src → dist}/lib/json/types/QueryWrapperDialog.d.ts +0 -0
- /package/{src → dist}/lib/json/types/ScreenConfig.d.ts +0 -0
- /package/{src → dist}/lib/layout/menu/MenuConfig.d.ts +0 -0
- /package/{src → dist}/lib/modal/FormActionDialog.d.ts +0 -0
- /package/{src → dist}/lib/page/PageQueryStateContainer.d.ts +0 -0
- /package/{src → dist}/lib/page/PageStateContainer.d.ts +0 -0
- /package/{src → dist}/lib/page/PageTopBar.d.ts +0 -0
- /package/{src → dist}/lib/table/TableContainerContext.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useBoolean } from "@compill/hooks";
|
|
2
|
+
import { mdiArrowLeft } from "@mdi/js";
|
|
3
|
+
import { IconButton } from "@valerya/ui";
|
|
4
|
+
import { useHotkeys } from "react-hotkeys-hook";
|
|
5
|
+
import Content from "./Content";
|
|
6
|
+
import { Sidebar } from "./Sidebar";
|
|
7
|
+
export function AdminLayout({ color, darkMode, logo, title, menuConfig, userMenuConfig, userSettingsPath, ...props }) {
|
|
8
|
+
return (<div w="full" h="screen" dflex flexRow bgColor={`${color}-${darkMode ? "900" : "100"}`} {...props}>
|
|
9
|
+
<LeftPanel color={color} darkMode={darkMode} logo={logo} title={title} menuConfig={menuConfig} userMenuConfig={userMenuConfig} userSettingsPath={userSettingsPath}/>
|
|
10
|
+
|
|
11
|
+
<div w="screen" py="2" pe="2">
|
|
12
|
+
<Content bgColor="white" rounded="lg" shadow/>
|
|
13
|
+
</div>
|
|
14
|
+
</div>);
|
|
15
|
+
}
|
|
16
|
+
function LeftPanel({ color, darkMode, logo, title, menuConfig, userMenuConfig, userSettingsPath }) {
|
|
17
|
+
const [isOpen, __, toggle] = useBoolean(true);
|
|
18
|
+
useHotkeys('ctrl+t', () => toggle(), [toggle]);
|
|
19
|
+
return (<div ms={isOpen ? "0" : "-14.5rem"} transition="all" duration="500" transform>
|
|
20
|
+
<Sidebar flexShrink="0" color={color} darkMode={darkMode} logo={logo} title={title} menuConfig={menuConfig} userMenuConfig={userMenuConfig} userSettingsPath={userSettingsPath}/>
|
|
21
|
+
|
|
22
|
+
<IconButton icon={mdiArrowLeft} transition="all" duration="500" transform rotate={isOpen ? "0" : "180"} position="absolute" bottom="14" end="-5" size="lg" corners="pill" onClick={toggle} z="100"/>
|
|
23
|
+
</div>);
|
|
24
|
+
}
|
|
@@ -7,7 +7,7 @@ export type ButtonBarButtonProps = {
|
|
|
7
7
|
icon?: string;
|
|
8
8
|
} & ButtonProps;
|
|
9
9
|
export declare const ButtonBarButton: React.ForwardRefExoticComponent<{
|
|
10
|
-
icon?: string
|
|
10
|
+
icon?: string;
|
|
11
11
|
} & ButtonProps & React.RefAttributes<HTMLDivElement>>;
|
|
12
12
|
export interface ButtonBarSubmitButtonProps extends ButtonBarButtonProps {
|
|
13
13
|
useDirty?: boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DialogButton } from "../buttons/DialogButton";
|
|
2
|
+
import { Button, Icon } from "@valerya/ui";
|
|
3
|
+
import { useFormikContext } from "formik";
|
|
4
|
+
import React from "react";
|
|
5
|
+
export function ButtonBar({ children, ...props }) {
|
|
6
|
+
return (<div dflex border="1px" borderColor="zinc-200" divideX="1px" divideColor="zinc-200" rounded="lg" overflow="hidden" {...props}>
|
|
7
|
+
{children}
|
|
8
|
+
</div>);
|
|
9
|
+
}
|
|
10
|
+
export const ButtonBarButton = React.forwardRef(function ({ icon, children, ...props }, ref) {
|
|
11
|
+
return (<Button scheme="dark" size="sm" aspectRatio={icon && !children ? "square" : undefined} variant="borderless" dflex alignItems="center" placeContent="center" corners="square" gap="2" {...props} ref={ref}>
|
|
12
|
+
{icon && <Icon path={icon}/>}
|
|
13
|
+
{children}
|
|
14
|
+
</Button>);
|
|
15
|
+
});
|
|
16
|
+
export const ButtonBarSubmitButton = React.forwardRef(function ({ useDirty, disabled, icon, children, ...props }, ref) {
|
|
17
|
+
const { dirty, handleSubmit } = useFormikContext() ?? { dirty: false, handleSubmit: undefined, values: {} };
|
|
18
|
+
const onSubmit = React.useCallback(() => handleSubmit(), [handleSubmit]);
|
|
19
|
+
return (<Button scheme="dark" size="sm" aspectRatio={icon && !children ? "square" : undefined} variant="borderless" dflex alignItems="center" placeContent="center" corners="square" gap="2" disabled={(useDirty && !dirty) || disabled} onClick={onSubmit} {...props} ref={ref}>
|
|
20
|
+
{icon && <Icon path={icon}/>}
|
|
21
|
+
{children}
|
|
22
|
+
</Button>);
|
|
23
|
+
});
|
|
24
|
+
export function ButtonBarDialogButton({ icon, children, ...props }) {
|
|
25
|
+
return (<DialogButton scheme="dark" size="sm" aspectRatio={icon && !children ? "square" : undefined} variant="borderless" dflex alignItems="center" placeContent="center" corners="square" gap="2" {...props}>
|
|
26
|
+
{icon && <Icon path={icon}/>}
|
|
27
|
+
{children}
|
|
28
|
+
</DialogButton>);
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function PageTitleBar({ title, children, ...props }) {
|
|
2
|
+
return (<div dflex flexRow alignItems="center" gap="3" p="8"
|
|
3
|
+
// borderB="0.5"
|
|
4
|
+
// borderBColor="--bg-2"
|
|
5
|
+
{...props}>
|
|
6
|
+
{title && <h2 textSize="x2" fontWeight="600" textColor="#3f4254" textTransform="capitalize">{title}</h2>}
|
|
7
|
+
|
|
8
|
+
<div flexGrow> </div>
|
|
9
|
+
|
|
10
|
+
{children}
|
|
11
|
+
</div>);
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FlexCenter } from "@compill/components";
|
|
2
|
+
import { Menu } from "./menu/Menu";
|
|
3
|
+
import { AppEnv } from "@compill/env";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
import { UserBlock } from "./menu/UserBlock";
|
|
6
|
+
export function Sidebar({ show, logo, title, menuConfig, userMenuConfig, userSettingsPath, color, darkMode, ...props }) {
|
|
7
|
+
return (<div dflex flexCol w="full" md_w="64" minH="screen" p="0" textColor={darkMode ? "white" : "slate-800"} {...props}>
|
|
8
|
+
<FlexCenter placeContent="start" p="4" font="title" gap="3" borderB="px" borderBColor="slate-900" borderOpacity="5">
|
|
9
|
+
{logo ?? <Logo width={40} height={40} darkMode={darkMode}/>}
|
|
10
|
+
<h1 textSize="md">{title || AppEnv.appName()}</h1>
|
|
11
|
+
</FlexCenter>
|
|
12
|
+
|
|
13
|
+
<Menu overflowY="auto" flexGrow="1" p="4" darkMode={darkMode} config={menuConfig}/>
|
|
14
|
+
|
|
15
|
+
<div p="2">
|
|
16
|
+
<UserBlock darkMode={darkMode} color={color} menuConfig={userMenuConfig} path={userSettingsPath}/>
|
|
17
|
+
</div>
|
|
18
|
+
</div>);
|
|
19
|
+
}
|
|
20
|
+
export default function Logo({ width, height, darkMode, ...props }) {
|
|
21
|
+
return (<div {...props}>
|
|
22
|
+
<Image src={`/logo_${darkMode ? "light" : "dark"}.png`} alt={AppEnv.appName() || ""} width={width} height={height} priority unoptimized/>
|
|
23
|
+
</div>);
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { MenuItem } from "./MenuItem";
|
|
2
|
+
import { NextMenuItem } from "./NextMenuItem";
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
export function Menu({ darkMode, config, useNextRouter, ...props }) {
|
|
8
|
+
const Comp = useNextRouter ? NextMenuItem : MenuItem;
|
|
9
|
+
return (<ul {...props}>
|
|
10
|
+
{config.map((item, index) => {
|
|
11
|
+
if (item.type == "divider")
|
|
12
|
+
return <Divider key={index} title={item.label}/>;
|
|
13
|
+
if (item.type == "item") {
|
|
14
|
+
return (<Comp key={index} icon={item.icon} path={item.path} depth={0} darkMode={darkMode} subMenu={item.children}>
|
|
15
|
+
{item.label}
|
|
16
|
+
</Comp>);
|
|
17
|
+
}
|
|
18
|
+
})}
|
|
19
|
+
</ul>);
|
|
20
|
+
}
|
|
21
|
+
function Divider({ title }) {
|
|
22
|
+
return (<div px="2" mt="5" mb="2" opacity="75" textTransform="capitalize" letterSpacing="widest" fontWeight="700" textSize="xs">
|
|
23
|
+
{title}
|
|
24
|
+
</div>);
|
|
25
|
+
}
|
|
26
|
+
function CategoryItem({ children, ...props }) {
|
|
27
|
+
return (<li ps="5" py="2" mt="5" textSize="sm" textColor="white" textOpacity="80" fontWeight="600" textTransform="uppercase">
|
|
28
|
+
{children}
|
|
29
|
+
</li>);
|
|
30
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Button, Icon } from "@valerya/ui";
|
|
2
|
+
export function MenuButton({ depth, darkMode, icon, selected, children, ...props }) {
|
|
3
|
+
return (<Button as="li" minH="8" ms={`${(depth ?? 0) * 2}`} p="2" font="title" textColor={darkMode ? "white" : "black"} fontWeight="600" rounded="lg" textSize="sm" variant="borderless" hover_bgColor={darkMode ? "white" : "black"} hover_bgOpacity="10" hover_textColor={darkMode ? "white" : "zinc-800"} cursor="pointer" dflex alignItems="center" gap="3" {...props}>
|
|
4
|
+
{icon && <Icon path={icon} opacity={selected ? "100" : "60"}/>}
|
|
5
|
+
{children}
|
|
6
|
+
</Button>);
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SelectedIndicator } from "./SelectedIndicator";
|
|
2
|
+
import { Link, useLocation, useMatch } from "react-router-dom";
|
|
3
|
+
import { MenuButton } from "./MenuButton";
|
|
4
|
+
export function MenuItem({ icon, path, depth, darkMode, subMenu, ...props }) {
|
|
5
|
+
const location = useLocation();
|
|
6
|
+
const selected = path == "/" ? location.pathname == "/" : location.pathname.startsWith(path.startsWith("/") ? path : `/${path}`);
|
|
7
|
+
const match = useMatch("/" + path) != null;
|
|
8
|
+
// const selected = match != null
|
|
9
|
+
return (<>
|
|
10
|
+
<Link to={path} style={{ position: "relative" }}>
|
|
11
|
+
<MenuButton depth={depth} darkMode={darkMode} icon={icon} selected={selected} {...props}/>
|
|
12
|
+
|
|
13
|
+
{match && <SelectedIndicator darkMode={darkMode}/>}
|
|
14
|
+
</Link>
|
|
15
|
+
|
|
16
|
+
{subMenu?.map((item, index) => (<MenuItem key={index} icon={item.icon} path={item.path} depth={(depth ?? 0) + 1} darkMode={darkMode} subMenu={item.children}>
|
|
17
|
+
{item.label}
|
|
18
|
+
</MenuItem>))}
|
|
19
|
+
</>);
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MenuButton } from "./MenuButton";
|
|
2
|
+
import { SelectedIndicator } from "./SelectedIndicator";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useRouter } from "next/router";
|
|
5
|
+
export function NextMenuItem({ icon, path, depth, darkMode, subMenu, ...props }) {
|
|
6
|
+
const { pathname } = useRouter();
|
|
7
|
+
const selected = path == "/" ? pathname == "/" : pathname.startsWith(path.startsWith("/") ? path : `/${path}`);
|
|
8
|
+
const match = path == "/" ? pathname == "/" : pathname == (path.startsWith("/") ? path : `/${path}`);
|
|
9
|
+
return (<>
|
|
10
|
+
<Link href={path} style={{ position: "relative" }}>
|
|
11
|
+
<MenuButton depth={depth} darkMode={darkMode} icon={icon} selected={selected} {...props}/>
|
|
12
|
+
|
|
13
|
+
{match && <SelectedIndicator darkMode={darkMode}/>}
|
|
14
|
+
</Link>
|
|
15
|
+
|
|
16
|
+
{subMenu?.map((item, index) => (<NextMenuItem key={index} icon={item.icon} path={item.path} depth={(depth ?? 0) + 1} darkMode={darkMode} subMenu={item.children}>
|
|
17
|
+
{item.label}
|
|
18
|
+
</NextMenuItem>))}
|
|
19
|
+
</>);
|
|
20
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useSessionLogout, useSessionUser } from "@compill/auth";
|
|
2
|
+
import { mdiCog, mdiDotsVertical, mdiLogout } from "@mdi/js";
|
|
3
|
+
import { Avatar, Button, Icon, IconButton, Popup } from "@valerya/ui";
|
|
4
|
+
import { capitalize } from "es-toolkit";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { useNavigate } from "react-router-dom";
|
|
7
|
+
export function UserBlock({ color, darkMode, menuConfig, path }) {
|
|
8
|
+
const { isLoading, user } = useSessionUser();
|
|
9
|
+
const navigate = useNavigate();
|
|
10
|
+
const handleClick = React.useCallback(() => navigate(path), [navigate, path]);
|
|
11
|
+
if (isLoading)
|
|
12
|
+
return null;
|
|
13
|
+
return (<div dflex alignItems="center" border="0.5" borderColor={`${color}-${darkMode ? "800" : "200"}`} ps="3" py="1.5" textSize="md" rounded="lg" hover_bgColor={`${color}-${darkMode ? "800" : "200"}`} cursor="pointer" textColor={darkMode ? "white" : "slate-800"} onClick={handleClick}>
|
|
14
|
+
<Avatar size="sm" src={user?.media?.url ?? ""} name={`${user?.firstname} ${user?.lastname}`}/>
|
|
15
|
+
|
|
16
|
+
<span flexGrow ms="2">{`${capitalize(user?.firstname || user?.lastname || "")}`}</span>
|
|
17
|
+
|
|
18
|
+
<IconButton variant="borderless" corners="pill" scheme="dark" textColor={darkMode ? "white" : "slate-800"} hover_textColor={darkMode ? "white" : "slate-800"} hover_bgColor={`${color}-${darkMode ? "900" : "200"}`} icon={mdiCog} onClick={handleClick}/>
|
|
19
|
+
|
|
20
|
+
<OverflowMenu color={color} darkMode={darkMode} menuConfig={menuConfig}/>
|
|
21
|
+
</div>);
|
|
22
|
+
}
|
|
23
|
+
function OverflowMenu({ color, darkMode, menuConfig }) {
|
|
24
|
+
const [showPopup, setShowPopup] = React.useState(false);
|
|
25
|
+
const navigate = useNavigate();
|
|
26
|
+
const logout = useSessionLogout(false);
|
|
27
|
+
return (<>
|
|
28
|
+
<Popup show={showPopup} position="relative" side="bottom-end" onClick={(e) => { e.preventDefault(); e.stopPropagation(); setShowPopup((show) => !show); }} onHide={() => setShowPopup(false)}>
|
|
29
|
+
<IconButton icon={mdiDotsVertical} variant="borderless" corners="pill" scheme="dark" textColor={darkMode ? "white" : "slate-800"} hover_textColor={darkMode ? "white" : "slate-800"} hover_bgColor={`${color}-${darkMode ? "900" : "200"}`}/>
|
|
30
|
+
|
|
31
|
+
<div bgColor="white" rounded="sm" overflow="hidden" shadow mt="1" border="px" borderColor="gray-200" divideColor="gray-200" divideY="px" minW="40">
|
|
32
|
+
{menuConfig && menuConfig.length > 0 && menuConfig.map((item, index) => {
|
|
33
|
+
if (item.type == "item") {
|
|
34
|
+
return (<MenuItem key={index} icon={item.icon} onClick={() => navigate(item.path)}>
|
|
35
|
+
{item.label}
|
|
36
|
+
</MenuItem>);
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
})}
|
|
40
|
+
<MenuItem icon={mdiLogout} onClick={logout}>Logout</MenuItem>
|
|
41
|
+
</div>
|
|
42
|
+
</Popup>
|
|
43
|
+
</>);
|
|
44
|
+
}
|
|
45
|
+
function MenuItem({ icon, onClick, children, ...props }) {
|
|
46
|
+
const handleClick = React.useCallback((e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
onClick?.(e);
|
|
50
|
+
}, []);
|
|
51
|
+
return (<Button variant="borderless" scheme="dark" size="sm" alignItems="center" dflex gap="2" px="2" py="1.5" w="full" onClick={handleClick} textColor="slate-700" {...props}>
|
|
52
|
+
{icon && <Icon path={icon} size="md"/>}
|
|
53
|
+
{children}
|
|
54
|
+
</Button>);
|
|
55
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useApiMutation, useApiQuery } from "@compill/api";
|
|
2
|
+
import { ModalLoadingOverlay, QueryLoadingState, RetryOnError } from "@compill/components";
|
|
3
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { Button, Checkbox, Modal } from "@valerya/ui";
|
|
5
|
+
import { sortBy } from "es-toolkit";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { toast } from "react-toastify";
|
|
8
|
+
const defaultErrorMsg = "Oops, something went wrong...";
|
|
9
|
+
export function AttachDialog({ queryId, queryKey, queryFetchFn, queryFetchAllKey, queryFetchAllFn, querySaveFn, matchKey, size = "lg", show, onClose, itemLabel, onSuccess, onFetchError, fetchErrorMsg = defaultErrorMsg, onSaveError, saveErrorMsg = defaultErrorMsg, invalidateQueriesOnSuccess = true, retryText = "Retry", cancelLabel = "Cancel", saveLabel, formikProps, getItemName, ...props }) {
|
|
10
|
+
const queryClient = useQueryClient();
|
|
11
|
+
const { data: attached, isInitialLoading: fetchLoading, isError: fetchError, refetch, error } = useApiQuery(queryKey, queryFetchFn);
|
|
12
|
+
const { data, isInitialLoading: fetchAllLoading, isError: fetchAllError, refetch: refetchAll, error: errorAll } = useApiQuery(queryFetchAllKey, queryFetchAllFn);
|
|
13
|
+
const [selectedResources, setSelectedResources] = React.useState([]);
|
|
14
|
+
const isLoading = fetchLoading || fetchAllLoading;
|
|
15
|
+
const isError = fetchError || fetchAllError;
|
|
16
|
+
const mutation = useApiMutation(querySaveFn, queryKey, queryId);
|
|
17
|
+
const handleClick = React.useCallback((event) => {
|
|
18
|
+
const id = event?.currentTarget.dataset.id ?? "";
|
|
19
|
+
const arr = selectedResources.concat([]);
|
|
20
|
+
const i = selectedResources.indexOf(id);
|
|
21
|
+
if (i != -1)
|
|
22
|
+
arr.splice(i, 1);
|
|
23
|
+
else
|
|
24
|
+
arr.push(id);
|
|
25
|
+
setSelectedResources(arr);
|
|
26
|
+
}, [selectedResources, setSelectedResources]);
|
|
27
|
+
const retry = React.useCallback(() => {
|
|
28
|
+
if (fetchError)
|
|
29
|
+
refetch();
|
|
30
|
+
if (fetchAllError)
|
|
31
|
+
refetchAll();
|
|
32
|
+
}, [refetch, refetchAll, fetchError, fetchAllError]);
|
|
33
|
+
const saveItem = React.useCallback(() => {
|
|
34
|
+
// Clear mutation error if any
|
|
35
|
+
mutation.reset();
|
|
36
|
+
mutation.mutateAsync({ resources: selectedResources })
|
|
37
|
+
.then((response) => {
|
|
38
|
+
if (onSuccess)
|
|
39
|
+
onSuccess(response);
|
|
40
|
+
else
|
|
41
|
+
toast.success(`${itemLabel} saved`);
|
|
42
|
+
if (invalidateQueriesOnSuccess)
|
|
43
|
+
queryClient.invalidateQueries({ queryKey: queryKey });
|
|
44
|
+
// closing delete modal
|
|
45
|
+
onClose?.();
|
|
46
|
+
})
|
|
47
|
+
.catch(error => {
|
|
48
|
+
console.error("on error", error);
|
|
49
|
+
if (onSaveError)
|
|
50
|
+
onSaveError();
|
|
51
|
+
else
|
|
52
|
+
toast.error(`Error adding ${itemLabel}`);
|
|
53
|
+
});
|
|
54
|
+
}, [mutation, queryId, onSuccess, queryClient, onSaveError, onClose]);
|
|
55
|
+
const resources = React.useMemo(() => {
|
|
56
|
+
let r = [];
|
|
57
|
+
if (attached && data) {
|
|
58
|
+
r = [].concat(data);
|
|
59
|
+
attached.forEach((attachedItem) => r.splice(r.findIndex((item) => item.id == attachedItem[matchKey]), 1));
|
|
60
|
+
// TODO set keyField for name (could be title for example)
|
|
61
|
+
if (getItemName)
|
|
62
|
+
r = r.map((item) => { return { ...item, name: getItemName(item) }; });
|
|
63
|
+
r = sortBy(r, ["name"]);
|
|
64
|
+
}
|
|
65
|
+
return r;
|
|
66
|
+
}, [attached, data]);
|
|
67
|
+
return (<Modal size={size} show={show} onClose={onClose} scheme="light" transition
|
|
68
|
+
// closeOnMaskClick={false}
|
|
69
|
+
{...props}>
|
|
70
|
+
<Modal.Header>
|
|
71
|
+
{`Add ${itemLabel}`}
|
|
72
|
+
</Modal.Header>
|
|
73
|
+
|
|
74
|
+
{isLoading && (<Modal.Body>
|
|
75
|
+
<QueryLoadingState minW="72"/>
|
|
76
|
+
</Modal.Body>)}
|
|
77
|
+
|
|
78
|
+
{isError && (<Modal.Body>
|
|
79
|
+
<RetryOnError label={`${fetchErrorMsg} ${error}`} onClick={retry}/>
|
|
80
|
+
</Modal.Body>)}
|
|
81
|
+
|
|
82
|
+
{!isLoading && !isError &&
|
|
83
|
+
(<>
|
|
84
|
+
<Modal.Body px="0" pb="6" maxH="750px" overflow="auto">
|
|
85
|
+
<div dflex flexCol overflow="auto">
|
|
86
|
+
|
|
87
|
+
{resources.map(item => (<ListItem key={item.id} label={item.name} value={item.id} data-id={item.id} checked={selectedResources.includes(`${item.id}`)} onClick={handleClick}/>))}
|
|
88
|
+
</div>
|
|
89
|
+
</Modal.Body>
|
|
90
|
+
|
|
91
|
+
<Modal.Footer dflex placeContent="end" spaceX="3">
|
|
92
|
+
<Button disabled={mutation.isLoading} onClick={onClose} variant="borderless" me="2">
|
|
93
|
+
{cancelLabel}
|
|
94
|
+
</Button>
|
|
95
|
+
<Button type="submit" disabled={selectedResources.length == 0 || mutation.isLoading /* || !isValid*/} onClick={saveItem}>
|
|
96
|
+
{saveLabel ? saveLabel : (queryId ? "Update" : "Create")}
|
|
97
|
+
</Button>
|
|
98
|
+
</Modal.Footer>
|
|
99
|
+
</>)}
|
|
100
|
+
|
|
101
|
+
{mutation.isLoading && <ModalLoadingOverlay />}
|
|
102
|
+
</Modal>);
|
|
103
|
+
}
|
|
104
|
+
function ListItem({ label, value, checked, ...props }) {
|
|
105
|
+
return (<div dflex alignItems="center" hover_bgColor="slate-100" px="5" py="2" cursor="pointer" {...props}>
|
|
106
|
+
<span flexGrow>{label}</span>
|
|
107
|
+
<Checkbox name={`resources.${value}`} value={value} checked={checked}/>
|
|
108
|
+
</div>);
|
|
109
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useMutate } from "@compill/api";
|
|
2
|
+
import { ModalLoadingOverlay } from "@compill/components";
|
|
3
|
+
import { SubmitButton, FormRenderer, FormProvider } from "@compill/form";
|
|
4
|
+
import { useApiMutation, useInvalidateParentMutation } from "@compill/api";
|
|
5
|
+
import { Button, Modal } from "@valerya/ui";
|
|
6
|
+
import React from "react";
|
|
7
|
+
export function FormActionDialog({ initialValues, itemLabel, queryId = "", queryKey, queryFn, queryOptions, onSuccess, successMsg, showSuccessMsg, onError, errorMsg, showErrorMsg, processInput, invalidateQueriesOnSuccess = true, cancelLabel = "Cancel", saveLabel = "Send", size = "lg", title, form, show, onClose, formikProps, ...props }) {
|
|
8
|
+
const mutation = invalidateQueriesOnSuccess ? useInvalidateParentMutation(queryFn, queryKey, queryOptions) : useApiMutation(queryFn, queryKey, queryId, queryOptions);
|
|
9
|
+
const mutate = useMutate(mutation, {
|
|
10
|
+
onSuccess,
|
|
11
|
+
successMsg,
|
|
12
|
+
showSuccessMsg,
|
|
13
|
+
onError,
|
|
14
|
+
errorMsg,
|
|
15
|
+
showErrorMsg,
|
|
16
|
+
processInput,
|
|
17
|
+
});
|
|
18
|
+
return (<Modal size={size} show={show} onClose={onClose} scheme="light" transition
|
|
19
|
+
// closeOnMaskClick={false}
|
|
20
|
+
{...props}>
|
|
21
|
+
<Modal.Header>
|
|
22
|
+
{title}
|
|
23
|
+
</Modal.Header>
|
|
24
|
+
|
|
25
|
+
{/* @ts-ignore */}
|
|
26
|
+
<FormProvider initialValues={initialValues ?? {}} onSubmit={mutate} {...formikProps}>
|
|
27
|
+
<Modal.Body pb="6">
|
|
28
|
+
{React.isValidElement(form) && form}
|
|
29
|
+
|
|
30
|
+
{Array.isArray(form) && <FormRenderer form={form}/>}
|
|
31
|
+
</Modal.Body>
|
|
32
|
+
|
|
33
|
+
<Modal.Footer dflex placeContent="end" spaceX="3">
|
|
34
|
+
<Button disabled={mutation.isLoading} onClick={onClose} variant="borderless" me="2">
|
|
35
|
+
{cancelLabel}
|
|
36
|
+
</Button>
|
|
37
|
+
|
|
38
|
+
<SubmitButton disabled={mutation.isLoading}>
|
|
39
|
+
{saveLabel}
|
|
40
|
+
</SubmitButton>
|
|
41
|
+
</Modal.Footer>
|
|
42
|
+
</FormProvider>
|
|
43
|
+
|
|
44
|
+
{mutation.isLoading && <ModalLoadingOverlay />}
|
|
45
|
+
</Modal>);
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FormEditor } from "@compill/form-editor";
|
|
2
|
+
import { PageMain } from "./PageMain";
|
|
3
|
+
import { ImageExtension } from "@compill/editor";
|
|
4
|
+
export function PageContentEditor({ name, ...props }) {
|
|
5
|
+
const extensions = [ImageExtension];
|
|
6
|
+
return (<PageMain h="min" {...props}>
|
|
7
|
+
<FormEditor minH="128" minW="144" maxW={props.maxW}
|
|
8
|
+
// w={props.w}
|
|
9
|
+
name={name} placeHolder="Write here..." extensions={extensions}/>
|
|
10
|
+
</PageMain>);
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { QueryLoadingState, RetryOnError } from "@compill/components";
|
|
2
|
+
import { useApiQuery, useInvalidateQuery } from "@compill/api";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { PageContainer } from "./PageContainer";
|
|
5
|
+
function PageQueryStateContainerInner({ queryId, api, apiFn, loadingStyles, errorStyles, children, ...props }) {
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
const { data, isLoading, isError } = apiFn == "getAll" ? useApiQuery(api.queryKey, api.getAll, props.apiParams) : useApiQuery(api.queryKey, api.get, queryId);
|
|
8
|
+
const invalidate = useInvalidateQuery(api.queryKey, queryId);
|
|
9
|
+
if (isLoading)
|
|
10
|
+
return <QueryLoadingState w="full" h="100%" {...loadingStyles}/>;
|
|
11
|
+
if (isError)
|
|
12
|
+
return <RetryOnError p="0" onClick={invalidate} {...errorStyles}/>;
|
|
13
|
+
return <PageContainer {...props}>{apiFn == "get" ? children(data) : children(data)}</PageContainer>;
|
|
14
|
+
}
|
|
15
|
+
export const PageQueryStateContainer = React.forwardRef(PageQueryStateContainerInner);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { QueryLoadingState } from "@compill/components";
|
|
2
|
+
import { Container } from "@valerya/ui";
|
|
3
|
+
import React from "react";
|
|
4
|
+
export const PageStateContainer = React.forwardRef(({ loading = false, children, ...props }, ref) => {
|
|
5
|
+
return (<>
|
|
6
|
+
{loading && <QueryLoadingState w="full" h="100%"/>}
|
|
7
|
+
{!loading && <Container ref={ref} center size="x2" dflex flexCol gap="8" {...props}>{children}</Container>}
|
|
8
|
+
</>);
|
|
9
|
+
});
|