@sqrzro/admin 2.1.0-bz.1 → 2.1.0-bz.11
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/LICENSE +1 -1
- package/README.md +8 -5
- package/dist/components/AppLayout/index.d.ts +4 -1
- package/dist/components/AppLayout/index.js +7 -4
- package/dist/components/AppNavigation/index.js +4 -3
- package/dist/components/Auth/index.d.ts +5 -0
- package/dist/components/Auth/index.js +12 -0
- package/dist/components/Badge/index.d.ts +8 -0
- package/dist/components/Badge/index.js +11 -0
- package/dist/components/Dashboard/index.js +1 -1
- package/dist/components/DateFilter/index.js +0 -1
- package/dist/components/FilterBar/index.d.ts +4 -3
- package/dist/components/FilterBar/index.js +12 -3
- package/dist/components/FilterBarItem/index.d.ts +2 -1
- package/dist/components/FilterBarItem/index.js +2 -2
- package/dist/components/GridList/index.d.ts +2 -2
- package/dist/components/GridList/index.js +1 -1
- package/dist/components/GridListItem/index.d.ts +1 -1
- package/dist/components/GridListItem/index.js +3 -2
- package/dist/components/InfoPanel/index.d.ts +8 -0
- package/dist/components/InfoPanel/index.js +10 -0
- package/dist/components/List/index.d.ts +12 -10
- package/dist/components/List/index.js +7 -5
- package/dist/components/ListActions/index.js +1 -1
- package/dist/components/ListItem/index.d.ts +9 -6
- package/dist/components/ListItem/index.js +8 -4
- package/dist/components/MeActions/index.js +3 -10
- package/dist/components/MePanel/index.d.ts +6 -1
- package/dist/components/MePanel/index.js +2 -5
- package/dist/components/Page/index.d.ts +1 -1
- package/dist/components/Page/index.js +2 -2
- package/dist/components/Panel/index.d.ts +1 -1
- package/dist/components/Panel/index.js +1 -1
- package/dist/components/RootLayout/index.d.ts +2 -1
- package/dist/components/RootLayout/index.js +12 -5
- package/dist/components/SettingsForm/index.d.ts +2 -0
- package/dist/components/SettingsForm/index.js +9 -0
- package/dist/components/SettingsPage/index.d.ts +2 -0
- package/dist/components/SettingsPage/index.js +7 -0
- package/dist/components/Table/index.d.ts +2 -2
- package/dist/components/TableClientComponent/index.d.ts +4 -4
- package/dist/components/TableClientComponent/index.js +17 -4
- package/dist/components/Tabs/index.js +20 -2
- package/dist/components/index.d.ts +12 -4
- package/dist/components/index.js +6 -2
- package/dist/services/ConfigService.d.ts +3 -1
- package/dist/services/ConfigService.js +5 -5
- package/dist/styles/config.js +94 -35
- package/dist/styles/postcss.d.ts +6 -0
- package/dist/styles/postcss.js +4 -0
- package/dist/styles/tailwind.d.ts +10 -0
- package/dist/styles/tailwind.js +72 -0
- package/package.json +37 -46
- package/postcss.js +3 -0
- package/tailwind.d.ts +1 -0
- package/tailwind.js +1 -0
- package/.babelrc.json +0 -16
- package/.eslintignore +0 -6
- package/.eslintrc +0 -5
- package/.sqrzrorc +0 -5
- package/.storybook/main.js +0 -11
- package/.storybook/preview-head.html +0 -3
- package/.storybook/preview.js +0 -1
- package/.turbo/turbo-build.log +0 -20
- package/.turbo/turbo-prettier.log +0 -43
- package/COMPONENTS.md +0 -45
- package/dist/components/LoginForm/index.d.ts +0 -6
- package/dist/components/LoginForm/index.js +0 -7
- package/dist/styles.css +0 -1723
- package/dist/utility/formatters.d.ts +0 -1
- package/dist/utility/formatters.js +0 -8
- package/next-env.d.ts +0 -5
- package/postcss.config.js +0 -3
- package/prettier.config.js +0 -1
- package/src/components/AppLayout/index.tsx +0 -32
- package/src/components/AppNavigation/index.tsx +0 -39
- package/src/components/BooleanFilter/index.tsx +0 -28
- package/src/components/Config/index.tsx +0 -17
- package/src/components/Dashboard/index.tsx +0 -15
- package/src/components/DateFilter/index.tsx +0 -49
- package/src/components/DropdownFilter/index.tsx +0 -19
- package/src/components/FilterBar/index.tsx +0 -35
- package/src/components/FilterBarItem/index.tsx +0 -126
- package/src/components/GridList/index.tsx +0 -11
- package/src/components/GridListItem/index.tsx +0 -31
- package/src/components/Icon/index.tsx +0 -33
- package/src/components/List/index.tsx +0 -70
- package/src/components/ListAction/index.tsx +0 -25
- package/src/components/ListActions/index.tsx +0 -43
- package/src/components/ListItem/index.tsx +0 -56
- package/src/components/LoginForm/index.tsx +0 -34
- package/src/components/MeActions/index.tsx +0 -21
- package/src/components/MePanel/index.tsx +0 -21
- package/src/components/Page/index.tsx +0 -60
- package/src/components/PageActions/index.tsx +0 -22
- package/src/components/Panel/Panel.spec.tsx +0 -10
- package/src/components/Panel/Panel.stories.tsx +0 -9
- package/src/components/Panel/index.tsx +0 -15
- package/src/components/RootLayout/index.tsx +0 -39
- package/src/components/Table/index.tsx +0 -34
- package/src/components/TableClientComponent/index.tsx +0 -112
- package/src/components/Tabs/index.tsx +0 -25
- package/src/components/index.ts +0 -68
- package/src/icons/ErrorIcon/index.tsx +0 -19
- package/src/icons/InfoIcon/index.tsx +0 -19
- package/src/icons/SuccessIcon/index.tsx +0 -19
- package/src/icons/WarningIcon/index.tsx +0 -19
- package/src/index.ts +0 -3
- package/src/interfaces.ts +0 -3
- package/src/react.d.ts +0 -12
- package/src/services/ConfigService.ts +0 -24
- package/src/styles/config.ts +0 -95
- package/src/styles/tailwind.css +0 -47
- package/src/utility/formatters.ts +0 -9
- package/tailwind.config.js +0 -16
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -18
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function formatDate(value?: Date): string;
|
package/next-env.d.ts
DELETED
package/postcss.config.js
DELETED
package/prettier.config.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('@sqrzro/prettier-config');
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Fragment } from 'react';
|
|
2
|
-
import { Container, Link } from '@sqrzro/components';
|
|
3
|
-
|
|
4
|
-
import { getConfig } from '../../services/ConfigService';
|
|
5
|
-
|
|
6
|
-
import AppNavigation from '../AppNavigation';
|
|
7
|
-
import MePanel from '../MePanel';
|
|
8
|
-
|
|
9
|
-
export interface AppLayoutProps {
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function AppLayout({ children }: Readonly<AppLayoutProps>): React.ReactElement {
|
|
14
|
-
return (
|
|
15
|
-
<Fragment>
|
|
16
|
-
<header className="bg-gray-800">
|
|
17
|
-
<Container isFullWidth>
|
|
18
|
-
<div className="flex h-16 items-center border-b border-gray-700">
|
|
19
|
-
<Link className="h-9 w-9 rounded-full border-4 border-primary" href="/">
|
|
20
|
-
<span className="sr-only">{getConfig().app.name}</span>
|
|
21
|
-
</Link>
|
|
22
|
-
<AppNavigation />
|
|
23
|
-
<MePanel />
|
|
24
|
-
</div>
|
|
25
|
-
</Container>
|
|
26
|
-
</header>
|
|
27
|
-
<main className="mb-10 block">{children}</main>
|
|
28
|
-
</Fragment>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default AppLayout;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import clsx from 'clsx';
|
|
4
|
-
|
|
5
|
-
import { Link } from '@sqrzro/components';
|
|
6
|
-
import { useNavigation } from '@sqrzro/hooks';
|
|
7
|
-
import type { NavigationData } from '@sqrzro/hooks';
|
|
8
|
-
|
|
9
|
-
import { getConfig } from '../../services/ConfigService';
|
|
10
|
-
|
|
11
|
-
function getNavigation(): NavigationData {
|
|
12
|
-
const { navigation } = getConfig();
|
|
13
|
-
return Object.entries(navigation).map(([href, label]) => ({ href, label }));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function AppNavigation(): React.ReactElement {
|
|
17
|
-
const navigation = useNavigation(getNavigation());
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<ul className="flex h-full items-center gap-2 pl-6">
|
|
21
|
-
{navigation.map(({ href, isActive, label }) => (
|
|
22
|
-
<li key={href} className="h-full">
|
|
23
|
-
<Link
|
|
24
|
-
className="block flex h-full items-center font-semibold text-white"
|
|
25
|
-
href={href}
|
|
26
|
-
>
|
|
27
|
-
<span
|
|
28
|
-
className={clsx('rounded px-3 py-2', isActive ? 'bg-gray-700' : null)}
|
|
29
|
-
>
|
|
30
|
-
{label}
|
|
31
|
-
</span>
|
|
32
|
-
</Link>
|
|
33
|
-
</li>
|
|
34
|
-
))}
|
|
35
|
-
</ul>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default AppNavigation;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Dropdown } from '@sqrzro/components';
|
|
2
|
-
|
|
3
|
-
import type { FilterComponentProps } from '../../interfaces';
|
|
4
|
-
|
|
5
|
-
const data = [
|
|
6
|
-
{
|
|
7
|
-
id: '0',
|
|
8
|
-
name: 'No',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
id: '1',
|
|
12
|
-
name: 'Yes',
|
|
13
|
-
},
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
function BooleanFilter({
|
|
17
|
-
name,
|
|
18
|
-
onChange,
|
|
19
|
-
value,
|
|
20
|
-
}: Readonly<FilterComponentProps>): React.ReactElement {
|
|
21
|
-
return <Dropdown data={data} name={name} onChange={onChange} value={value} isPanelOnly />;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function renderBooleanValue(value: string): string {
|
|
25
|
-
return data.find((item) => item.id === value)?.name || '';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default BooleanFilter;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { setConfig } from '../../services/ConfigService';
|
|
4
|
-
import type { Config as ConfigObject } from '../../services/ConfigService';
|
|
5
|
-
|
|
6
|
-
interface ConfigProps {
|
|
7
|
-
data?: ConfigObject;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function Config({ data }: Readonly<ConfigProps>): null {
|
|
11
|
-
if (data) {
|
|
12
|
-
setConfig(data);
|
|
13
|
-
}
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default Config;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface DashboardProps {
|
|
2
|
-
children?: React.ReactNode;
|
|
3
|
-
title: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function Dashboard({ children, title }: Readonly<DashboardProps>): React.ReactElement {
|
|
7
|
-
return (
|
|
8
|
-
<div>
|
|
9
|
-
<h1 className="mx-auto text-center text-4xl font-semibold">{title}</h1>
|
|
10
|
-
{children}
|
|
11
|
-
</div>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default Dashboard;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { CalendarInput } from '@sqrzro/components';
|
|
2
|
-
|
|
3
|
-
import type { FilterComponentProps } from '../../interfaces';
|
|
4
|
-
|
|
5
|
-
function DateFilter({ name, onChange, value }: Readonly<FilterComponentProps>): React.ReactElement {
|
|
6
|
-
return <CalendarInput name={name} onChange={onChange} value={value} isPanelOnly isRange />;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function transformDateValue(value?: string | null): string {
|
|
10
|
-
//return value?.split(',').map((item) => new Date(item)) || [];
|
|
11
|
-
return value || '';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const LENGTH_OF_YEAR = 4;
|
|
15
|
-
|
|
16
|
-
function renderDateString(value: string, noYear?: boolean): string {
|
|
17
|
-
const date = new Date(value);
|
|
18
|
-
|
|
19
|
-
if (Number.isNaN(date.getTime())) {
|
|
20
|
-
return '';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const day = date.getDate().toString().padStart(2, '0');
|
|
24
|
-
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
25
|
-
const year = date.getFullYear();
|
|
26
|
-
|
|
27
|
-
return `${day}/${month}${noYear ? '' : `/${year}`}`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function renderDateValue(value: string): string {
|
|
31
|
-
if (value.includes(',')) {
|
|
32
|
-
const values = value.split(',');
|
|
33
|
-
const years = values.map((item) => parseInt(item.substring(0, LENGTH_OF_YEAR), 10));
|
|
34
|
-
|
|
35
|
-
return value
|
|
36
|
-
.split(',')
|
|
37
|
-
.map((item) =>
|
|
38
|
-
renderDateString(
|
|
39
|
-
item,
|
|
40
|
-
years[0] === years[1] && years[0] === new Date().getFullYear()
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
.join(' - ');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return renderDateString(value);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default DateFilter;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Dropdown } from '@sqrzro/components';
|
|
2
|
-
import type { SimpleObject } from '@sqrzro/interfaces';
|
|
3
|
-
|
|
4
|
-
import type { FilterComponentProps } from '../../interfaces';
|
|
5
|
-
|
|
6
|
-
function DropdownFilter({
|
|
7
|
-
data,
|
|
8
|
-
name,
|
|
9
|
-
onChange,
|
|
10
|
-
value,
|
|
11
|
-
}: Readonly<FilterComponentProps>): React.ReactElement {
|
|
12
|
-
return <Dropdown data={data} name={name} onChange={onChange} value={value} isPanelOnly />;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function renderDropdownValue(value: string, data?: SimpleObject[]): string {
|
|
16
|
-
return data?.find((item) => item.id === value)?.name || '';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default DropdownFilter;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useFilters } from '@sqrzro/hooks';
|
|
4
|
-
|
|
5
|
-
import FilterBarItem from '../FilterBarItem';
|
|
6
|
-
import type { FilterObject } from '../FilterBarItem';
|
|
7
|
-
|
|
8
|
-
export type FilterMap = FilterObject[];
|
|
9
|
-
|
|
10
|
-
export interface FilterBarProps {
|
|
11
|
-
map: FilterMap;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function FilterBar({ map }: Readonly<FilterBarProps>): React.ReactElement {
|
|
15
|
-
const [filters, setFilter] = useFilters();
|
|
16
|
-
|
|
17
|
-
function handleChange(name: string, value: string): void {
|
|
18
|
-
setFilter(name, value);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<ul className="sticky top-0 flex gap-2">
|
|
23
|
-
{map.map((filter) => (
|
|
24
|
-
<FilterBarItem
|
|
25
|
-
key={filter.name}
|
|
26
|
-
onChange={handleChange}
|
|
27
|
-
value={filters.get(filter.name) || ''}
|
|
28
|
-
{...filter}
|
|
29
|
-
/>
|
|
30
|
-
))}
|
|
31
|
-
</ul>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default FilterBar;
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useRef } from 'react';
|
|
4
|
-
|
|
5
|
-
import type { InputEvent, SimpleObject } from '@sqrzro/interfaces';
|
|
6
|
-
import clsx from 'clsx';
|
|
7
|
-
|
|
8
|
-
import type { FilterComponentProps } from '../../interfaces';
|
|
9
|
-
|
|
10
|
-
import BooleanFilter, { renderBooleanValue } from '../BooleanFilter';
|
|
11
|
-
import DateFilter, { renderDateValue, transformDateValue } from '../DateFilter';
|
|
12
|
-
import DropdownFilter, { renderDropdownValue } from '../DropdownFilter';
|
|
13
|
-
|
|
14
|
-
export interface FilterObject {
|
|
15
|
-
data?: SimpleObject[];
|
|
16
|
-
label: string;
|
|
17
|
-
name: string;
|
|
18
|
-
type: 'boolean' | 'date' | 'dropdown';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface FilterBarItemProps extends FilterObject {
|
|
22
|
-
onChange: (name: string, value?: string) => void;
|
|
23
|
-
value?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface FilterMapItem {
|
|
27
|
-
component: React.FunctionComponent<FilterComponentProps>;
|
|
28
|
-
renderValue: (value: string, data?: SimpleObject[]) => string;
|
|
29
|
-
size?: string;
|
|
30
|
-
transformValue?: (value?: string) => string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const map: Record<FilterObject['type'], FilterMapItem> = {
|
|
34
|
-
boolean: { component: BooleanFilter, renderValue: renderBooleanValue },
|
|
35
|
-
date: {
|
|
36
|
-
component: DateFilter,
|
|
37
|
-
renderValue: renderDateValue,
|
|
38
|
-
size: 'w-96',
|
|
39
|
-
transformValue: transformDateValue,
|
|
40
|
-
},
|
|
41
|
-
dropdown: { component: DropdownFilter, renderValue: renderDropdownValue },
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const DEFAULT_SIZE = 'w-56';
|
|
45
|
-
|
|
46
|
-
function FilterBarItem({
|
|
47
|
-
data,
|
|
48
|
-
label,
|
|
49
|
-
name,
|
|
50
|
-
onChange,
|
|
51
|
-
type,
|
|
52
|
-
value,
|
|
53
|
-
}: Readonly<FilterBarItemProps>): React.ReactElement {
|
|
54
|
-
const panel = useRef<HTMLDivElement>(null);
|
|
55
|
-
|
|
56
|
-
function handleChange(event: InputEvent<string>): void {
|
|
57
|
-
onChange(name, event.target.value);
|
|
58
|
-
panel.current?.hidePopover();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function handleClear(): void {
|
|
62
|
-
onChange(name, '');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const { component: FilterComponent, renderValue, size, transformValue } = map[type];
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<li
|
|
69
|
-
className={clsx(
|
|
70
|
-
'relative inline-flex gap-2 rounded-full border px-5 py-2 text-xs text-white',
|
|
71
|
-
value ? 'border-solid border-gray-400' : 'border-dashed border-gray-500',
|
|
72
|
-
value ? 'bg-gray-700 pr-10' : 'text-slate-500'
|
|
73
|
-
)}
|
|
74
|
-
>
|
|
75
|
-
<p className="">{label}</p>
|
|
76
|
-
{value ? (
|
|
77
|
-
<p className="border-l border-l-slate-300 pl-2 font-semibold">
|
|
78
|
-
{renderValue(value, data)}
|
|
79
|
-
</p>
|
|
80
|
-
) : null}
|
|
81
|
-
<button
|
|
82
|
-
className={clsx(
|
|
83
|
-
'absolute left-0 top-0 h-full select-none text-transparent',
|
|
84
|
-
value ? 'right-10' : 'right-0'
|
|
85
|
-
)}
|
|
86
|
-
id={`${name}-control`}
|
|
87
|
-
popovertarget={`${name}-target`}
|
|
88
|
-
type="button"
|
|
89
|
-
>
|
|
90
|
-
Edit
|
|
91
|
-
</button>
|
|
92
|
-
{value ? (
|
|
93
|
-
<button
|
|
94
|
-
className="absolute right-0 top-0 flex h-full w-10 select-none items-center justify-center"
|
|
95
|
-
onClick={handleClear}
|
|
96
|
-
type="button"
|
|
97
|
-
>
|
|
98
|
-
<span className="sr-only">Remove</span>
|
|
99
|
-
<span className="flex h-4 w-4 items-center justify-center rounded-full bg-white leading-none text-gray-800">
|
|
100
|
-
×
|
|
101
|
-
</span>
|
|
102
|
-
</button>
|
|
103
|
-
) : null}
|
|
104
|
-
<div
|
|
105
|
-
ref={panel}
|
|
106
|
-
anchor={`${name}-control`}
|
|
107
|
-
className={`showpanel -ml-px mt-2 ${
|
|
108
|
-
size || DEFAULT_SIZE
|
|
109
|
-
} left-[anchor(left)] top-[anchor(bottom)] origin-top-left rounded-md bg-white shadow-lg`}
|
|
110
|
-
id={`${name}-target`}
|
|
111
|
-
popover="auto"
|
|
112
|
-
>
|
|
113
|
-
<div className="py-1" role="none">
|
|
114
|
-
<FilterComponent
|
|
115
|
-
data={data}
|
|
116
|
-
name={name}
|
|
117
|
-
onChange={handleChange}
|
|
118
|
-
value={transformValue ? transformValue(value) : value}
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</li>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export default FilterBarItem;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import GridListItem from '../GridListItem';
|
|
2
|
-
import List from '../List';
|
|
3
|
-
import type { ListProps } from '../List';
|
|
4
|
-
|
|
5
|
-
export type GridListProps<T> = Omit<ListProps<T>, 'renderItem'>;
|
|
6
|
-
|
|
7
|
-
function GridList<T extends object>(props: ListProps<T>): React.ReactElement {
|
|
8
|
-
return <List {...props} className="grid grid-cols-3 gap-6" renderItem={GridListItem} />;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default GridList;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Link } from '@sqrzro/components';
|
|
2
|
-
|
|
3
|
-
import { renderMeta } from '../ListItem';
|
|
4
|
-
import type { ListObject } from '../ListItem';
|
|
5
|
-
|
|
6
|
-
function GridListItem({
|
|
7
|
-
actions: Actions,
|
|
8
|
-
href,
|
|
9
|
-
id,
|
|
10
|
-
image,
|
|
11
|
-
meta,
|
|
12
|
-
title,
|
|
13
|
-
}: Readonly<ListObject>): React.ReactElement {
|
|
14
|
-
return (
|
|
15
|
-
<li>
|
|
16
|
-
<article className="overflow-hidden rounded-md bg-white p-4 shadow">
|
|
17
|
-
{image ? <img alt="" className="mb-4 border border-gray-200" src={image} /> : null}
|
|
18
|
-
<h2 className="text-base font-semibold">
|
|
19
|
-
{href ? <Link href={href}>{title}</Link> : title}
|
|
20
|
-
</h2>
|
|
21
|
-
{meta || Actions ? <hr className="my-4" /> : null}
|
|
22
|
-
<div className="flex items-center justify-between">
|
|
23
|
-
{meta ? renderMeta(meta) : null}
|
|
24
|
-
{Actions ? <Actions id={id} /> : null}
|
|
25
|
-
</div>
|
|
26
|
-
</article>
|
|
27
|
-
</li>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export default GridListItem;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import ErrorIcon from '../../icons/ErrorIcon';
|
|
2
|
-
import InfoIcon from '../../icons/InfoIcon';
|
|
3
|
-
import SuccessIcon from '../../icons/SuccessIcon';
|
|
4
|
-
import WarningIcon from '../../icons/WarningIcon';
|
|
5
|
-
|
|
6
|
-
export type IconType = 'error' | 'info' | 'success' | 'warning';
|
|
7
|
-
|
|
8
|
-
const icons: Record<IconType, () => React.ReactElement> = {
|
|
9
|
-
error: ErrorIcon,
|
|
10
|
-
info: InfoIcon,
|
|
11
|
-
success: SuccessIcon,
|
|
12
|
-
warning: WarningIcon,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export interface IconProps {
|
|
16
|
-
type?: keyof typeof icons;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function Icon({ type }: Readonly<IconProps>): React.ReactElement | null {
|
|
20
|
-
if (!type) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const IconComponent = icons[type];
|
|
25
|
-
|
|
26
|
-
if (!IconComponent) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return <IconComponent />;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export default Icon;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { Fragment } from 'react';
|
|
2
|
-
import clsx from 'clsx';
|
|
3
|
-
import { EmptyMessage } from '@sqrzro/components';
|
|
4
|
-
import type { ErrorablePromise } from '@sqrzro/server';
|
|
5
|
-
|
|
6
|
-
import FilterBar from '../FilterBar';
|
|
7
|
-
import type { FilterMap } from '../FilterBar';
|
|
8
|
-
import ListItem from '../ListItem';
|
|
9
|
-
import type { ListObject } from '../ListItem';
|
|
10
|
-
|
|
11
|
-
export interface ListComponentProps {
|
|
12
|
-
params?: Record<string, string>;
|
|
13
|
-
searchParams?: Record<string, string>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ListProps<T> {
|
|
17
|
-
actions?: ({ id }: { readonly id: number }) => React.ReactElement;
|
|
18
|
-
className?: string;
|
|
19
|
-
filters?: FilterMap;
|
|
20
|
-
fn: (
|
|
21
|
-
params?: Record<string, string>,
|
|
22
|
-
searchParams?: Record<string, string>
|
|
23
|
-
) => ErrorablePromise<T[]>;
|
|
24
|
-
renderItem?: (props: ListObject) => React.ReactElement;
|
|
25
|
-
params?: Record<string, string>;
|
|
26
|
-
searchParams?: Record<string, string>;
|
|
27
|
-
transformer?: (item: T) => ListObject;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function defaultTransformer(): ListObject {
|
|
31
|
-
return {
|
|
32
|
-
id: 0,
|
|
33
|
-
title: '',
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function List<T extends object>({
|
|
38
|
-
actions,
|
|
39
|
-
className,
|
|
40
|
-
filters,
|
|
41
|
-
fn,
|
|
42
|
-
params,
|
|
43
|
-
renderItem = ListItem,
|
|
44
|
-
searchParams,
|
|
45
|
-
transformer,
|
|
46
|
-
}: Readonly<ListProps<T>>): Promise<React.ReactElement> {
|
|
47
|
-
const [response, error] = await fn(params, searchParams);
|
|
48
|
-
|
|
49
|
-
if (error) {
|
|
50
|
-
return <div>ERRORROROR</div>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const data = response.map(transformer || defaultTransformer);
|
|
54
|
-
const hasFilters = Boolean(Object.keys(searchParams || []).length);
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Fragment>
|
|
58
|
-
{filters && (data.length || hasFilters) ? <FilterBar map={filters} /> : null}
|
|
59
|
-
{data.length ? (
|
|
60
|
-
<ul className={clsx(className)}>
|
|
61
|
-
{data.map((item) => renderItem({ actions, ...item }))}
|
|
62
|
-
</ul>
|
|
63
|
-
) : (
|
|
64
|
-
<EmptyMessage />
|
|
65
|
-
)}
|
|
66
|
-
</Fragment>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export default List;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ConfirmableButton } from '@sqrzro/components';
|
|
2
|
-
import type { ConfirmableAction } from '@sqrzro/interfaces';
|
|
3
|
-
|
|
4
|
-
interface ListActionProps extends ConfirmableAction<number> {
|
|
5
|
-
id: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function ListAction({
|
|
9
|
-
id,
|
|
10
|
-
onClick,
|
|
11
|
-
label,
|
|
12
|
-
variant,
|
|
13
|
-
}: Readonly<ListActionProps>): React.ReactElement {
|
|
14
|
-
function handleClick(): void {
|
|
15
|
-
onClick(id);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<ConfirmableButton onClick={handleClick} variant={variant} isText>
|
|
20
|
-
{label}
|
|
21
|
-
</ConfirmableButton>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default ListAction;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useClickOutside } from '@sqrzro/hooks';
|
|
4
|
-
import type { ConfirmableAction } from '@sqrzro/interfaces';
|
|
5
|
-
|
|
6
|
-
import ListAction from '../ListAction';
|
|
7
|
-
|
|
8
|
-
export interface ListActionsProps {
|
|
9
|
-
actions: ConfirmableAction<number>[];
|
|
10
|
-
id: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function ListActions({ actions, id }: Readonly<ListActionsProps>): React.ReactElement {
|
|
14
|
-
const [isOpen, setIsOpen, ref] = useClickOutside();
|
|
15
|
-
|
|
16
|
-
function toggleIsOpen(): void {
|
|
17
|
-
setIsOpen(!isOpen);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div ref={ref} className="flex flex-row-reverse items-center gap-4">
|
|
22
|
-
<button
|
|
23
|
-
className="h-4 rotate-90 text-xs leading-[1em] tracking-tighter text-slate-500"
|
|
24
|
-
onClick={toggleIsOpen}
|
|
25
|
-
type="button"
|
|
26
|
-
>
|
|
27
|
-
•••
|
|
28
|
-
<span className="sr-only">{isOpen ? 'Close' : 'Open'} Actions</span>
|
|
29
|
-
</button>
|
|
30
|
-
{isOpen ? (
|
|
31
|
-
<ul className="showpanel relative flex origin-right gap-4 bg-white before:absolute before:left-[-999em] before:right-full before:h-full before:bg-white/80">
|
|
32
|
-
{actions.map(({ label, onClick, variant }) => (
|
|
33
|
-
<li key={label}>
|
|
34
|
-
<ListAction id={id} label={label} onClick={onClick} variant={variant} />
|
|
35
|
-
</li>
|
|
36
|
-
))}
|
|
37
|
-
</ul>
|
|
38
|
-
) : null}
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default ListActions;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Link } from '@sqrzro/components';
|
|
2
|
-
import { Fragment } from 'react';
|
|
3
|
-
|
|
4
|
-
export interface ListObject {
|
|
5
|
-
actions?: ({ id }: { id: number }) => React.ReactElement;
|
|
6
|
-
href?: string;
|
|
7
|
-
id: number;
|
|
8
|
-
image?: string;
|
|
9
|
-
meta?: string[];
|
|
10
|
-
title: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function renderMeta(meta: Record<string, string> | string[]): React.ReactElement {
|
|
14
|
-
if (Array.isArray(meta)) {
|
|
15
|
-
return (
|
|
16
|
-
<ul className="flex gap-4 text-xs">
|
|
17
|
-
{meta.map((item) => (
|
|
18
|
-
<li key={item}>{item}</li>
|
|
19
|
-
))}
|
|
20
|
-
</ul>
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<dl className="w-full text-xs">
|
|
26
|
-
{Object.entries(meta).map(([key, value]) => (
|
|
27
|
-
<div key={key} className="flex justify-between p-2 odd:bg-gray-100">
|
|
28
|
-
<dt>{key}</dt>
|
|
29
|
-
<dd>{value}</dd>
|
|
30
|
-
</div>
|
|
31
|
-
))}
|
|
32
|
-
</dl>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function ListItem({
|
|
37
|
-
actions: Actions,
|
|
38
|
-
href,
|
|
39
|
-
id,
|
|
40
|
-
meta,
|
|
41
|
-
title,
|
|
42
|
-
}: Readonly<ListObject>): React.ReactElement {
|
|
43
|
-
return (
|
|
44
|
-
<li className="mb-4">
|
|
45
|
-
<article className="overflow-hidden rounded-md bg-white p-4 shadow">
|
|
46
|
-
<h2 className="font-semibold">{href ? <Link href={href}>{title}</Link> : title}</h2>
|
|
47
|
-
<div className="mt-2 flex items-center justify-between">
|
|
48
|
-
{meta ? renderMeta(meta) : null}
|
|
49
|
-
{Actions ? <Actions id={id} /> : null}
|
|
50
|
-
</div>
|
|
51
|
-
</article>
|
|
52
|
-
</li>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default ListItem;
|