@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.
Files changed (84) hide show
  1. package/dist/components/AppLayout/index.js +5 -5
  2. package/dist/components/AppNavigation/index.d.ts +2 -2
  3. package/dist/components/AppNavigation/index.js +4 -12
  4. package/dist/components/AppNavigationComponent/index.d.ts +12 -0
  5. package/dist/components/AppNavigationComponent/index.js +19 -0
  6. package/dist/components/Auth/index.d.ts +10 -5
  7. package/dist/components/Auth/index.js +1 -6
  8. package/dist/components/Badge/index.d.ts +1 -1
  9. package/dist/components/Badge/index.js +6 -6
  10. package/dist/components/FilterBar/index.d.ts +2 -1
  11. package/dist/components/FilterBar/index.js +16 -3
  12. package/dist/components/FilterBarClearButton/index.d.ts +6 -0
  13. package/dist/components/FilterBarClearButton/index.js +5 -0
  14. package/dist/components/FilterBarItem/index.d.ts +4 -2
  15. package/dist/components/FilterBarItem/index.js +2 -1
  16. package/dist/components/GridList/index.d.ts +1 -1
  17. package/dist/components/GridListItem/index.d.ts +1 -1
  18. package/dist/components/GridListItem/index.js +2 -2
  19. package/dist/components/InfoPanel/index.js +10 -14
  20. package/dist/components/List/index.d.ts +3 -20
  21. package/dist/components/List/index.js +8 -24
  22. package/dist/components/ListActions/index.d.ts +6 -3
  23. package/dist/components/ListActions/index.js +10 -3
  24. package/dist/components/ListClientComponent/index.d.ts +15 -0
  25. package/dist/components/ListClientComponent/index.js +14 -0
  26. package/dist/components/ListComponent/index.d.ts +14 -0
  27. package/dist/components/ListComponent/index.js +21 -0
  28. package/dist/components/ListItem/index.d.ts +14 -9
  29. package/dist/components/ListItem/index.js +16 -6
  30. package/dist/components/ListSkeleton/index.d.ts +5 -0
  31. package/dist/components/ListSkeleton/index.js +5 -0
  32. package/dist/components/MeActions/index.js +2 -2
  33. package/dist/components/MePanel/index.d.ts +2 -1
  34. package/dist/components/MePanel/index.js +3 -2
  35. package/dist/components/Menu/index.d.ts +8 -0
  36. package/dist/components/Menu/index.js +7 -0
  37. package/dist/components/MenuItem/index.d.ts +5 -0
  38. package/dist/components/MenuItem/index.js +20 -0
  39. package/dist/components/Page/index.d.ts +4 -2
  40. package/dist/components/Page/index.js +4 -5
  41. package/dist/components/Panel/index.d.ts +3 -1
  42. package/dist/components/Panel/index.js +3 -2
  43. package/dist/components/RootLayout/index.d.ts +2 -1
  44. package/dist/components/RootLayout/index.js +3 -9
  45. package/dist/components/SettingsForm/index.d.ts +1 -3
  46. package/dist/components/SettingsForm/index.js +5 -5
  47. package/dist/components/SettingsPage/index.d.ts +1 -1
  48. package/dist/components/SettingsPage/index.js +2 -2
  49. package/dist/components/TableClientComponent/index.js +0 -8
  50. package/dist/components/Tabs/index.d.ts +2 -6
  51. package/dist/components/Tabs/index.js +5 -8
  52. package/dist/components/TabsComponent/index.d.ts +8 -0
  53. package/dist/components/TabsComponent/index.js +9 -0
  54. package/dist/components/index.d.ts +5 -5
  55. package/dist/components/index.js +2 -2
  56. package/dist/hooks/useNavigation.d.ts +11 -0
  57. package/dist/hooks/useNavigation.js +27 -0
  58. package/dist/index.cjs +1266 -0
  59. package/dist/index.cjs.map +1 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/interfaces.d.ts +8 -2
  62. package/dist/services/ConfigService.d.ts +7 -1
  63. package/dist/services/ConfigService.js +1 -1
  64. package/dist/services/PermissionService.d.ts +3 -0
  65. package/dist/services/PermissionService.js +25 -0
  66. package/dist/services/SettingsService.d.ts +8 -0
  67. package/dist/services/{LayoutService.js → SettingsService.js} +2 -2
  68. package/dist/styles/config.js +57 -21
  69. package/dist/styles/tailwind.d.ts +1 -1
  70. package/dist/styles/tailwind.js +6 -2
  71. package/package.json +27 -16
  72. package/dist/components/Icon/index.d.ts +0 -8
  73. package/dist/components/Icon/index.js +0 -23
  74. package/dist/icons/ErrorIcon/index.d.ts +0 -3
  75. package/dist/icons/ErrorIcon/index.js +0 -5
  76. package/dist/icons/InfoIcon/index.d.ts +0 -3
  77. package/dist/icons/InfoIcon/index.js +0 -5
  78. package/dist/icons/SuccessIcon/index.d.ts +0 -3
  79. package/dist/icons/SuccessIcon/index.js +0 -5
  80. package/dist/icons/WarningIcon/index.d.ts +0 -3
  81. package/dist/icons/WarningIcon/index.js +0 -5
  82. package/dist/services/LayoutService.d.ts +0 -7
  83. /package/{postcss.js → postcss.cjs} +0 -0
  84. /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 { Container, Link, tw } from '@sqrzro/components';
2
+ import { Link, tw } from '@sqrzro/components';
3
3
  import { getConfig } from '../../services/ConfigService';
4
- import { getLayout } from '../../services/LayoutService';
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: _jsx(Container, { isFullWidth: true, children: _jsxs("div", { className: tw('flex items-center', layout === 'sidebar'
11
- ? 'h-screen flex-col py-4'
12
- : 'h-16 border-b border-slate-700'), children: [_jsxs(Link, { className: tw(layout === 'sidebar' ? '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, { user: user })] }) }) }), _jsx("main", { className: "mb-10 block", children: children })] }));
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: string;
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
- function getNavigation() {
7
- const { navigation } = getConfig();
8
- return Object.entries(navigation).map(([href, label]) => ({ href, label }));
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
- /// <reference types="react" />
2
- import type { AuthProps as AuthComponentProps } from '@sqrzro/components';
3
- export type AuthProps = AuthComponentProps;
4
- declare function Auth(props: AuthComponentProps): JSX.Element;
5
- export default Auth;
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
- function Auth(props) {
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: StyleVariant;
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-500',
12
- error: 'fill-red-500',
13
- info: 'fill-sky-500',
14
- warning: 'fill-yellow-500',
15
- success: 'fill-green-500',
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: "relative flex gap-2 before:absolute before:left-[calc(50%-50vw)] before:top-full before:h-24 before:w-screen before:bg-slate-800", children: [hasSearch ? (_jsx("li", { children: _jsx(TextInput, { name: "search", onChange: handleSearchChange, onKeyDown: handleSearchKeyDown, value: search }) })) : null, (map || []).map((filter) => (_jsx(FilterBarItem, { onChange: handleChange, value: filters.get(filter.name) || '', ...filter }, filter.name)))] }));
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,6 @@
1
+ /// <reference types="react" />
2
+ interface FilterBarClearButtonProps {
3
+ onClick: () => void;
4
+ }
5
+ declare function FilterBarClearButton({ onClick, }: Readonly<FilterBarClearButtonProps>): React.ReactElement;
6
+ export default FilterBarClearButton;
@@ -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?: SimpleObject[];
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 py-2 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 ? (_jsxs("button", { className: "absolute right-0 top-0 flex h-full w-10 select-none items-center justify-center", onClick: handleClear, 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" })] })) : 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
+ 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({ actions: Actions, href, id, image, imageHref, meta, subtitle, title, }: Readonly<ListObject>): React.ReactElement;
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({ actions: Actions, href, id, image, imageHref, meta, subtitle, 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 }), subtitle ? _jsx("p", { className: "text-xs text-slate-500", children: subtitle }) : null, meta ? (_jsxs(Fragment, { children: [_jsx("hr", { className: "my-4" }), _jsx("div", { className: "flex items-center justify-between", children: meta ? renderMeta(meta) : null })] })) : null, href ? (_jsxs(Fragment, { children: [_jsx("hr", { className: "my-4" }), _jsx(Link, { href: href, scroll: false, children: "Edit" })] })) : null] }) }));
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
- import Icon from '../Icon';
4
- const iconTypeMap = {
5
- danger: 'danger',
6
- info: 'info',
7
- success: 'success',
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-50 text-red-700',
12
- info: 'bg-sky-50 text-sky-700',
13
- success: 'bg-green-50 text-green-700',
14
- warning: 'bg-yellow-50 text-yellow-700',
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('grid grid-cols-[1rem_auto] gap-4 rounded p-4', classMap[variant]), children: [_jsx(Icon, { type: mapVariantToIconType(variant) }), _jsx("div", { children: children })] }));
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 { ListObject } from '../ListItem';
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>({ actions, columns, emptyMessageProps, filters, fn, hasSearch, isMinimal, params, renderItem, transformer, }: Readonly<ListProps<Item, Params>>): Promise<React.ReactElement>;
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 { EmptyMessage, tw } from '@sqrzro/components';
2
+ import { Fragment, Suspense } from 'react';
3
+ import { getLayout } from '../../services/SettingsService';
4
4
  import FilterBar from '../FilterBar';
5
- import ListItem from '../ListItem';
6
- function defaultTransformer() {
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({ actions, columns, emptyMessageProps, filters, fn, hasSearch, isMinimal, params, renderItem = ListItem, transformer, }) {
17
- let searchParams;
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
- const [response, error] = await fn(params, searchParams);
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: ConfirmableAction<number>[];
5
- id: number;
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 { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useClickOutside } from '@sqrzro/hooks';
4
- import ListAction from '../ListAction';
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
- return (_jsxs("div", { ref: ref, className: "flex flex-row-reverse items-center gap-4", children: [_jsxs("button", { className: "h-4 rotate-90 text-xs leading-[1em] tracking-tighter text-slate-500", onClick: toggleIsOpen, type: "button", children: ["\u2022\u2022\u2022", _jsxs("span", { className: "sr-only", children: [isOpen ? 'Close' : 'Open', " Actions"] })] }), isOpen ? (_jsx("ul", { className: "show relative flex origin-right gap-4 bg-white before:absolute before:left-[-999em] before:right-full before:h-full before:bg-white/80", children: actions.map(({ label, onClick, variant }) => (_jsx("li", { children: _jsx(ListAction, { id: id, label: label, onClick: onClick, variant: variant }) }, label))) })) : null] }));
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?: ({ id }: {
4
- id: number | string;
5
- }) => React.ReactElement;
4
+ actions?: ListAction[];
6
5
  description?: React.ReactNode | null;
7
6
  href?: string;
8
- id: number | string;
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
- subtitle?: React.ReactNode | null;
13
- title?: string;
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: Actions, // eslint-disable-line @typescript-eslint/naming-convention
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 { Link } from '@sqrzro/components';
3
- export function renderMeta(meta) {
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-600", children: meta.map((item, index) => (_jsx("li", { children: item }, index))) }));
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: Actions, // eslint-disable-line @typescript-eslint/naming-convention
13
- description, href, id, meta, title, }) {
14
- return (_jsx("li", { children: _jsxs("article", { className: "bg-panel overflow-hidden rounded p-4 shadow-[0px_0px_0px_1px_rgba(9,9,11,0.07),0px_2px_2px_0px_rgba(9,9,11,0.05)]", children: [title ? (_jsx("h2", { className: "border-b border-slate-200 pb-3 text-lg font-semibold", children: href ? _jsx(Link, { href: href, children: title }) : title })) : null, description ? (_jsx("div", { className: "border-b border-slate-200 pb-3 ", children: description })) : null, _jsxs("div", { className: "mt-4 flex items-center justify-between", children: [meta ? renderMeta(meta) : null, Actions ? _jsx(Actions, { id: id }) : null] })] }) }));
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
+ /// <reference types="react" />
2
+ interface ListSkeletonProps {
3
+ }
4
+ declare function ListSkeleton({}: Readonly<ListSkeletonProps>): React.ReactElement;
5
+ export default ListSkeleton;
@@ -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, LogoutButton } from '@sqrzro/components';
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", { children: _jsx(LogoutButton, {}) })] }));
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;