@sqrzro/admin 2.1.0-bz.17 → 2.1.0-bz.19
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/components/AppLayout/index.js +5 -5
- package/dist/components/AppNavigation/index.d.ts +2 -2
- package/dist/components/AppNavigation/index.js +4 -12
- package/dist/components/AppNavigationComponent/index.d.ts +12 -0
- package/dist/components/AppNavigationComponent/index.js +19 -0
- package/dist/components/Auth/index.d.ts +10 -5
- package/dist/components/Auth/index.js +1 -6
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/Badge/index.js +6 -6
- package/dist/components/FilterBar/index.d.ts +2 -1
- package/dist/components/FilterBar/index.js +16 -3
- package/dist/components/FilterBarClearButton/index.d.ts +6 -0
- package/dist/components/FilterBarClearButton/index.js +5 -0
- package/dist/components/FilterBarItem/index.d.ts +4 -2
- package/dist/components/FilterBarItem/index.js +2 -1
- package/dist/components/GridList/index.d.ts +1 -1
- package/dist/components/GridListItem/index.d.ts +1 -1
- package/dist/components/GridListItem/index.js +2 -2
- package/dist/components/InfoPanel/index.js +10 -14
- package/dist/components/List/index.d.ts +3 -20
- package/dist/components/List/index.js +8 -24
- package/dist/components/ListActions/index.d.ts +6 -3
- package/dist/components/ListActions/index.js +10 -3
- package/dist/components/ListClientComponent/index.d.ts +15 -0
- package/dist/components/ListClientComponent/index.js +14 -0
- package/dist/components/ListComponent/index.d.ts +14 -0
- package/dist/components/ListComponent/index.js +21 -0
- package/dist/components/ListItem/index.d.ts +14 -9
- package/dist/components/ListItem/index.js +16 -6
- package/dist/components/ListSkeleton/index.d.ts +5 -0
- package/dist/components/ListSkeleton/index.js +5 -0
- package/dist/components/MeActions/index.js +2 -2
- package/dist/components/MePanel/index.d.ts +2 -1
- package/dist/components/MePanel/index.js +3 -2
- package/dist/components/Menu/index.d.ts +8 -0
- package/dist/components/Menu/index.js +7 -0
- package/dist/components/MenuItem/index.d.ts +5 -0
- package/dist/components/MenuItem/index.js +20 -0
- package/dist/components/Page/index.d.ts +4 -2
- package/dist/components/Page/index.js +4 -5
- package/dist/components/Panel/index.d.ts +3 -1
- package/dist/components/Panel/index.js +3 -2
- package/dist/components/RootLayout/index.d.ts +2 -1
- package/dist/components/RootLayout/index.js +3 -9
- package/dist/components/SettingsForm/index.d.ts +1 -3
- package/dist/components/SettingsForm/index.js +5 -5
- package/dist/components/SettingsPage/index.d.ts +1 -1
- package/dist/components/SettingsPage/index.js +2 -2
- package/dist/components/TableClientComponent/index.js +0 -8
- package/dist/components/Tabs/index.d.ts +2 -6
- package/dist/components/Tabs/index.js +5 -8
- package/dist/components/TabsComponent/index.d.ts +8 -0
- package/dist/components/TabsComponent/index.js +9 -0
- package/dist/components/index.d.ts +5 -5
- package/dist/components/index.js +2 -2
- package/dist/hooks/useNavigation.d.ts +11 -0
- package/dist/hooks/useNavigation.js +27 -0
- package/dist/index.cjs +1266 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +8 -2
- package/dist/services/ConfigService.d.ts +7 -1
- package/dist/services/ConfigService.js +1 -1
- package/dist/services/PermissionService.d.ts +3 -0
- package/dist/services/PermissionService.js +25 -0
- package/dist/services/SettingsService.d.ts +8 -0
- package/dist/services/{LayoutService.js → SettingsService.js} +2 -2
- package/dist/styles/config.js +57 -21
- package/dist/styles/tailwind.d.ts +1 -1
- package/dist/styles/tailwind.js +6 -2
- package/package.json +27 -16
- package/dist/components/Icon/index.d.ts +0 -8
- package/dist/components/Icon/index.js +0 -23
- package/dist/icons/ErrorIcon/index.d.ts +0 -3
- package/dist/icons/ErrorIcon/index.js +0 -5
- package/dist/icons/InfoIcon/index.d.ts +0 -3
- package/dist/icons/InfoIcon/index.js +0 -5
- package/dist/icons/SuccessIcon/index.d.ts +0 -3
- package/dist/icons/SuccessIcon/index.js +0 -5
- package/dist/icons/WarningIcon/index.d.ts +0 -3
- package/dist/icons/WarningIcon/index.js +0 -5
- package/dist/services/LayoutService.d.ts +0 -7
- /package/{postcss.js → postcss.cjs} +0 -0
- /package/{tailwind.js → tailwind.cjs} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Link, tw } from '@sqrzro/components';
|
|
3
3
|
import { getConfig } from '../../services/ConfigService';
|
|
4
|
-
import { getLayout } from '../../services/
|
|
4
|
+
import { getLayout } from '../../services/SettingsService';
|
|
5
5
|
import AppNavigation from '../AppNavigation';
|
|
6
6
|
import MePanel from '../MePanel';
|
|
7
7
|
async function AppLayout({ children, user }) {
|
|
8
8
|
const config = getConfig();
|
|
9
9
|
const layout = await getLayout();
|
|
10
|
-
return (_jsxs("div", { className: tw('', layout === 'sidebar' ? 'grid grid-cols-[12rem_1fr]' : null), children: [_jsx("header", { className: "bg-slate-800", children:
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
return (_jsxs("div", { className: tw('', layout === 'sidebar' ? 'grid grid-cols-[12rem_1fr]' : null), children: [_jsx("header", { className: "bg-slate-800", children: _jsxs("div", { className: tw('flex items-center', layout === 'sidebar'
|
|
11
|
+
? 'h-screen flex-col'
|
|
12
|
+
: 'h-16 border-b border-slate-700 px-4'), children: [_jsxs(Link, { className: tw(layout === 'sidebar' ? 'my-7 h-12 w-12' : 'h-9 w-9'), href: "/", children: [config.logo?.(), _jsx("span", { className: "sr-only", children: config.app.name })] }), _jsx(AppNavigation, { layout: layout }), _jsx(MePanel, { layout: layout, user: user })] }) }), _jsx("main", { className: "mb-10 block", children: children })] }));
|
|
13
13
|
}
|
|
14
14
|
export default AppLayout;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
interface AppNavigationProps {
|
|
3
|
-
layout
|
|
3
|
+
layout?: string;
|
|
4
4
|
}
|
|
5
|
-
declare function AppNavigation({ layout }: Readonly<AppNavigationProps>): React.ReactElement
|
|
5
|
+
declare function AppNavigation({ layout, }: Readonly<AppNavigationProps>): Promise<React.ReactElement>;
|
|
6
6
|
export default AppNavigation;
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Link, tw } from '@sqrzro/components';
|
|
4
|
-
import { useNavigation } from '@sqrzro/hooks';
|
|
5
2
|
import { getConfig } from '../../services/ConfigService';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
function AppNavigation({ layout }) {
|
|
11
|
-
const navigation = useNavigation(getNavigation());
|
|
12
|
-
return (_jsx("nav", { className: "h-full pl-6", children: _jsx("ul", { className: tw('flex h-full gap-2', layout === 'sidebar' ? 'flex-col items-start' : 'items-center'), children: navigation.map(({ href, isActive, label }) => (_jsx("li", { className: tw(layout === 'sidebar' ? null : 'h-full'), children: _jsx(Link, { className: tw('relative flex h-full items-center px-1 font-semibold', isActive
|
|
13
|
-
? 'before:bg-primary text-white before:absolute before:bottom-0 before:left-0 before:right-0 before:h-1'
|
|
14
|
-
: 'text-white/80 hover:text-white'), href: href, children: label }) }, href))) }) }));
|
|
3
|
+
import { filterList } from '../../services/PermissionService';
|
|
4
|
+
import AppNavigationComponent from '../AppNavigationComponent';
|
|
5
|
+
async function AppNavigation({ layout, }) {
|
|
6
|
+
return (_jsx(AppNavigationComponent, { data: await filterList(getConfig().navigation), layout: layout }));
|
|
15
7
|
}
|
|
16
8
|
export default AppNavigation;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface ConfigNavigationObject {
|
|
3
|
+
href: string;
|
|
4
|
+
label: string;
|
|
5
|
+
permission?: string;
|
|
6
|
+
}
|
|
7
|
+
interface AppNavigationComponentProps {
|
|
8
|
+
data: ConfigNavigationObject[];
|
|
9
|
+
layout?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function AppNavigationComponent({ data, layout, }: Readonly<AppNavigationComponentProps>): React.ReactElement;
|
|
12
|
+
export default AppNavigationComponent;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Link, tw } from '@sqrzro/components';
|
|
4
|
+
import useNavigation from '../../hooks/useNavigation';
|
|
5
|
+
function getActiveClassNames(layout) {
|
|
6
|
+
if (layout === 'sidebar') {
|
|
7
|
+
return 'bg-slate-700 text-white';
|
|
8
|
+
}
|
|
9
|
+
return 'before:bg-primary text-white before:absolute before:bottom-0 before:left-0 before:right-0 before:h-1';
|
|
10
|
+
}
|
|
11
|
+
function AppNavigationComponent({ data, layout, }) {
|
|
12
|
+
const navigation = useNavigation(data);
|
|
13
|
+
return (_jsx("nav", { className: tw('h-full', layout === 'sidebar' ? 'w-full' : 'pl-6'), children: _jsx("ul", { className: tw('flex h-full gap-4', layout === 'sidebar' ? 'flex-col items-start' : 'items-center'), children: navigation.map(({ href, isActive, label }) => (_jsx("li", { className: tw(layout === 'sidebar' ? 'w-full' : 'h-full'), children: _jsx(Link, { className: tw(layout === 'sidebar'
|
|
14
|
+
? 'block px-4 py-3'
|
|
15
|
+
: 'relative flex h-full items-center px-1 font-semibold', isActive
|
|
16
|
+
? getActiveClassNames(layout)
|
|
17
|
+
: 'text-white/80 hover:text-white'), href: href, children: label }) }, href))) }) }));
|
|
18
|
+
}
|
|
19
|
+
export default AppNavigationComponent;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
declare const classNames: {
|
|
2
|
+
root: string;
|
|
3
|
+
logo: string;
|
|
4
|
+
panel: string;
|
|
5
|
+
title: string;
|
|
6
|
+
link: string;
|
|
7
|
+
actions: string;
|
|
8
|
+
footer: string;
|
|
9
|
+
};
|
|
10
|
+
export default classNames;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Auth as AuthComponent } from '@sqrzro/components';
|
|
3
1
|
const classNames = {
|
|
4
2
|
root: 'bg-slate-800 gap-8',
|
|
5
3
|
logo: 'mx-auto flex h-12 w-32 items-end',
|
|
@@ -9,7 +7,4 @@ const classNames = {
|
|
|
9
7
|
actions: 'pt-2 w-full',
|
|
10
8
|
footer: '-mb-2',
|
|
11
9
|
};
|
|
12
|
-
|
|
13
|
-
return _jsx(AuthComponent, { ...props, classNames: classNames });
|
|
14
|
-
}
|
|
15
|
-
export default Auth;
|
|
10
|
+
export default classNames;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { StyleVariant } from '@sqrzro/interfaces';
|
|
3
3
|
export interface BadgeProps {
|
|
4
4
|
children: React.ReactNode;
|
|
5
|
-
variant
|
|
5
|
+
variant?: StyleVariant | null;
|
|
6
6
|
}
|
|
7
7
|
declare function Badge({ children, variant }: Readonly<BadgeProps>): React.ReactElement;
|
|
8
8
|
export default Badge;
|
|
@@ -8,13 +8,13 @@ const classMap = {
|
|
|
8
8
|
success: 'bg-green-100 text-green-700',
|
|
9
9
|
};
|
|
10
10
|
const dotMap = {
|
|
11
|
-
danger: 'fill-red-
|
|
12
|
-
error: 'fill-red-
|
|
13
|
-
info: 'fill-sky-
|
|
14
|
-
warning: 'fill-yellow-
|
|
15
|
-
success: 'fill-green-
|
|
11
|
+
danger: 'fill-red-400',
|
|
12
|
+
error: 'fill-red-400',
|
|
13
|
+
info: 'fill-sky-400',
|
|
14
|
+
warning: 'fill-yellow-400',
|
|
15
|
+
success: 'fill-green-400',
|
|
16
16
|
};
|
|
17
17
|
function Badge({ children, variant }) {
|
|
18
|
-
return (_jsxs("strong", { className: tw('inline-flex items-center gap-1.5 rounded-full px-2 py-1 text-xs font-medium', classMap[variant]), children: [_jsx("svg", { "aria-hidden": "true", className: tw('h-1.5 w-1.5', dotMap[variant]), viewBox: "0 0 6 6", children: _jsx("circle", { cx: "3", cy: "3", r: "3" }) }), children] }));
|
|
18
|
+
return (_jsxs("strong", { className: tw('inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-2 py-1 text-xs font-medium text-slate-600', variant ? classMap[variant] : null), "data-testid": "badge-root", children: [_jsx("svg", { "aria-hidden": "true", className: tw('h-1.5 w-1.5 fill-slate-400', variant ? dotMap[variant] : null), viewBox: "0 0 6 6", children: _jsx("circle", { cx: "3", cy: "3", r: "3" }) }), children] }));
|
|
19
19
|
}
|
|
20
20
|
export default Badge;
|
|
@@ -3,7 +3,8 @@ import type { FilterObject } from '../FilterBarItem';
|
|
|
3
3
|
export type { FilterObject } from '../FilterBarItem';
|
|
4
4
|
export interface FilterBarProps {
|
|
5
5
|
hasSearch?: boolean;
|
|
6
|
+
layout?: string;
|
|
6
7
|
map?: FilterObject[];
|
|
7
8
|
}
|
|
8
|
-
declare function FilterBar({ hasSearch, map }: Readonly<FilterBarProps>): React.ReactElement;
|
|
9
|
+
declare function FilterBar({ hasSearch, layout, map }: Readonly<FilterBarProps>): React.ReactElement;
|
|
9
10
|
export default FilterBar;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import { TextInput } from '@sqrzro/components';
|
|
4
|
+
import { TextInput, tw } from '@sqrzro/components';
|
|
5
5
|
import { useFilters } from '@sqrzro/hooks';
|
|
6
6
|
import { useRouter } from 'next/navigation';
|
|
7
|
+
import FilterBarClearButton from '../FilterBarClearButton';
|
|
7
8
|
import FilterBarItem from '../FilterBarItem';
|
|
8
|
-
function FilterBar({ hasSearch, map }) {
|
|
9
|
+
function FilterBar({ hasSearch, layout, map }) {
|
|
9
10
|
const router = useRouter();
|
|
10
11
|
const [filters, setFilter] = useFilters();
|
|
11
12
|
const [search, setSearch] = useState(filters.get('search') || '');
|
|
@@ -16,11 +17,23 @@ function FilterBar({ hasSearch, map }) {
|
|
|
16
17
|
function handleSearchChange(event) {
|
|
17
18
|
setSearch(event.target.value || '');
|
|
18
19
|
}
|
|
20
|
+
function handleSearchClear() {
|
|
21
|
+
setSearch('');
|
|
22
|
+
setFilter('search', '');
|
|
23
|
+
}
|
|
19
24
|
function handleSearchKeyDown(event) {
|
|
20
25
|
if (event.key === 'Enter') {
|
|
21
26
|
setFilter('search', event.currentTarget.value);
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
|
-
return (_jsxs("ul", { className:
|
|
29
|
+
return (_jsxs("ul", { className: tw('flex gap-2', layout === 'sidebar'
|
|
30
|
+
? ''
|
|
31
|
+
: 'relative before:absolute before:left-[calc(50%-50vw)] before:top-full before:h-24 before:w-screen before:bg-slate-800'), children: [hasSearch ? (_jsxs("li", { className: "relative", children: [_jsx(TextInput, { classNames: {
|
|
32
|
+
root: {
|
|
33
|
+
default: tw('h-8 w-56 rounded-full border border-slate-400 px-3 text-xs text-white placeholder:text-slate-300', filters.get('search')
|
|
34
|
+
? 'bg-slate-700'
|
|
35
|
+
: 'bg-transparent focus:bg-slate-700'),
|
|
36
|
+
},
|
|
37
|
+
}, name: "search", onChange: handleSearchChange, onKeyDown: handleSearchKeyDown, placeholder: "Search...", value: search }), filters.get('search') ? (_jsx(FilterBarClearButton, { onClick: handleSearchClear })) : null] })) : null, (map || []).map((filter) => (_jsx(FilterBarItem, { onChange: handleChange, value: filters.get(filter.name) || '', ...filter }, filter.name)))] }));
|
|
25
38
|
}
|
|
26
39
|
export default FilterBar;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function FilterBarClearButton({ onClick, }) {
|
|
3
|
+
return (_jsxs("button", { className: "absolute right-0 top-0 flex h-full w-10 select-none items-center justify-center", onClick: onClick, type: "button", children: [_jsx("span", { className: "sr-only", children: "Remove" }), _jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-white leading-none text-slate-800", children: "\u00D7" })] }));
|
|
4
|
+
}
|
|
5
|
+
export default FilterBarClearButton;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { SimpleObject } from '@sqrzro/interfaces';
|
|
3
2
|
export type FilterType = 'boolean' | 'date' | 'dropdown';
|
|
4
3
|
export interface FilterObject {
|
|
5
|
-
data?:
|
|
4
|
+
data?: {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}[];
|
|
6
8
|
label: string;
|
|
7
9
|
name: string;
|
|
8
10
|
type: FilterType;
|
|
@@ -5,6 +5,7 @@ import { tw } from '@sqrzro/components';
|
|
|
5
5
|
import BooleanFilter, { renderBooleanValue } from '../BooleanFilter';
|
|
6
6
|
import DateFilter, { renderDateValue, transformDateValue } from '../DateFilter';
|
|
7
7
|
import DropdownFilter, { renderDropdownValue } from '../DropdownFilter';
|
|
8
|
+
import FilterBarClearButton from '../FilterBarClearButton';
|
|
8
9
|
const map = {
|
|
9
10
|
boolean: { component: BooleanFilter, renderValue: renderBooleanValue },
|
|
10
11
|
date: {
|
|
@@ -26,6 +27,6 @@ function FilterBarItem({ data, label, name, onChange, type, value, }) {
|
|
|
26
27
|
onChange(name, '');
|
|
27
28
|
}
|
|
28
29
|
const { component: FilterComponent, renderValue, size, transformValue } = map[type];
|
|
29
|
-
return (_jsxs("li", { className: tw('relative inline-flex gap-2 rounded-full border px-5
|
|
30
|
+
return (_jsxs("li", { className: tw('relative inline-flex h-8 items-center gap-2 rounded-full border px-5 text-xs text-white', value ? 'border-solid border-slate-400' : 'border-dashed border-slate-400', value ? 'bg-slate-700 pr-10' : 'text-slate-300'), children: [_jsx("p", { className: "", children: label }), value ? (_jsx("p", { className: "border-l border-l-slate-300 pl-2 font-semibold", children: renderValue(value, data) })) : null, _jsx("button", { className: tw('absolute left-0 top-0 h-full select-none text-transparent', value ? 'right-10' : 'right-0'), id: `${name}-control`, popovertarget: `${name}-target`, type: "button", children: "Edit" }), value ? _jsx(FilterBarClearButton, { onClick: handleClear }) : null, _jsx("div", { ref: panel, anchor: `${name}-control`, className: tw('show left-[anchor(left)] top-[anchor(bottom)] -ml-px mt-2 origin-top-left origin-top-left rounded bg-white shadow-lg', size || DEFAULT_SIZE), id: `${name}-target`, popover: "auto", children: _jsx("div", { className: "py-1", role: "none", children: _jsx(FilterComponent, { data: data, name: name, onChange: handleChange, value: transformValue ? transformValue(value) : value }) }) })] }));
|
|
30
31
|
}
|
|
31
32
|
export default FilterBarItem;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import type { ListProps } from '../List';
|
|
3
|
-
export type GridListProps<Item, Params> = Omit<ListProps<Item, Params>, 'renderItem'>;
|
|
3
|
+
export type GridListProps<Item extends object, Params> = Omit<ListProps<Item, Params>, 'renderItem'>;
|
|
4
4
|
declare function GridList<Item extends object, Params>(props: GridListProps<Item, Params>): React.ReactElement;
|
|
5
5
|
export default GridList;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import type { ListObject } from '../ListItem';
|
|
3
|
-
declare function GridListItem({
|
|
3
|
+
declare function GridListItem({ description, href, id, image, imageHref, meta, title, }: Readonly<ListObject>): React.ReactElement;
|
|
4
4
|
export default GridListItem;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
3
|
import { Link } from '@sqrzro/components';
|
|
4
4
|
import { renderMeta } from '../ListItem';
|
|
5
|
-
function GridListItem({
|
|
6
|
-
return (_jsx("li", { children: _jsxs("article", { className: "bg-panel overflow-hidden rounded p-4 shadow", children: [_jsx("div", { className: "relative mb-4 h-64 rounded border border border-slate-300 bg-slate-50 p-4", children: image ? (_jsxs(Fragment, { children: [_jsx("img", { alt: "", className: "h-full w-full object-contain", src: image }), imageHref ? (_jsx(Link, { className: "absolute inset-0", href: imageHref, scroll: false, children: _jsx("span", { className: "sr-only", children: "Edit Image" }) })) : null] })) : imageHref ? (_jsxs(Link, { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 font-semibold text-slate-500", href: imageHref, scroll: false, children: [_jsx("i", { className: "block h-12 w-12 rounded-full border-2 border-slate-300" }), "Add Image"] })) : null }), _jsx("h2", { className: "text-base font-semibold", children: href ? _jsx(Link, { href: href, children: title }) : title }),
|
|
5
|
+
function GridListItem({ description, href, id, image, imageHref, meta, title, }) {
|
|
6
|
+
return (_jsx("li", { children: _jsxs("article", { className: "bg-panel overflow-hidden rounded p-4 shadow", children: [_jsx("div", { className: "relative mb-4 h-64 rounded border border border-slate-300 bg-slate-50 p-4", children: image ? (_jsxs(Fragment, { children: [_jsx("img", { alt: "", className: "h-full w-full object-contain", src: image }), imageHref ? (_jsx(Link, { className: "absolute inset-0", href: imageHref, scroll: false, children: _jsx("span", { className: "sr-only", children: "Edit Image" }) })) : null] })) : imageHref ? (_jsxs(Link, { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 font-semibold text-slate-500", href: imageHref, scroll: false, children: [_jsx("i", { className: "block h-12 w-12 rounded-full border-2 border-slate-300" }), "Add Image"] })) : null }), _jsx("h2", { className: "text-base font-semibold", children: href ? _jsx(Link, { href: href, children: title }) : title }), description ? _jsx("p", { className: "text-xs text-slate-500", children: description }) : null, meta ? (_jsxs(Fragment, { children: [_jsx("hr", { className: "my-4" }), _jsx("div", { className: "flex items-center justify-between", children: meta ? renderMeta(id, meta) : null })] })) : null, href ? (_jsxs(Fragment, { children: [_jsx("hr", { className: "my-4" }), _jsx(Link, { href: href, scroll: false, children: "Edit" })] })) : null] }) }));
|
|
7
7
|
}
|
|
8
8
|
export default GridListItem;
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { tw } from '@sqrzro/components';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
warning: 'warning',
|
|
3
|
+
const iconClassMap = {
|
|
4
|
+
danger: 'bg-[url(/admin/images/danger.svg)]',
|
|
5
|
+
info: 'bg-[url(/admin/images/info.svg)]',
|
|
6
|
+
success: 'bg-[url(/admin/images/success.svg)]',
|
|
7
|
+
warning: 'bg-[url(/admin/images/warning.svg)]',
|
|
9
8
|
};
|
|
10
9
|
const classMap = {
|
|
11
|
-
danger: 'bg-red-
|
|
12
|
-
info: 'bg-sky-
|
|
13
|
-
success: 'bg-green-
|
|
14
|
-
warning: 'bg-yellow-
|
|
10
|
+
danger: 'bg-red-100 text-red-700',
|
|
11
|
+
info: 'bg-sky-100 text-sky-700',
|
|
12
|
+
success: 'bg-green-100 text-green-700',
|
|
13
|
+
warning: 'bg-yellow-100 text-yellow-700',
|
|
15
14
|
};
|
|
16
|
-
function mapVariantToIconType(variant) {
|
|
17
|
-
return iconTypeMap[variant] || 'info';
|
|
18
|
-
}
|
|
19
15
|
function InfoPanel({ children, variant = 'info' }) {
|
|
20
|
-
return (_jsxs("aside", { className: tw('
|
|
16
|
+
return (_jsxs("aside", { className: tw('flex gap-2 rounded p-4', classMap[variant]), children: [_jsx("i", { className: tw('aspect-square w-5 flex-none bg-contain bg-no-repeat', iconClassMap[variant]) }), _jsx("div", { children: children })] }));
|
|
21
17
|
}
|
|
22
18
|
export default InfoPanel;
|
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { EmptyMessageProps } from '@sqrzro/components';
|
|
3
|
-
import type { Errorable } from '@sqrzro/interfaces';
|
|
4
2
|
import type { FilterObject } from '../FilterBar';
|
|
5
|
-
import type {
|
|
6
|
-
export interface ListComponentProps {
|
|
7
|
-
params?: Record<string, string>;
|
|
8
|
-
searchParams?: Record<string, string>;
|
|
9
|
-
}
|
|
10
|
-
export interface ListProps<Item, Params> {
|
|
11
|
-
actions?: ({ id }: {
|
|
12
|
-
readonly id: number;
|
|
13
|
-
}) => React.ReactElement;
|
|
14
|
-
columns?: number;
|
|
15
|
-
emptyMessageProps?: EmptyMessageProps;
|
|
3
|
+
import type { ListComponentProps } from '../ListComponent';
|
|
4
|
+
export interface ListProps<Item extends object, Params> extends ListComponentProps<Item, Params> {
|
|
16
5
|
filters?: FilterObject[];
|
|
17
|
-
fn: (params?: Params, searchParams?: URLSearchParams) => Promise<Errorable<Item[]>>;
|
|
18
|
-
hasSearch?: boolean;
|
|
19
|
-
isMinimal?: boolean;
|
|
20
|
-
renderItem?: (props: ListObject) => React.ReactElement;
|
|
21
|
-
params?: Params;
|
|
22
|
-
transformer?: (item: Item) => ListObject;
|
|
23
6
|
}
|
|
24
|
-
declare function List<Item extends object, Params>({
|
|
7
|
+
declare function List<Item extends object, Params>({ filters, hasSearch, ...props }: Readonly<ListProps<Item, Params>>): Promise<React.ReactElement>;
|
|
25
8
|
export default List;
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { Fragment, Suspense } from 'react';
|
|
3
|
+
import { getLayout } from '../../services/SettingsService';
|
|
4
4
|
import FilterBar from '../FilterBar';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
id: 0,
|
|
9
|
-
title: '',
|
|
10
|
-
};
|
|
11
|
-
}
|
|
5
|
+
import ListComponent from '../ListComponent';
|
|
6
|
+
import ListSkeleton from '../ListSkeleton';
|
|
12
7
|
const reservedSearchParams = ['action'];
|
|
13
8
|
function checkHasFilters(searchParams) {
|
|
14
9
|
return Array.from(searchParams.entries()).some(([key, value]) => !reservedSearchParams.includes(key) && Boolean(value));
|
|
15
10
|
}
|
|
16
|
-
async function List({
|
|
17
|
-
|
|
11
|
+
async function List({ filters, hasSearch, ...props }) {
|
|
12
|
+
const layout = await getLayout();
|
|
13
|
+
let searchParams; // eslint-disable-line @typescript-eslint/init-declarations
|
|
18
14
|
try {
|
|
19
15
|
const { headers } = await import('next/headers');
|
|
20
16
|
searchParams = new URLSearchParams(headers().get('x-search-params') || '');
|
|
@@ -22,18 +18,6 @@ async function List({ actions, columns, emptyMessageProps, filters, fn, hasSearc
|
|
|
22
18
|
catch (err) {
|
|
23
19
|
searchParams = new URLSearchParams();
|
|
24
20
|
}
|
|
25
|
-
|
|
26
|
-
if (error) {
|
|
27
|
-
return _jsx("div", { children: "Error" });
|
|
28
|
-
}
|
|
29
|
-
const data = response.map(transformer || defaultTransformer);
|
|
30
|
-
const hasFilters = checkHasFilters(searchParams);
|
|
31
|
-
const componentEmptyMessageProps = hasFilters
|
|
32
|
-
? {
|
|
33
|
-
children: 'Try adjusting the filters above. If you think this is a mistake, please contact your site administrator.',
|
|
34
|
-
title: 'No results match the current filters',
|
|
35
|
-
}
|
|
36
|
-
: emptyMessageProps;
|
|
37
|
-
return (_jsxs(Fragment, { children: [(filters || hasSearch) && (data.length || hasFilters) ? (_jsx(FilterBar, { hasSearch: hasSearch, map: filters })) : null, data.length ? (_jsx("ul", { className: tw('relative', columns ? 'grid grid-cols-3 gap-6' : 'flex flex-col gap-4'), children: data.map((item) => renderItem({ actions, ...item })) })) : (_jsx(EmptyMessage, { ...componentEmptyMessageProps, classNameProps: { isMinimal } }))] }));
|
|
21
|
+
return (_jsxs(Fragment, { children: [filters || hasSearch ? (_jsx(FilterBar, { hasSearch: hasSearch, layout: layout, map: filters })) : null, _jsx(Suspense, { fallback: _jsx(ListSkeleton, {}), children: _jsx(ListComponent, { ...props, searchParams: searchParams }) })] }));
|
|
38
22
|
}
|
|
39
23
|
export default List;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { ConfirmableAction } from '@sqrzro/interfaces';
|
|
2
|
+
import type { ConfirmableAction, LinkableAction } from '@sqrzro/interfaces';
|
|
3
|
+
export interface ListAction extends Omit<ConfirmableAction & LinkableAction, 'onClick'> {
|
|
4
|
+
onClick?: (id: string) => void;
|
|
5
|
+
}
|
|
3
6
|
export interface ListActionsProps {
|
|
4
|
-
actions:
|
|
5
|
-
id:
|
|
7
|
+
actions: ListAction[];
|
|
8
|
+
id: string;
|
|
6
9
|
}
|
|
7
10
|
declare function ListActions({ actions, id }: Readonly<ListActionsProps>): React.ReactElement;
|
|
8
11
|
export default ListActions;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useClickOutside } from '@sqrzro/hooks';
|
|
4
|
-
import
|
|
4
|
+
import Menu from '../Menu';
|
|
5
5
|
function ListActions({ actions, id }) {
|
|
6
6
|
const [isOpen, setIsOpen, ref] = useClickOutside();
|
|
7
|
+
function handleClick(item) {
|
|
8
|
+
item.onClick?.(id);
|
|
9
|
+
}
|
|
7
10
|
function toggleIsOpen() {
|
|
8
11
|
setIsOpen(!isOpen);
|
|
9
12
|
}
|
|
10
|
-
|
|
13
|
+
const transformedActions = actions.map((item) => ({
|
|
14
|
+
...item,
|
|
15
|
+
onClick: () => handleClick(item),
|
|
16
|
+
}));
|
|
17
|
+
return (_jsxs("div", { ref: ref, className: "relative h-5", children: [_jsx("button", { onClick: toggleIsOpen, type: "button", children: _jsx("svg", { "aria-hidden": "true", className: "h-5 w-5 fill-slate-500", viewBox: "0 0 20 20", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M10 3a1.5 1.5 0 110 3 1.5 1.5 0 010-3zM10 8.5a1.5 1.5 0 110 3 1.5 1.5 0 010-3zM11.5 15.5a1.5 1.5 0 10-3 0 1.5 1.5 0 003 0z" }) }) }), isOpen ? _jsx(Menu, { actions: transformedActions, align: "right" }) : null] }));
|
|
11
18
|
}
|
|
12
19
|
export default ListActions;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { EmptyMessageProps } from '@sqrzro/components';
|
|
3
|
+
import type { ListAction } from '../ListActions';
|
|
4
|
+
import type { ListObject } from '../ListItem';
|
|
5
|
+
export interface ListClientComponentProps {
|
|
6
|
+
actions?: ListAction[];
|
|
7
|
+
columns?: number;
|
|
8
|
+
data: ListObject[];
|
|
9
|
+
emptyMessageProps?: EmptyMessageProps;
|
|
10
|
+
hasFilters?: boolean;
|
|
11
|
+
isMinimal?: boolean;
|
|
12
|
+
renderItem?: (props: ListObject) => JSX.Element;
|
|
13
|
+
}
|
|
14
|
+
declare function ListClientComponent({ actions, columns, data, emptyMessageProps, hasFilters, isMinimal, renderItem, }: Readonly<ListClientComponentProps>): React.ReactElement;
|
|
15
|
+
export default ListClientComponent;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { EmptyMessage, tw } from '@sqrzro/components';
|
|
4
|
+
import ListItem from '../ListItem';
|
|
5
|
+
function ListClientComponent({ actions, columns, data, emptyMessageProps, hasFilters, isMinimal, renderItem = ListItem, }) {
|
|
6
|
+
const componentEmptyMessageProps = hasFilters
|
|
7
|
+
? {
|
|
8
|
+
children: 'Try adjusting the filters above. If you think this is a mistake, please contact your site administrator.',
|
|
9
|
+
title: 'No results match the current filters',
|
|
10
|
+
}
|
|
11
|
+
: emptyMessageProps;
|
|
12
|
+
return data.length ? (_jsx("ul", { className: tw('relative', columns ? 'grid grid-cols-3 gap-6' : 'flex flex-col gap-4'), children: data.map((item) => (_jsx(Fragment, { children: renderItem({ actions, isMinimal, ...item }) }, item.id))) })) : (_jsx(EmptyMessage, { ...componentEmptyMessageProps, classNameProps: { isMinimal } }));
|
|
13
|
+
}
|
|
14
|
+
export default ListClientComponent;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { Errorable } from '@sqrzro/interfaces';
|
|
3
|
+
import type { ListObject } from '../ListItem';
|
|
4
|
+
import type { ListClientComponentProps } from '../ListClientComponent';
|
|
5
|
+
export interface ListComponentProps<Item, Params> extends Omit<ListClientComponentProps, 'data' | 'hasFilters'> {
|
|
6
|
+
fn: (params?: Params, searchParams?: URLSearchParams) => Promise<Errorable<Item[]>>;
|
|
7
|
+
hasFilters?: boolean;
|
|
8
|
+
hasSearch?: boolean;
|
|
9
|
+
params?: Params;
|
|
10
|
+
searchParams?: URLSearchParams;
|
|
11
|
+
transformer?: (item: Item) => ListObject;
|
|
12
|
+
}
|
|
13
|
+
declare function ListComponent<Item extends object, Params>({ fn, hasFilters, params, searchParams, transformer, ...clientProps }: Readonly<ListComponentProps<Item, Params>>): Promise<React.ReactElement>;
|
|
14
|
+
export default ListComponent;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { filterList } from '../../services/PermissionService';
|
|
3
|
+
import ListClientComponent from '../ListClientComponent';
|
|
4
|
+
function defaultTransformer() {
|
|
5
|
+
return {
|
|
6
|
+
id: '',
|
|
7
|
+
title: '',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
async function ListComponent({ fn, hasFilters, params, searchParams, transformer, ...clientProps }) {
|
|
11
|
+
const [response, error] = await fn(params, searchParams);
|
|
12
|
+
if (!Array.isArray(response)) {
|
|
13
|
+
throw new Error('Response is not an array. Did you forget to return an Errorable object in the function?');
|
|
14
|
+
}
|
|
15
|
+
if (error) {
|
|
16
|
+
return _jsx("div", { children: "Error" });
|
|
17
|
+
}
|
|
18
|
+
const data = await filterList(response.map(transformer || defaultTransformer));
|
|
19
|
+
return _jsx(ListClientComponent, { data: data, hasFilters: hasFilters, ...clientProps });
|
|
20
|
+
}
|
|
21
|
+
export default ListComponent;
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
+
import type { ListAction } from '../ListActions';
|
|
2
3
|
export interface ListObject {
|
|
3
|
-
actions?:
|
|
4
|
-
id: number | string;
|
|
5
|
-
}) => React.ReactElement;
|
|
4
|
+
actions?: ListAction[];
|
|
6
5
|
description?: React.ReactNode | null;
|
|
7
6
|
href?: string;
|
|
8
|
-
id:
|
|
7
|
+
id: string;
|
|
8
|
+
isMinimal?: boolean;
|
|
9
9
|
image?: string | null;
|
|
10
10
|
imageHref?: string | null;
|
|
11
11
|
meta?: React.ReactNode[] | Record<string, React.ReactNode>;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
permission?: string;
|
|
13
|
+
secondary?: {
|
|
14
|
+
description?: string;
|
|
15
|
+
meta?: React.ReactNode[];
|
|
16
|
+
title?: string;
|
|
17
|
+
};
|
|
18
|
+
tertiary?: React.ReactNode;
|
|
19
|
+
title?: React.ReactNode;
|
|
14
20
|
}
|
|
15
|
-
export declare function renderMeta(meta: ListObject['meta']): React.ReactElement | null;
|
|
16
|
-
declare function ListItem({ actions
|
|
17
|
-
description, href, id, meta, title, }: Readonly<ListObject>): React.ReactElement;
|
|
21
|
+
export declare function renderMeta(id: string, meta: ListObject['meta']): React.ReactElement | null;
|
|
22
|
+
declare function ListItem({ actions, description, href, id, isMinimal, meta, secondary, tertiary, title, }: Readonly<ListObject>): React.ReactElement;
|
|
18
23
|
export default ListItem;
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { isValidElement } from 'react';
|
|
3
|
+
import { Link, tw } from '@sqrzro/components';
|
|
4
|
+
import { filterNull } from '@sqrzro/utility';
|
|
5
|
+
import ListActions from '../ListActions';
|
|
6
|
+
function getKey(item) {
|
|
7
|
+
if (isValidElement(item)) {
|
|
8
|
+
return item.key;
|
|
9
|
+
}
|
|
10
|
+
return String(item);
|
|
11
|
+
}
|
|
12
|
+
export function renderMeta(id, meta) {
|
|
4
13
|
if (!meta) {
|
|
5
14
|
return null;
|
|
6
15
|
}
|
|
7
16
|
if (Array.isArray(meta)) {
|
|
8
|
-
return (_jsx("ul", { className: "flex items-center gap-4 text-xs text-slate-
|
|
17
|
+
return (_jsx("ul", { className: "flex items-center gap-4 text-xs text-slate-500", "data-testid": `list-item-meta--${id}`, children: filterNull(meta).map((item) => (_jsx("li", { children: item }, getKey(item)))) }));
|
|
9
18
|
}
|
|
10
19
|
return (_jsx("table", { className: "w-full text-xs", children: _jsx("tbody", { children: Object.entries(meta).map(([key, value]) => (_jsxs("tr", { className: "odd:bg-slate-100", children: [_jsx("th", { className: "p-2 font-semibold", children: key }), _jsx("td", { className: "p-2 text-right", children: value || '-' })] }, key))) }) }));
|
|
11
20
|
}
|
|
12
|
-
function ListItem({ actions
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
function ListItem({ actions, description, href, id, isMinimal, meta, secondary, tertiary, title, }) {
|
|
22
|
+
return (_jsx("li", { children: _jsx("article", { className: tw('@container', isMinimal
|
|
23
|
+
? 'border-b border-slate-200 pb-4'
|
|
24
|
+
: 'bg-panel rounded p-4 shadow-[0px_0px_0px_1px_rgba(9,9,11,0.07),0px_2px_2px_0px_rgba(9,9,11,0.05)]'), "data-testid": `list-item-root--${id}`, children: _jsxs("div", { className: "grid grid-cols-[3fr_2fr_2fr_auto]", children: [_jsxs("div", { children: [title ? (_jsx("h2", { className: "text-base font-semibold", "data-testid": `list-item-title--${id}`, children: href ? _jsx(Link, { href: href, children: title }) : title })) : null, description, meta ? (_jsx("div", { className: tw(isMinimal ? 'mt-1' : 'mt-2 flex items-center justify-between'), children: meta ? renderMeta(id, meta) : null })) : null] }), tertiary ? (_jsx("div", { className: "flex h-full flex-col justify-center", children: tertiary })) : null, secondary ? (_jsxs("div", { className: "col-start-3 text-right", children: [secondary.title ? (_jsx("p", { className: "text-base font-semibold", children: secondary.title })) : null, secondary.description ? (_jsx("div", { className: "text-xs text-slate-500", children: secondary.description })) : null, secondary.meta ? (_jsx("ul", { className: "flex items-center justify-end gap-4 text-xs text-slate-500", children: filterNull(secondary.meta).map((item) => (_jsx("li", { children: item }, getKey(item)))) })) : null] })) : null, actions ? (_jsx("div", { className: "h-5 self-center pl-6", children: _jsx(ListActions, { actions: actions, id: id }) })) : null] }) }) }));
|
|
15
25
|
}
|
|
16
26
|
export default ListItem;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function ListSkeleton({}) {
|
|
3
|
+
return (_jsx("div", { className: "relative flex flex-col gap-4", children: Array.from({ length: 4 }).map((item, index) => (_jsxs("div", { className: "grid grid-cols-[auto_auto] grid-rows-[auto_auto] gap-2 rounded bg-white p-4 shadow-[0px_0px_0px_1px_rgba(9,9,11,0.07),0px_2px_2px_0px_rgba(9,9,11,0.05)]", children: [_jsx("div", { className: "h-7 w-64 bg-slate-200" }), _jsx("div", { className: "row-start-2 h-4 w-24 bg-slate-200" }), _jsx("div", { className: "col-start-2 flex justify-end", children: _jsx("div", { className: "col-start-2 h-7 w-24 bg-slate-200" }) }), _jsx("div", { className: "col-start-2 row-start-2 flex justify-end", children: _jsx("div", { className: "h-4 w-36 bg-slate-200" }) })] }, index))) }));
|
|
4
|
+
}
|
|
5
|
+
export default ListSkeleton;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Link
|
|
2
|
+
import { Link } from '@sqrzro/components';
|
|
3
3
|
function MeActions() {
|
|
4
|
-
return (_jsxs("ul", { className: "flex gap-2 text-xs text-slate-300", children: [_jsx("li", { children: _jsx(Link, { href: "/settings", children: "Settings" }) }), _jsx("li", {
|
|
4
|
+
return (_jsxs("ul", { className: "flex gap-2 text-xs text-slate-300", children: [_jsx("li", { children: _jsx(Link, { href: "/settings", children: "Settings" }) }), _jsx("li", {})] }));
|
|
5
5
|
}
|
|
6
6
|
export default MeActions;
|