@sqrzro/admin 2.1.0-bz.9 → 2.1.0-r19.3
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/assets/images/check-white.svg +3 -0
- package/assets/images/chevron.svg +3 -0
- package/assets/images/close.svg +3 -0
- package/assets/images/danger.svg +3 -0
- package/assets/images/eye.svg +12 -0
- package/assets/images/info.svg +3 -0
- package/assets/images/success.svg +3 -0
- package/assets/images/warning.svg +3 -0
- package/dist/components/AppLayout/index.d.ts +2 -2
- package/dist/components/AppLayout/index.js +7 -5
- package/dist/components/AppNavigation/index.d.ts +4 -2
- package/dist/components/AppNavigation/index.js +4 -13
- package/dist/components/AppNavigationComponent/index.d.ts +11 -0
- package/dist/components/AppNavigationComponent/index.js +19 -0
- package/dist/components/AppNavigationSub/index.d.ts +11 -0
- package/dist/components/AppNavigationSub/index.js +12 -0
- package/dist/components/Auth/index.d.ts +10 -0
- package/dist/components/Auth/index.js +10 -0
- package/dist/components/Badge/index.d.ts +1 -2
- package/dist/components/Badge/index.js +16 -7
- package/dist/components/BooleanFilter/index.d.ts +0 -1
- package/dist/components/Dashboard/index.d.ts +0 -1
- package/dist/components/Dashboard/index.js +1 -1
- package/dist/components/DataTable/index.d.ts +5 -0
- package/dist/components/DataTable/index.js +5 -0
- package/dist/components/DateFilter/index.d.ts +0 -1
- package/dist/components/DateFilter/index.js +24 -1
- package/dist/components/DropdownFilter/index.d.ts +0 -1
- package/dist/components/FilterBar/index.d.ts +4 -3
- package/dist/components/FilterBar/index.js +30 -3
- package/dist/components/FilterBarClearButton/index.d.ts +5 -0
- package/dist/components/FilterBarClearButton/index.js +5 -0
- package/dist/components/FilterBarItem/index.d.ts +4 -3
- package/dist/components/FilterBarItem/index.js +6 -5
- package/dist/components/GridList/index.d.ts +2 -3
- package/dist/components/GridListItem/index.d.ts +1 -2
- package/dist/components/GridListItem/index.js +3 -2
- package/dist/components/InfoPanel/index.d.ts +2 -2
- package/dist/components/InfoPanel/index.js +18 -6
- package/dist/components/List/index.d.ts +4 -20
- package/dist/components/List/index.js +13 -17
- package/dist/components/ListActions/index.d.ts +6 -4
- package/dist/components/ListActions/index.js +16 -3
- package/dist/components/ListClientComponent/index.d.ts +13 -0
- package/dist/components/ListClientComponent/index.js +14 -0
- package/dist/components/ListComponent/index.d.ts +13 -0
- package/dist/components/ListComponent/index.js +21 -0
- package/dist/components/ListItem/index.d.ts +19 -10
- package/dist/components/ListItem/index.js +18 -5
- package/dist/components/ListSkeleton/index.d.ts +4 -0
- package/dist/components/ListSkeleton/index.js +5 -0
- package/dist/components/MeActions/index.d.ts +4 -2
- package/dist/components/MeActions/index.js +4 -11
- package/dist/components/MePanel/index.d.ts +3 -2
- package/dist/components/MePanel/index.js +3 -2
- package/dist/components/Menu/index.d.ts +7 -0
- package/dist/components/Menu/index.js +7 -0
- package/dist/components/MenuItem/index.d.ts +4 -0
- package/dist/components/MenuItem/index.js +20 -0
- package/dist/components/Page/index.d.ts +4 -3
- package/dist/components/Page/index.js +6 -3
- package/dist/components/PageActions/index.d.ts +0 -1
- package/dist/components/PaginatedList/index.d.ts +8 -0
- package/dist/components/PaginatedList/index.js +18 -0
- package/dist/components/PaginatedListComponent/index.d.ts +13 -0
- package/dist/components/PaginatedListComponent/index.js +20 -0
- package/dist/components/Pagination/index.d.ts +7 -0
- package/dist/components/Pagination/index.js +40 -0
- package/dist/components/PaginationItem/index.d.ts +9 -0
- package/dist/components/PaginationItem/index.js +16 -0
- package/dist/components/Panel/index.d.ts +3 -2
- package/dist/components/Panel/index.js +13 -2
- package/dist/components/RootLayout/index.d.ts +2 -2
- package/dist/components/RootLayout/index.js +6 -12
- package/dist/components/SettingsForm/index.d.ts +6 -0
- package/dist/components/SettingsForm/index.js +14 -0
- package/dist/components/SettingsPage/index.d.ts +2 -0
- package/dist/components/SettingsPage/index.js +9 -0
- package/dist/components/Table/index.d.ts +6 -7
- package/dist/components/Table/index.js +16 -14
- package/dist/components/TableClientComponent/index.d.ts +0 -1
- package/dist/components/TableClientComponent/index.js +2 -11
- package/dist/components/TableComponent/index.d.ts +11 -0
- package/dist/components/TableComponent/index.js +17 -0
- package/dist/components/Tabs/index.d.ts +2 -7
- package/dist/components/Tabs/index.js +5 -22
- package/dist/components/TabsComponent/index.d.ts +7 -0
- package/dist/components/TabsComponent/index.js +9 -0
- package/dist/components/index.d.ts +12 -5
- package/dist/components/index.js +5 -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 +8 -3
- package/dist/services/ConfigService.js +6 -6
- package/dist/services/PermissionService.d.ts +3 -0
- package/dist/services/PermissionService.js +30 -0
- package/dist/services/SettingsService.d.ts +8 -0
- package/dist/services/SettingsService.js +9 -0
- package/dist/styles/config.js +119 -47
- package/dist/styles/tailwind.d.ts +9 -1
- package/dist/styles/tailwind.js +16 -3
- package/package.json +46 -45
- package/dist/components/Icon/index.d.ts +0 -8
- package/dist/components/Icon/index.js +0 -22
- package/dist/components/LoginForm/index.d.ts +0 -8
- package/dist/components/LoginForm/index.js +0 -7
- 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/AuthService.d.ts +0 -1
- package/dist/services/AuthService.js +0 -5
- package/dist/utility/formatters.d.ts +0 -1
- package/dist/utility/formatters.js +0 -8
- /package/{postcss.js → postcss.cjs} +0 -0
- /package/{tailwind.js → tailwind.cjs} +0 -0
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment } from 'react';
|
|
3
|
-
import
|
|
4
|
-
import { EmptyMessage } from '@sqrzro/components';
|
|
2
|
+
import { Fragment, Suspense } from 'react';
|
|
3
|
+
import { getLayout } from '../../services/SettingsService';
|
|
5
4
|
import FilterBar from '../FilterBar';
|
|
6
|
-
import
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return _jsx("div", { children: "Error" });
|
|
5
|
+
import ListComponent from '../ListComponent';
|
|
6
|
+
async function List({ filters, hasSearch, ...props }) {
|
|
7
|
+
const layout = await getLayout();
|
|
8
|
+
let searchParams; // eslint-disable-line @typescript-eslint/init-declarations
|
|
9
|
+
try {
|
|
10
|
+
const { headers } = await import('next/headers');
|
|
11
|
+
searchParams = new URLSearchParams((await headers()).get('x-search-params') || '');
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
searchParams = new URLSearchParams();
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
const hasFilters = Boolean(Object.keys(searchParams || []).length);
|
|
20
|
-
return (_jsxs(Fragment, { children: [filters && (data.length || hasFilters) ? _jsx(FilterBar, { map: filters }) : null, data.length ? (_jsx("ul", { className: clsx('relative', columns ? 'grid grid-cols-3 gap-6' : 'flex flex-col gap-4'), children: data.map((item) => renderItem({ actions, ...item })) })) : (_jsx(EmptyMessage, { ...emptyMessageProps }))] }));
|
|
16
|
+
return (_jsxs(Fragment, { children: [filters || hasSearch ? (_jsx(FilterBar, { hasSearch: hasSearch, layout: layout, map: filters })) : null, _jsx(Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: _jsx(ListComponent, { ...props, searchParams: searchParams }) })] }));
|
|
21
17
|
}
|
|
22
18
|
export default List;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { ConfirmableAction, LinkableAction } from '@sqrzro/interfaces';
|
|
2
|
+
export interface ListAction extends Omit<ConfirmableAction & LinkableAction, 'onClick'> {
|
|
3
|
+
onClick?: (id: string) => void;
|
|
4
|
+
}
|
|
3
5
|
export interface ListActionsProps {
|
|
4
|
-
actions:
|
|
5
|
-
id:
|
|
6
|
+
actions?: (ListAction | null)[] | ((id: string) => (ListAction | null)[]);
|
|
7
|
+
id: string;
|
|
6
8
|
}
|
|
7
9
|
declare function ListActions({ actions, id }: Readonly<ListActionsProps>): React.ReactElement;
|
|
8
10
|
export default ListActions;
|
|
@@ -1,12 +1,25 @@
|
|
|
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
|
+
function parseHref(href, id) {
|
|
6
|
+
return href.replace(':id', id);
|
|
7
|
+
}
|
|
5
8
|
function ListActions({ actions, id }) {
|
|
6
9
|
const [isOpen, setIsOpen, ref] = useClickOutside();
|
|
10
|
+
function handleClick(item) {
|
|
11
|
+
item.onClick?.(id);
|
|
12
|
+
}
|
|
7
13
|
function toggleIsOpen() {
|
|
8
14
|
setIsOpen(!isOpen);
|
|
9
15
|
}
|
|
10
|
-
|
|
16
|
+
const transformedActions = (typeof actions === 'function' ? actions(id) : actions)
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((item) => ({
|
|
19
|
+
...item,
|
|
20
|
+
href: item.href ? parseHref(item.href, id) : undefined, // eslint-disable-line no-undefined
|
|
21
|
+
onClick: () => handleClick(item),
|
|
22
|
+
}));
|
|
23
|
+
return (_jsxs("div", { ref: ref, className: "relative col-start-4 flex h-5 justify-end self-center pl-4", 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
24
|
}
|
|
12
25
|
export default ListActions;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EmptyMessageProps } from '@sqrzro/components';
|
|
2
|
+
import type { ListObject } from '../ListItem';
|
|
3
|
+
export interface ListClientComponentProps<Data = Record<string, unknown>> {
|
|
4
|
+
actions?: ListObject<Data>['actions'];
|
|
5
|
+
columns?: number;
|
|
6
|
+
data: ListObject<Data>[];
|
|
7
|
+
emptyMessageProps?: EmptyMessageProps;
|
|
8
|
+
hasFilters?: boolean;
|
|
9
|
+
isMinimal?: boolean;
|
|
10
|
+
renderItem?: (props: ListObject<Data>) => React.ReactElement;
|
|
11
|
+
}
|
|
12
|
+
declare function ListClientComponent<Data = Record<string, unknown>>({ actions, columns, data, emptyMessageProps, hasFilters, isMinimal, renderItem, }: Readonly<ListClientComponentProps<Data>>): React.ReactElement;
|
|
13
|
+
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,13 @@
|
|
|
1
|
+
import type { Errorable } from '@sqrzro/interfaces';
|
|
2
|
+
import type { ListObject } from '../ListItem';
|
|
3
|
+
import type { ListClientComponentProps } from '../ListClientComponent';
|
|
4
|
+
export interface ListComponentProps<Item, Params> extends Omit<ListClientComponentProps, 'data' | 'hasFilters'> {
|
|
5
|
+
fn: (params?: Params, searchParams?: URLSearchParams) => Promise<Errorable<Item[]>>;
|
|
6
|
+
hasFilters?: boolean;
|
|
7
|
+
hasSearch?: boolean;
|
|
8
|
+
params?: Params;
|
|
9
|
+
searchParams?: URLSearchParams;
|
|
10
|
+
transformer?: (item: Item) => ListObject;
|
|
11
|
+
}
|
|
12
|
+
declare function ListComponent<Item extends object, Params>({ fn, hasFilters, params, searchParams, transformer, ...clientProps }: Readonly<ListComponentProps<Item, Params>>): Promise<React.ReactElement>;
|
|
13
|
+
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,15 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
export interface ListObject {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { StyleVariant } from '@sqrzro/interfaces';
|
|
2
|
+
export interface ListObject<Data = Record<string, unknown>> {
|
|
3
|
+
$data?: Data;
|
|
4
|
+
actions?: (data: Data) => React.ReactNode;
|
|
5
|
+
description?: React.ReactNode | null;
|
|
6
6
|
href?: string;
|
|
7
|
-
id:
|
|
7
|
+
id: string;
|
|
8
|
+
isMinimal?: boolean;
|
|
8
9
|
image?: string | null;
|
|
10
|
+
imageHref?: string | null;
|
|
9
11
|
meta?: React.ReactNode[] | Record<string, React.ReactNode>;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
permission?: string;
|
|
13
|
+
secondary?: {
|
|
14
|
+
description?: string;
|
|
15
|
+
meta?: React.ReactNode[];
|
|
16
|
+
title?: string;
|
|
17
|
+
};
|
|
18
|
+
tertiary?: React.ReactNode;
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
variant?: StyleVariant;
|
|
12
21
|
}
|
|
13
|
-
export declare function renderMeta(meta: ListObject['meta']): React.ReactElement | null;
|
|
14
|
-
declare function ListItem({ actions
|
|
22
|
+
export declare function renderMeta(id: string, meta: ListObject['meta']): React.ReactElement | null;
|
|
23
|
+
declare function ListItem<Data = Record<string, unknown>>({ $data, actions, description, href, id, isMinimal, meta, secondary, tertiary, title, variant, }: Readonly<ListObject<Data>>): React.ReactElement;
|
|
15
24
|
export default ListItem;
|
|
@@ -1,15 +1,28 @@
|
|
|
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
|
+
const variantMap = {
|
|
6
|
+
danger: 'text-red-700',
|
|
7
|
+
};
|
|
8
|
+
function getKey(item) {
|
|
9
|
+
if (isValidElement(item)) {
|
|
10
|
+
return item.key;
|
|
11
|
+
}
|
|
12
|
+
return String(item);
|
|
13
|
+
}
|
|
14
|
+
export function renderMeta(id, meta) {
|
|
4
15
|
if (!meta) {
|
|
5
16
|
return null;
|
|
6
17
|
}
|
|
7
18
|
if (Array.isArray(meta)) {
|
|
8
|
-
return (_jsx("ul", { className: "flex items-center gap-4 text-xs text-slate-
|
|
19
|
+
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
20
|
}
|
|
10
21
|
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
22
|
}
|
|
12
|
-
function ListItem({ actions
|
|
13
|
-
return (_jsx("li", { children:
|
|
23
|
+
function ListItem({ $data, actions, description, href, id, isMinimal, meta, secondary, tertiary, title, variant, }) {
|
|
24
|
+
return (_jsx("li", { children: _jsx("article", { className: tw('', isMinimal
|
|
25
|
+
? 'border-b border-slate-200 pb-4'
|
|
26
|
+
: '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)]', variant ? variantMap[variant] : ''), "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: tw('text-base font-semibold', variant === 'danger' ? 'line-through' : ''), "data-testid": `list-item-title--${id}`, children: href ? _jsx(Link, { href: href, children: title }) : title })) : null, description, meta ? (_jsx("div", { className: tw('mt-1', isMinimal ? '' : '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: tw('text-base font-semibold', variant === 'danger' ? 'line-through' : ''), children: secondary.title })) : null, secondary.description ? (_jsx("div", { className: "mt-1 text-xs text-slate-500", children: secondary.description })) : null, secondary.meta ? (_jsx("ul", { className: "mt-1 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, typeof actions === 'function' ? actions($data) : null] }) }) }));
|
|
14
27
|
}
|
|
15
28
|
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,3 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface MeActionsProps {
|
|
2
|
+
logoutButton?: React.ReactNode;
|
|
3
|
+
}
|
|
4
|
+
declare function MeActions({ logoutButton }: Readonly<MeActionsProps>): React.ReactElement;
|
|
3
5
|
export default MeActions;
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function MeActions() {
|
|
6
|
-
const router = useRouter();
|
|
7
|
-
async function handleLogout() {
|
|
8
|
-
await logout();
|
|
9
|
-
router.refresh();
|
|
10
|
-
}
|
|
11
|
-
return (_jsx("button", { className: "text-xs text-slate-300", onClick: handleLogout, type: "button", children: "Log Out" }));
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Link } from '@sqrzro/components';
|
|
3
|
+
function MeActions({ 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", { children: logoutButton })] }));
|
|
12
5
|
}
|
|
13
6
|
export default MeActions;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
interface MePanelProps {
|
|
2
|
+
layout?: string;
|
|
3
|
+
logoutButton?: React.ReactNode;
|
|
3
4
|
user?: {
|
|
4
5
|
name: string;
|
|
5
6
|
} | null;
|
|
6
7
|
}
|
|
7
|
-
declare function MePanel({ user }: Readonly<MePanelProps>): React.ReactElement;
|
|
8
|
+
declare function MePanel({ layout, logoutButton, user }: Readonly<MePanelProps>): React.ReactElement;
|
|
8
9
|
export default MePanel;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { tw } from '@sqrzro/components';
|
|
2
3
|
import MeActions from '../MeActions';
|
|
3
|
-
function MePanel({ user }) {
|
|
4
|
-
return (_jsxs("div", { className:
|
|
4
|
+
function MePanel({ layout, logoutButton, user }) {
|
|
5
|
+
return (_jsxs("div", { className: tw('flex gap-3', layout === 'sidebar' ? 'w-full p-4' : 'ml-auto items-center'), children: [_jsxs("div", { className: tw('flex flex-col gap-0.5 text-white', layout === 'sidebar' ? '' : 'items-end'), children: [_jsx("strong", { children: user?.name }), _jsx(MeActions, { logoutButton: logoutButton })] }), layout === 'sidebar' ? null : (_jsx("div", { className: "h-9 w-9 flex-none rounded-full border-4 border-slate-500" }))] }));
|
|
5
6
|
}
|
|
6
7
|
export default MePanel;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConfirmableAction, LinkableAction } from '@sqrzro/interfaces';
|
|
2
|
+
interface MenuProps {
|
|
3
|
+
actions: (ConfirmableAction & LinkableAction)[];
|
|
4
|
+
align: 'left' | 'right';
|
|
5
|
+
}
|
|
6
|
+
declare function Menu({ actions, align }: Readonly<MenuProps>): React.ReactElement;
|
|
7
|
+
export default Menu;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { InputPanel } from '@sqrzro/components';
|
|
3
|
+
import MenuItem from '../MenuItem';
|
|
4
|
+
function Menu({ actions, align }) {
|
|
5
|
+
return (_jsx(InputPanel, { align: align, children: _jsx("ul", { className: "min-w-44 py-1", children: actions.map((item) => (_jsx("li", { children: _jsx(MenuItem, { ...item }) }, item.label))) }) }));
|
|
6
|
+
}
|
|
7
|
+
export default Menu;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ConfirmableAction, LinkableAction } from '@sqrzro/interfaces';
|
|
2
|
+
type MenuItemProps = ConfirmableAction & LinkableAction;
|
|
3
|
+
declare function MenuItem({ href, isConfirmable, label, onClick, variant, }: Readonly<MenuItemProps>): React.ReactElement;
|
|
4
|
+
export default MenuItem;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ConfirmableButton, Link, tw } from '@sqrzro/components';
|
|
3
|
+
const classMap = {
|
|
4
|
+
danger: 'text-red-700',
|
|
5
|
+
error: 'text-red-700',
|
|
6
|
+
info: 'text-sky-700',
|
|
7
|
+
warning: 'text-yellow-700',
|
|
8
|
+
success: 'text-green-700',
|
|
9
|
+
};
|
|
10
|
+
function MenuItem({ href, isConfirmable, label, onClick, variant, }) {
|
|
11
|
+
const classNames = tw('block w-full whitespace-nowrap px-3 py-1.5 text-left text-sm text-slate-700', variant ? classMap[variant] : null);
|
|
12
|
+
if (!href) {
|
|
13
|
+
if (isConfirmable) {
|
|
14
|
+
return (_jsx(ConfirmableButton, { classNames: { root: { default: classNames } }, onClick: onClick, type: "button", children: label }));
|
|
15
|
+
}
|
|
16
|
+
return (_jsx("button", { className: classNames, onClick: onClick, type: "button", children: label }));
|
|
17
|
+
}
|
|
18
|
+
return (_jsx(Link, { className: classNames, href: href, onClick: onClick, children: label }));
|
|
19
|
+
}
|
|
20
|
+
export default MenuItem;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import type { LinkableAction } from '@sqrzro/interfaces';
|
|
2
|
+
import type { NavigationAction } from '../../interfaces';
|
|
3
3
|
export interface PageProps {
|
|
4
4
|
actions?: LinkableAction[];
|
|
5
5
|
basePath?: string;
|
|
6
6
|
children: React.ReactNode;
|
|
7
|
+
icon?: string;
|
|
7
8
|
isFullWidth?: boolean;
|
|
8
|
-
tabs?:
|
|
9
|
+
tabs?: (NavigationAction | null)[];
|
|
9
10
|
title: string;
|
|
10
11
|
}
|
|
11
|
-
declare function Page({ basePath, children, isFullWidth, tabs, title, }: Readonly<PageProps>): React.ReactElement
|
|
12
|
+
declare function Page({ basePath, children, icon, isFullWidth, tabs, title, }: Readonly<PageProps>): Promise<React.ReactElement>;
|
|
12
13
|
export default Page;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
|
-
import { Container } from '@sqrzro/components';
|
|
3
|
+
import { Container, tw } from '@sqrzro/components';
|
|
4
|
+
import { filterNull } from '@sqrzro/utility';
|
|
5
|
+
import { getLayout } from '../../services/SettingsService';
|
|
4
6
|
import Tabs from '../Tabs';
|
|
5
|
-
function Page({ basePath, children, isFullWidth, tabs, title, }) {
|
|
6
|
-
|
|
7
|
+
async function Page({ basePath, children, icon, isFullWidth, tabs, title, }) {
|
|
8
|
+
const layout = await getLayout();
|
|
9
|
+
return (_jsxs(Fragment, { children: [_jsx("header", { className: tw(layout === 'sidebar' ? 'bg-slate-200' : 'bg-slate-800 pb-16 text-white'), children: _jsxs(Container, { isFullWidth: layout === 'sidebar' || isFullWidth, children: [_jsxs("div", { className: tw('flex items-center justify-between', layout === 'sidebar' ? 'py-8' : 'py-10'), children: [_jsxs("h1", { className: "flex min-h-10 items-center text-3xl font-semibold", children: [icon ? (_jsx("i", { className: tw('mr-3 h-10 w-10 rounded-full border-4 border-blue-300/50 bg-blue-300 bg-clip-padding', icon) })) : null, title] }), _jsx("div", { className: "flex gap-2", id: "page-actions" })] }), tabs ? (_jsx("div", { className: tw('', layout === 'sidebar' ? '' : '-mt-4 mb-8 border-b border-slate-700'), children: _jsx(Tabs, { basePath: basePath, data: filterNull(tabs) }) })) : null] }) }), _jsx(Container, { isFullWidth: layout === 'sidebar' || isFullWidth, children: _jsx("div", { className: tw('@container flex flex-col gap-8', layout === 'sidebar' ? 'mt-8' : '-mt-16'), children: children }) })] }));
|
|
7
10
|
}
|
|
8
11
|
export default Page;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FilterObject } from '../FilterBar';
|
|
2
|
+
import type { PaginatedListComponentProps } from '../PaginatedListComponent';
|
|
3
|
+
export interface PaginatedListProps<Item extends object, Params> extends Omit<PaginatedListComponentProps<Item, Params>, 'actions'> {
|
|
4
|
+
actions?: (data: Record<string, unknown>) => React.ReactNode;
|
|
5
|
+
filters?: FilterObject[];
|
|
6
|
+
}
|
|
7
|
+
declare function PaginatedList<Item extends object, Params>({ filters, hasSearch, ...props }: Readonly<PaginatedListProps<Item, Params>>): Promise<React.ReactElement>;
|
|
8
|
+
export default PaginatedList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, Suspense } from 'react';
|
|
3
|
+
import { getLayout } from '../../services/SettingsService';
|
|
4
|
+
import FilterBar from '../FilterBar';
|
|
5
|
+
import PaginatedListComponent from '../PaginatedListComponent';
|
|
6
|
+
async function PaginatedList({ filters, hasSearch, ...props }) {
|
|
7
|
+
const layout = await getLayout();
|
|
8
|
+
let searchParams; // eslint-disable-line @typescript-eslint/init-declarations
|
|
9
|
+
try {
|
|
10
|
+
const { headers } = await import('next/headers');
|
|
11
|
+
searchParams = new URLSearchParams((await headers()).get('x-search-params') || '');
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
searchParams = new URLSearchParams();
|
|
15
|
+
}
|
|
16
|
+
return (_jsxs(Fragment, { children: [filters || hasSearch ? (_jsx(FilterBar, { hasSearch: hasSearch, layout: layout, map: filters })) : null, _jsx(Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: _jsx(PaginatedListComponent, { ...props, searchParams: searchParams }) })] }));
|
|
17
|
+
}
|
|
18
|
+
export default PaginatedList;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Errorable, Paginated } from '@sqrzro/interfaces';
|
|
2
|
+
import type { ListObject } from '../ListItem';
|
|
3
|
+
import type { ListClientComponentProps } from '../ListClientComponent';
|
|
4
|
+
export interface PaginatedListComponentProps<Item, Params> extends Omit<ListClientComponentProps, 'data' | 'hasFilters'> {
|
|
5
|
+
fn: (params?: Params, searchParams?: URLSearchParams) => Promise<Errorable<Paginated<Item>>>;
|
|
6
|
+
hasFilters?: boolean;
|
|
7
|
+
hasSearch?: boolean;
|
|
8
|
+
params?: Params;
|
|
9
|
+
searchParams?: URLSearchParams;
|
|
10
|
+
transformer?: (item: Item) => ListObject;
|
|
11
|
+
}
|
|
12
|
+
declare function PaginatedListComponent<Item extends object, Params>({ fn, hasFilters, params, searchParams, transformer, ...clientProps }: Readonly<PaginatedListComponentProps<Item, Params>>): Promise<React.ReactElement>;
|
|
13
|
+
export default PaginatedListComponent;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { filterList } from '../../services/PermissionService';
|
|
4
|
+
import ListClientComponent from '../ListClientComponent';
|
|
5
|
+
import Pagination from '../Pagination';
|
|
6
|
+
function defaultTransformer() {
|
|
7
|
+
return {
|
|
8
|
+
id: '',
|
|
9
|
+
title: '',
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function PaginatedListComponent({ fn, hasFilters, params, searchParams, transformer, ...clientProps }) {
|
|
13
|
+
const [response, error] = await fn(params, searchParams);
|
|
14
|
+
if (error) {
|
|
15
|
+
return _jsx("div", { children: "Error" });
|
|
16
|
+
}
|
|
17
|
+
const data = await filterList(response.data.map(transformer || defaultTransformer));
|
|
18
|
+
return (_jsxs(Fragment, { children: [_jsx(ListClientComponent, { data: data, hasFilters: hasFilters, ...clientProps }), _jsx(Pagination, { ...response.meta })] }));
|
|
19
|
+
}
|
|
20
|
+
export default PaginatedListComponent;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Link, tw } from '@sqrzro/components';
|
|
4
|
+
import { usePathname, useSearchParams } from 'next/navigation';
|
|
5
|
+
import PaginationItem, { getLink } from '../PaginationItem';
|
|
6
|
+
function getOffsetPage(pageCount, page) {
|
|
7
|
+
if (page === 1) {
|
|
8
|
+
return 2;
|
|
9
|
+
}
|
|
10
|
+
if (page === pageCount) {
|
|
11
|
+
return pageCount - 1;
|
|
12
|
+
}
|
|
13
|
+
return page;
|
|
14
|
+
}
|
|
15
|
+
let spaceIndex = 0;
|
|
16
|
+
function getLinks(pageCount, page) {
|
|
17
|
+
const offsetPage = getOffsetPage(pageCount, page);
|
|
18
|
+
const set = new Set([1, offsetPage - 1, offsetPage, offsetPage + 1, pageCount]);
|
|
19
|
+
return Array.from(set)
|
|
20
|
+
.filter((item) => item > 0 && item <= pageCount)
|
|
21
|
+
.reduce((acc, cur, index, arr) => {
|
|
22
|
+
if (arr[index + 1] && arr[index + 1] !== cur + 1) {
|
|
23
|
+
spaceIndex -= 1;
|
|
24
|
+
return [...acc, cur, spaceIndex];
|
|
25
|
+
}
|
|
26
|
+
return [...acc, cur];
|
|
27
|
+
}, []);
|
|
28
|
+
}
|
|
29
|
+
function Pagination({ limit, page, total }) {
|
|
30
|
+
const pathname = usePathname();
|
|
31
|
+
const searchParams = useSearchParams();
|
|
32
|
+
const pageCount = Math.ceil(total / limit);
|
|
33
|
+
if (pageCount <= 1) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const links = getLinks(pageCount, page);
|
|
37
|
+
const linkClass = tw('inline-flex h-9 items-center justify-center gap-1 whitespace-nowrap rounded-md px-4 py-2 pl-2.5 text-sm font-medium hover:bg-slate-100');
|
|
38
|
+
return (_jsxs("nav", { className: "mx-auto flex w-full justify-between", children: [_jsx(Link, { className: tw(linkClass, page <= 1 ? 'pointer-events-none opacity-30' : ''), href: getLink(pathname, searchParams, page - 1), children: "\u2190 Previous" }), _jsx("ul", { className: "flex flex-row items-center gap-1", children: links.map((item) => (_jsx("li", { children: _jsx(PaginationItem, { currentPage: page || 1, number: item, pathname: pathname, searchParams: searchParams }) }, item))) }), _jsx(Link, { className: tw(linkClass, page >= pageCount ? 'pointer-events-none opacity-30' : ''), href: getLink(pathname, searchParams, page + 1), children: "Next \u2192" })] }));
|
|
39
|
+
}
|
|
40
|
+
export default Pagination;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function getLink(pathname: string, searchParams: URLSearchParams, page: number): string;
|
|
2
|
+
interface PaginationItemProps {
|
|
3
|
+
currentPage: number;
|
|
4
|
+
number: number;
|
|
5
|
+
pathname: string;
|
|
6
|
+
searchParams: URLSearchParams;
|
|
7
|
+
}
|
|
8
|
+
declare function PaginationItem({ currentPage, number, pathname, searchParams, }: Readonly<PaginationItemProps>): React.ReactElement;
|
|
9
|
+
export default PaginationItem;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Link, tw } from '@sqrzro/components';
|
|
3
|
+
export function getLink(pathname, searchParams, page) {
|
|
4
|
+
const newSearchParams = new URLSearchParams(searchParams.toString());
|
|
5
|
+
newSearchParams.set('page', page.toString());
|
|
6
|
+
return `${pathname}?${newSearchParams.toString()}`;
|
|
7
|
+
}
|
|
8
|
+
function PaginationItem({ currentPage, number, pathname, searchParams, }) {
|
|
9
|
+
if (number < 0) {
|
|
10
|
+
return (_jsx("span", { className: "flex h-9 w-9 items-center justify-center text-xs text-slate-400", children: "\u2022\u2022\u2022" }));
|
|
11
|
+
}
|
|
12
|
+
return (_jsx(Link, { className: tw('inline-flex h-9 w-9 items-center justify-center whitespace-nowrap rounded-md border text-sm font-medium hover:bg-slate-100 disabled:pointer-events-none disabled:opacity-30', number === currentPage
|
|
13
|
+
? 'border-slate-200 bg-white shadow-sm'
|
|
14
|
+
: 'border-transparent'), href: getLink(pathname, searchParams, number), children: number }));
|
|
15
|
+
}
|
|
16
|
+
export default PaginationItem;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { LinkableAction } from '@sqrzro/interfaces';
|
|
2
2
|
export interface PanelProps {
|
|
3
|
+
action?: LinkableAction | React.ReactElement;
|
|
3
4
|
children: React.ReactNode;
|
|
4
5
|
title?: string;
|
|
5
6
|
}
|
|
6
|
-
declare function Panel({ children, title }: Readonly<PanelProps>): React.ReactElement;
|
|
7
|
+
declare function Panel({ action, children, title }: Readonly<PanelProps>): React.ReactElement;
|
|
7
8
|
export default Panel;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { Button } from '@sqrzro/components';
|
|
3
|
+
import { isValidElement } from 'react';
|
|
4
|
+
function renderAction(action) {
|
|
5
|
+
if (typeof action === 'object' && 'href' in action && 'label' in action) {
|
|
6
|
+
return _jsx(Button, { href: action.href, children: action.label });
|
|
7
|
+
}
|
|
8
|
+
if (isValidElement(action)) {
|
|
9
|
+
return action;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function Panel({ action, children, title }) {
|
|
14
|
+
return (_jsxs("article", { className: "@container bg-panel relative rounded p-6 shadow-[0px_0px_0px_1px_rgba(9,9,11,0.07),0px_2px_2px_0px_rgba(9,9,11,0.05)]", children: [title ? (_jsxs("header", { className: "-mt-6 mb-4 flex h-16 items-center justify-between border-b border-slate-200", children: [_jsx("h3", { className: "text-lg font-semibold leading-none", children: title }), action ? renderAction(action) : null] })) : null, children] }));
|
|
4
15
|
}
|
|
5
16
|
export default Panel;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import type { Config as ConfigObject } from '../../services/ConfigService';
|
|
3
2
|
export interface RootLayoutProps {
|
|
4
3
|
children: React.ReactNode;
|
|
5
4
|
config?: ConfigObject;
|
|
5
|
+
font?: string;
|
|
6
6
|
logo?: () => React.ReactElement;
|
|
7
7
|
}
|
|
8
|
-
declare function RootLayout({ children, config, logo }: Readonly<RootLayoutProps>): React.ReactElement;
|
|
8
|
+
declare function RootLayout({ children, config, font, logo, }: Readonly<RootLayoutProps>): React.ReactElement;
|
|
9
9
|
export default RootLayout;
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import clsx from 'clsx';
|
|
5
|
-
import { ClassNames, Toaster } from '@sqrzro/components';
|
|
2
|
+
/* eslint-disable react/no-danger */
|
|
3
|
+
import { ClassNames, Toaster, tw } from '@sqrzro/components';
|
|
6
4
|
import { setConfig } from '../../services/ConfigService';
|
|
7
5
|
import classNames from '../../styles/config';
|
|
8
6
|
import Config from '../Config';
|
|
9
|
-
|
|
10
|
-
subsets: ['latin'],
|
|
11
|
-
variable: '--font-inter',
|
|
12
|
-
});
|
|
13
|
-
function RootLayout({ children, config, logo }) {
|
|
7
|
+
function RootLayout({ children, config, font, logo, }) {
|
|
14
8
|
if (config) {
|
|
15
9
|
setConfig(config, logo);
|
|
16
10
|
}
|
|
17
|
-
return (_jsxs("html", { lang: "en", children: [_jsx("head", {}), _jsxs("body", { className:
|
|
18
|
-
__html:
|
|
19
|
-
} }), _jsx(Config, { data: config }), _jsx(ClassNames, { data: classNames }), children, _jsx(
|
|
11
|
+
return (_jsxs("html", { lang: "en", children: [_jsx("head", { children: _jsx("link", { href: "/images/favicon.svg", rel: "icon" }) }), _jsxs("body", { className: tw(font, 'overflow-x-hidden overflow-y-scroll bg-slate-50 font-sans text-sm text-slate-800 has-[[data-modal][open]]:overflow-hidden'), children: [_jsx("script", { dangerouslySetInnerHTML: {
|
|
12
|
+
__html: "(function(d){var v=d.createElement('div'),t=d.createElement('style'),s=v.style;s.overflowY='scroll';s.width='50';s.height='50';d.body.append(v);t.innerHTML='body:has([data-modal][open]){padding-right:'+(v.offsetWidth-v.clientWidth)+'px}';d.body.append(t);v.remove()}(document))",
|
|
13
|
+
} }), _jsx(Config, { data: config }), _jsx(ClassNames, { data: classNames }), children, _jsx(Toaster, {})] })] }));
|
|
20
14
|
}
|
|
21
15
|
export default RootLayout;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SettingsFormFields } from '../../services/SettingsService';
|
|
2
|
+
interface SettingsFormProps {
|
|
3
|
+
defaults: Partial<SettingsFormFields>;
|
|
4
|
+
}
|
|
5
|
+
declare function SettingsForm({ defaults }: Readonly<SettingsFormProps>): React.ReactElement;
|
|
6
|
+
export default SettingsForm;
|