@sito/dashboard-app 0.0.44 → 0.0.46
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/README.md +125 -1
- package/dist/components/Drawer/Drawer.test.d.ts +1 -0
- package/dist/components/Empty/Empty.stories.d.ts +1 -0
- package/dist/components/Error/Error.stories.d.ts +3 -3
- package/dist/components/Error/types.d.ts +30 -2
- package/dist/components/Navbar/Navbar.stories.d.ts +8 -0
- package/dist/components/Navbar/NavbarProvider.d.ts +14 -0
- package/dist/components/Navbar/index.d.ts +1 -0
- package/dist/components/Navbar/types.d.ts +10 -0
- package/dist/components/TabsLayout/TabsLayout.stories.d.ts +2 -0
- package/dist/components/TabsLayout/types.d.ts +6 -0
- package/dist/dashboard-app.cjs +1 -1
- package/dist/dashboard-app.js +2155 -1810
- package/dist/lib/api/APIClient.d.ts +25 -1
- package/dist/lib/api/AuthClient.d.ts +10 -4
- package/dist/lib/api/AuthClient.test.d.ts +1 -0
- package/dist/lib/api/BaseClient.d.ts +2 -2
- package/dist/lib/api/IManager.d.ts +2 -1
- package/dist/lib/api/IndexedDBClient.d.ts +23 -0
- package/dist/lib/api/IndexedDBClient.test.d.ts +0 -0
- package/dist/lib/api/index.d.ts +1 -0
- package/dist/lib/api/utils/query.test.d.ts +1 -0
- package/dist/lib/entities/auth/AuthDto.d.ts +3 -1
- package/dist/lib/entities/auth/RefreshDto.d.ts +3 -0
- package/dist/lib/entities/auth/SessionDto.d.ts +2 -0
- package/dist/lib/entities/auth/SignOutDto.d.ts +3 -0
- package/dist/lib/entities/auth/index.d.ts +2 -0
- package/dist/main.css +1 -1
- package/dist/providers/DrawerMenuProvider.d.ts +1 -1
- package/dist/providers/DrawerMenuProvider.test.d.ts +1 -0
- package/dist/providers/NotificationProvider.test.d.ts +1 -0
- package/dist/providers/types.d.ts +5 -2
- package/package.json +4 -3
- package/dist/components/Clock/Clock.stories.d.ts +0 -12
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ pnpm add @sito/dashboard-app
|
|
|
19
19
|
- React DOM `18.3.1`
|
|
20
20
|
- `@tanstack/react-query` `5.83.0`
|
|
21
21
|
- `react-hook-form` `7.61.1`
|
|
22
|
-
- `@sito/dashboard` `^0.0.
|
|
22
|
+
- `@sito/dashboard` `^0.0.68`
|
|
23
23
|
- Font Awesome + Emotion peers defined in `package.json`
|
|
24
24
|
|
|
25
25
|
## Core exports
|
|
@@ -31,6 +31,48 @@ pnpm add @sito/dashboard-app
|
|
|
31
31
|
- Hooks: `useImportDialog`, `useDeleteDialog`, `usePostForm`, `useDeleteAction`, `useNavbar`, and more — all action hooks ship with default `sticky`, `multiple`, `id`, `icon`, and `tooltip` values so only `onClick` is required
|
|
32
32
|
- Providers and utilities: `ConfigProvider`, `ManagerProvider`, `AuthProvider`, `NotificationProvider`, `NavbarProvider`, DTOs, API clients
|
|
33
33
|
|
|
34
|
+
## Component usage patterns
|
|
35
|
+
|
|
36
|
+
### Error component
|
|
37
|
+
|
|
38
|
+
`Error` supports two modes:
|
|
39
|
+
|
|
40
|
+
- Default mode: icon + message + retry (uses `Button` internally)
|
|
41
|
+
- Custom mode: pass `children` for fully custom content
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { Error } from "@sito/dashboard-app";
|
|
45
|
+
|
|
46
|
+
<Error
|
|
47
|
+
error={error}
|
|
48
|
+
onRetry={() => refetch()}
|
|
49
|
+
retryLabel="Retry"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<Error>
|
|
53
|
+
<CustomErrorPanel />
|
|
54
|
+
</Error>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Do not combine default props (`error`, `message`, `onRetry`, etc.) with `children` in the same instance.
|
|
58
|
+
|
|
59
|
+
### TabsLayout link mode
|
|
60
|
+
|
|
61
|
+
`TabsLayout` renders route links by default (`useLinks = true`).
|
|
62
|
+
If your tabs are local UI state and should not navigate, set `useLinks={false}`.
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { TabsLayout } from "@sito/dashboard-app";
|
|
66
|
+
|
|
67
|
+
<TabsLayout
|
|
68
|
+
useLinks={false}
|
|
69
|
+
tabButtonProps={{ variant: "outlined", color: "secondary" }}
|
|
70
|
+
tabs={tabs}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`tabButtonProps` lets you customize each tab button style/behavior (except `onClick` and `children`, which are controlled by `TabsLayout`).
|
|
75
|
+
|
|
34
76
|
## Initial setup example
|
|
35
77
|
|
|
36
78
|
Wrap your app with the providers to enable navigation, React Query integration, auth context, and notifications.
|
|
@@ -112,6 +154,88 @@ export function App() {
|
|
|
112
154
|
- `npm run storybook`: run Storybook locally
|
|
113
155
|
- `npm run build-storybook`: generate static Storybook build
|
|
114
156
|
|
|
157
|
+
## Offline-first / IndexedDB fallback
|
|
158
|
+
|
|
159
|
+
`IndexedDBClient` is a drop-in offline alternative to `BaseClient`. It exposes the exact same method surface (`insert`, `insertMany`, `update`, `get`, `getById`, `export`, `import`, `commonGet`, `softDelete`, `restore`) but stores data locally in the browser's IndexedDB instead of calling a remote API.
|
|
160
|
+
|
|
161
|
+
### When to use it
|
|
162
|
+
|
|
163
|
+
Use `IndexedDBClient` when the remote API is unreachable — for example in offline-capable dashboards, field apps, or PWAs. The pattern is to detect connectivity and swap the client transparently:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { BaseClient, IndexedDBClient } from "@sito/dashboard-app";
|
|
167
|
+
|
|
168
|
+
// Online: hit the API. Offline: read/write from IndexedDB.
|
|
169
|
+
const productsClient = navigator.onLine
|
|
170
|
+
? new ProductsClient(import.meta.env.VITE_API_URL)
|
|
171
|
+
: new ProductsIndexedDBClient();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Creating an offline client
|
|
175
|
+
|
|
176
|
+
Extend `IndexedDBClient` the same way you would extend `BaseClient`:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import {
|
|
180
|
+
IndexedDBClient,
|
|
181
|
+
BaseEntityDto,
|
|
182
|
+
BaseCommonEntityDto,
|
|
183
|
+
BaseFilterDto,
|
|
184
|
+
DeleteDto,
|
|
185
|
+
ImportPreviewDto,
|
|
186
|
+
} from "@sito/dashboard-app";
|
|
187
|
+
|
|
188
|
+
interface ProductDto extends BaseEntityDto {
|
|
189
|
+
name: string;
|
|
190
|
+
price: number;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
interface ProductFilterDto extends BaseFilterDto {
|
|
194
|
+
category?: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
class ProductsIndexedDBClient extends IndexedDBClient<
|
|
198
|
+
"products",
|
|
199
|
+
ProductDto,
|
|
200
|
+
ProductDto, // TCommonDto
|
|
201
|
+
Omit<ProductDto, "id" | "createdAt" | "updatedAt" | "deletedAt">,
|
|
202
|
+
ProductDto, // TUpdateDto (extends DeleteDto via BaseEntityDto)
|
|
203
|
+
ProductFilterDto,
|
|
204
|
+
ImportPreviewDto
|
|
205
|
+
> {
|
|
206
|
+
constructor() {
|
|
207
|
+
super("products", "my-app-db");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Reacting to connectivity changes at runtime
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { useState, useEffect } from "react";
|
|
216
|
+
|
|
217
|
+
function useProductsClient() {
|
|
218
|
+
const [client, setClient] = useState(
|
|
219
|
+
() => navigator.onLine ? new ProductsClient(apiUrl) : new ProductsIndexedDBClient()
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
const goOnline = () => setClient(new ProductsClient(apiUrl));
|
|
224
|
+
const goOffline = () => setClient(new ProductsIndexedDBClient());
|
|
225
|
+
window.addEventListener("online", goOnline);
|
|
226
|
+
window.addEventListener("offline", goOffline);
|
|
227
|
+
return () => {
|
|
228
|
+
window.removeEventListener("online", goOnline);
|
|
229
|
+
window.removeEventListener("offline", goOffline);
|
|
230
|
+
};
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
return client;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
> **Note:** `IndexedDBClient` requires a browser environment. It will not work in SSR/Node contexts.
|
|
238
|
+
|
|
115
239
|
## Tests
|
|
116
240
|
|
|
117
241
|
Automated tests are configured with `Vitest` + `@testing-library/react`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,10 +4,10 @@ declare const meta: {
|
|
|
4
4
|
title: string;
|
|
5
5
|
component: typeof ErrorComponent;
|
|
6
6
|
tags: string[];
|
|
7
|
-
args: {
|
|
8
|
-
error: Error;
|
|
9
|
-
};
|
|
10
7
|
};
|
|
11
8
|
export default meta;
|
|
12
9
|
type Story = StoryObj<typeof meta>;
|
|
13
10
|
export declare const Basic: Story;
|
|
11
|
+
export declare const WithRetry: Story;
|
|
12
|
+
export declare const WithCustomIcon: Story;
|
|
13
|
+
export declare const CustomContent: Story;
|
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
|
|
3
|
+
import { ButtonPropsType } from '@sito/dashboard';
|
|
4
|
+
type ErrorIconPropsType = Omit<FontAwesomeIconProps, "icon"> & {
|
|
5
|
+
icon?: FontAwesomeIconProps["icon"];
|
|
6
|
+
};
|
|
7
|
+
export type ErrorDefaultPropsType = {
|
|
8
|
+
error?: Error | null;
|
|
9
|
+
message?: string;
|
|
10
|
+
iconProps?: ErrorIconPropsType | null;
|
|
11
|
+
onRetry?: () => void;
|
|
12
|
+
retryLabel?: string;
|
|
13
|
+
retryButtonProps?: Omit<ButtonPropsType, "type">;
|
|
14
|
+
messageProps?: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>;
|
|
15
|
+
className?: string;
|
|
3
16
|
resetErrorBoundary?: () => void;
|
|
17
|
+
children?: never;
|
|
18
|
+
};
|
|
19
|
+
export type ErrorCustomContentPropsType = {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
className?: string;
|
|
22
|
+
error?: never;
|
|
23
|
+
message?: never;
|
|
24
|
+
iconProps?: never;
|
|
25
|
+
onRetry?: never;
|
|
26
|
+
retryLabel?: never;
|
|
27
|
+
retryButtonProps?: never;
|
|
28
|
+
messageProps?: never;
|
|
29
|
+
resetErrorBoundary?: never;
|
|
4
30
|
};
|
|
31
|
+
export type ErrorPropsType = ErrorDefaultPropsType | ErrorCustomContentPropsType;
|
|
32
|
+
export {};
|
|
@@ -16,9 +16,17 @@ declare const meta: {
|
|
|
16
16
|
openDrawer: () => void;
|
|
17
17
|
showSearch: true;
|
|
18
18
|
};
|
|
19
|
+
decorators: ((Story: import('@storybook/core/csf').PartialStoryFn<import('@storybook/react').ReactRenderer, {
|
|
20
|
+
menuButtonProps?: import('..').IconButtonPropsLocalType | undefined;
|
|
21
|
+
openDrawer: () => void;
|
|
22
|
+
showSearch?: boolean | undefined;
|
|
23
|
+
}>) => import("react/jsx-runtime").JSX.Element)[];
|
|
19
24
|
};
|
|
20
25
|
export default meta;
|
|
21
26
|
type Story = StoryObj<typeof meta>;
|
|
22
27
|
export declare const Basic: Story;
|
|
23
28
|
export declare const WithoutSearch: Story;
|
|
24
29
|
export declare const WithCustomMenuButtonProps: Story;
|
|
30
|
+
export declare const WithCustomTitle: Story;
|
|
31
|
+
export declare const WithRightContent: Story;
|
|
32
|
+
export declare const WithTitleAndRightContent: Story;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NavbarContextType, NavbarProviderPropTypes } from './types.js';
|
|
2
|
+
declare const NavbarContext: import('react').Context<NavbarContextType>;
|
|
3
|
+
/**
|
|
4
|
+
* Navbar Provider
|
|
5
|
+
* @param props - provider props
|
|
6
|
+
* @returns React component
|
|
7
|
+
*/
|
|
8
|
+
declare const NavbarProvider: (props: NavbarProviderPropTypes) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* useNavbar hook
|
|
11
|
+
* @returns Navbar context
|
|
12
|
+
*/
|
|
13
|
+
declare const useNavbar: () => NavbarContextType;
|
|
14
|
+
export { NavbarContext, NavbarProvider, useNavbar };
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { IconButtonPropsLocalType } from '../Buttons/IconButton';
|
|
2
3
|
export type NavbarPropsType = {
|
|
3
4
|
menuButtonProps?: IconButtonPropsLocalType;
|
|
4
5
|
openDrawer: () => void;
|
|
5
6
|
showSearch?: boolean;
|
|
6
7
|
};
|
|
8
|
+
export type NavbarProviderPropTypes = {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export type NavbarContextType = {
|
|
12
|
+
title: string;
|
|
13
|
+
setTitle: (value: string) => void;
|
|
14
|
+
rightContent: ReactNode;
|
|
15
|
+
setRightContent: (value: ReactNode) => void;
|
|
16
|
+
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { ButtonPropsType } from '@sito/dashboard';
|
|
3
|
+
export type TabButtonPropsType = Omit<ButtonPropsType, "children" | "onClick" | "type">;
|
|
2
4
|
export type TabsLayoutPropsType = {
|
|
3
5
|
tabs: TabsType[];
|
|
4
6
|
defaultTab?: number;
|
|
5
7
|
className?: string;
|
|
6
8
|
tabsContainerClassName?: string;
|
|
9
|
+
useLinks?: boolean;
|
|
10
|
+
tabButtonProps?: TabButtonPropsType;
|
|
7
11
|
};
|
|
8
12
|
export type TabsType = {
|
|
9
13
|
id: number | string;
|
|
@@ -18,4 +22,6 @@ export type TabPropsType = {
|
|
|
18
22
|
active: boolean;
|
|
19
23
|
onClick: () => void;
|
|
20
24
|
siblings?: boolean;
|
|
25
|
+
useLinks?: boolean;
|
|
26
|
+
tabButtonProps?: TabButtonPropsType;
|
|
21
27
|
};
|