@qumra/jisr 1.0.1 → 1.0.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/README.md CHANGED
@@ -347,18 +347,18 @@ const myAction: AppBridgeAction = {
347
347
 
348
348
  | Package | Description |
349
349
  |---------|-------------|
350
- | [@qumra/jisr](https://www.npmjs.com/package/@qumra/jisr) | React hooks for app bridge communication |
351
- | [@qumra/config](https://www.npmjs.com/package/@qumra/config) | Centralized config management for Qumra apps |
352
- | [@qumra/logger](https://www.npmjs.com/package/@qumra/logger) | Structured logging utility |
353
- | [@qumra/ui](https://www.npmjs.com/package/@qumra/ui) | Reusable React component library |
354
- | [@qumra/api-client](https://www.npmjs.com/package/@qumra/api-client) | API client for Qumra services |
355
- | [@qumra/types](https://www.npmjs.com/package/@qumra/types) | Shared TypeScript type definitions |
356
- | [@qumra/utils](https://www.npmjs.com/package/@qumra/utils) | Common utility functions |
350
+ | [@qumra/app-sdk](https://www.npmjs.com/package/@qumra/app-sdk) | Core SDK sessions, security, errors |
351
+ | [@qumra/app-react-router](https://www.npmjs.com/package/@qumra/app-react-router) | React Router 7 integration |
352
+ | [@qumra/app-session-storage-prisma](https://www.npmjs.com/package/@qumra/app-session-storage-prisma) | Prisma session storage |
353
+ | [@qumra/app-session-storage-mongodb](https://www.npmjs.com/package/@qumra/app-session-storage-mongodb) | MongoDB session storage |
354
+ | [@qumra/jisr](https://www.npmjs.com/package/@qumra/jisr) | App Bridge for iframe communication |
355
+ | [@qumra/manara](https://www.npmjs.com/package/@qumra/manara) | Design system & components |
356
+ | [@qumra/riwaq](https://www.npmjs.com/package/@qumra/riwaq) | UI Extensions SDK |
357
357
 
358
358
  ## License
359
359
 
360
- ISC © [Nicetag](https://github.com/nicetag)
360
+ ISC © Qumra
361
361
 
362
- ## Repository
362
+ ## Documentation
363
363
 
364
- [github.com/nicetag/qumra-apps-sdk](https://github.com/nicetag/qumra-apps-sdk)
364
+ https://docs.qumra.cloud
@@ -12,6 +12,8 @@ export interface QumraAppBridgeProviderProps {
12
12
  children: React.ReactNode;
13
13
  /** Configuration for the AppBridge instance */
14
14
  config: AppBridgeConfig;
15
+ /** Custom navigate handler. If not provided, uses built-in SPA navigation (pushState + popstate). */
16
+ navigate?: (url: string) => void;
15
17
  }
16
18
  /**
17
19
  * Provider component for AppBridge
@@ -19,18 +21,18 @@ export interface QumraAppBridgeProviderProps {
19
21
  *
20
22
  * @example
21
23
  * ```tsx
22
- * import { QumraAppBridgeProvider } from '@qumra/app-bridge-react';
24
+ * // Works out of the box — built-in navigation
25
+ * <QumraAppBridgeProvider config={{ apiKey: 'your-api-key' }}>
26
+ * <YourApp />
27
+ * </QumraAppBridgeProvider>
23
28
  *
24
- * export default function App() {
25
- * return (
26
- * <QumraAppBridgeProvider config={{ apiKey: 'your-api-key' }}>
27
- * <YourApp />
28
- * </QumraAppBridgeProvider>
29
- * );
30
- * }
29
+ * // Or with a custom navigate handler
30
+ * <QumraAppBridgeProvider config={{ apiKey }} navigate={(url) => navigate(url)}>
31
+ * <YourApp />
32
+ * </QumraAppBridgeProvider>
31
33
  * ```
32
34
  */
33
- export declare function QumraAppBridgeProvider({ children, config, }: QumraAppBridgeProviderProps): JSX.Element;
35
+ export declare function QumraAppBridgeProvider({ children, config, navigate, }: QumraAppBridgeProviderProps): JSX.Element;
34
36
  /**
35
37
  * Internal hook to get the AppBridge context
36
38
  * @internal
@@ -38,4 +40,9 @@ export declare function QumraAppBridgeProvider({ children, config, }: QumraAppBr
38
40
  * @throws Error if used outside of QumraAppBridgeProvider
39
41
  */
40
42
  export declare function useAppBridgeContext(): AppBridge;
43
+ /**
44
+ * Internal hook to get the app-level navigate handler
45
+ * @internal
46
+ */
47
+ export declare function useAppNavigate(): (url: string) => void;
41
48
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/hooks/context.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzE;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+CAA+C;IAC/C,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,MAAM,GACP,EAAE,2BAA2B,GAAG,GAAG,CAAC,OAAO,CAkB3C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,CAU/C"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/hooks/context.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAoBzE;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+CAA+C;IAC/C,MAAM,EAAE,eAAe,CAAC;IACxB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,MAAM,EACN,QAAQ,GACT,EAAE,2BAA2B,GAAG,GAAG,CAAC,OAAO,CAwB3C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,CAU/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAGtD"}
@@ -7,34 +7,49 @@ import React from 'react';
7
7
  import { AppBridge } from '../utils/app-bridge.js';
8
8
  /** React Context for AppBridge instance */
9
9
  const AppBridgeContext = React.createContext(null);
10
+ /** App-level navigate handler */
11
+ const NavigateContext = React.createContext(null);
12
+ /**
13
+ * Built-in navigate that works with React Router and most SPA routers.
14
+ * Uses pushState + popstate event which routers listen to.
15
+ */
16
+ function defaultNavigate(url) {
17
+ if (typeof window === 'undefined')
18
+ return;
19
+ window.history.pushState(null, '', url);
20
+ window.dispatchEvent(new PopStateEvent('popstate'));
21
+ }
10
22
  /**
11
23
  * Provider component for AppBridge
12
24
  * Must wrap your application to provide the AppBridge to all hooks
13
25
  *
14
26
  * @example
15
27
  * ```tsx
16
- * import { QumraAppBridgeProvider } from '@qumra/app-bridge-react';
28
+ * // Works out of the box — built-in navigation
29
+ * <QumraAppBridgeProvider config={{ apiKey: 'your-api-key' }}>
30
+ * <YourApp />
31
+ * </QumraAppBridgeProvider>
17
32
  *
18
- * export default function App() {
19
- * return (
20
- * <QumraAppBridgeProvider config={{ apiKey: 'your-api-key' }}>
21
- * <YourApp />
22
- * </QumraAppBridgeProvider>
23
- * );
24
- * }
33
+ * // Or with a custom navigate handler
34
+ * <QumraAppBridgeProvider config={{ apiKey }} navigate={(url) => navigate(url)}>
35
+ * <YourApp />
36
+ * </QumraAppBridgeProvider>
25
37
  * ```
26
38
  */
27
- export function QumraAppBridgeProvider({ children, config, }) {
39
+ export function QumraAppBridgeProvider({ children, config, navigate, }) {
28
40
  const [bridge] = React.useState(() => new AppBridge(config));
41
+ const navigateRef = React.useRef(navigate ?? defaultNavigate);
42
+ navigateRef.current = navigate ?? defaultNavigate;
29
43
  React.useEffect(() => {
30
- // Signal to the host that the app is ready
44
+ bridge.setNavigateHandler((path) => {
45
+ navigateRef.current(path);
46
+ });
31
47
  bridge.ready();
32
- // Cleanup on unmount
33
48
  return () => {
34
49
  bridge.destroy();
35
50
  };
36
51
  }, [bridge]);
37
- return (_jsx(AppBridgeContext.Provider, { value: bridge, children: children }));
52
+ return (_jsx(AppBridgeContext.Provider, { value: bridge, children: _jsx(NavigateContext.Provider, { value: navigateRef, children: children }) }));
38
53
  }
39
54
  /**
40
55
  * Internal hook to get the AppBridge context
@@ -49,4 +64,12 @@ export function useAppBridgeContext() {
49
64
  }
50
65
  return context;
51
66
  }
67
+ /**
68
+ * Internal hook to get the app-level navigate handler
69
+ * @internal
70
+ */
71
+ export function useAppNavigate() {
72
+ const ref = React.useContext(NavigateContext);
73
+ return ref?.current ?? defaultNavigate;
74
+ }
52
75
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/hooks/context.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAwB,MAAM,wBAAwB,CAAC;AAEzE,2CAA2C;AAC3C,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAAmB,IAAI,CAAC,CAAC;AAYrE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,QAAQ,EACR,MAAM,GACsB;IAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAE7D,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,2CAA2C;QAC3C,MAAM,CAAC,KAAK,EAAE,CAAC;QAEf,qBAAqB;QACrB,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YACrC,QAAQ,GACiB,CAC7B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAEnD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/hooks/context.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAwB,MAAM,wBAAwB,CAAC;AAEzE,2CAA2C;AAC3C,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAAmB,IAAI,CAAC,CAAC;AAErE,iCAAiC;AACjC,MAAM,eAAe,GAAG,KAAK,CAAC,aAAa,CAEzC,IAAI,CAAC,CAAC;AAER;;;GAGG;AACH,SAAS,eAAe,CAAC,GAAW;IAClC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;AACtD,CAAC;AAcD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,QAAQ,EACR,MAAM,EACN,QAAQ,GACoB;IAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,IAAI,eAAe,CAAC,CAAC;IAC9D,WAAW,CAAC,OAAO,GAAG,QAAQ,IAAI,eAAe,CAAC;IAElD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,EAAE,EAAE;YACjC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,EAAE,CAAC;QAEf,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YACtC,KAAC,eAAe,CAAC,QAAQ,IAAC,KAAK,EAAE,WAAW,YACzC,QAAQ,GACgB,GACD,CAC7B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAEnD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAC9C,OAAO,GAAG,EAAE,OAAO,IAAI,eAAe,CAAC;AACzC,CAAC"}
@@ -2,45 +2,44 @@
2
2
  * Hook for managing modal dialogs
3
3
  */
4
4
  import type { ModalOptions } from '../utils/actions.js';
5
+ /**
6
+ * Options for modal.show() — modal config + event callbacks
7
+ */
8
+ export interface ModalShowOptions extends ModalOptions {
9
+ /** Called when the user clicks the primary action */
10
+ onConfirm?: () => void | Promise<void>;
11
+ /** Called when the user clicks the secondary action or dismisses the modal */
12
+ onCancel?: () => void;
13
+ }
5
14
  /**
6
15
  * Modal utility functions
7
16
  */
8
17
  export interface UseModalResult {
9
- /** Open a modal dialog */
10
- open: (options: ModalOptions) => void;
18
+ /** Show a modal dialog */
19
+ show: (options: ModalShowOptions) => void;
11
20
  /** Close the current modal */
12
- close: () => void;
21
+ hide: () => void;
13
22
  }
14
23
  /**
15
24
  * Hook to manage modal dialogs
16
25
  *
17
26
  * @example
18
27
  * ```tsx
19
- * import { useModal } from '@qumra/app-bridge-react';
20
- *
21
- * export function MyComponent() {
22
- * const modal = useModal();
23
- *
24
- * const handleOpenSettings = () => {
25
- * modal.open({
26
- * title: 'Settings',
27
- * url: '/settings',
28
- * size: 'large',
29
- * primaryAction: {
30
- * content: 'Save',
31
- * onAction: () => {
32
- * // Handle save
33
- * modal.close();
34
- * }
35
- * }
36
- * });
37
- * };
28
+ * const modal = useModal();
38
29
  *
39
- * return <button onClick={handleOpenSettings}>Open Settings</button>;
40
- * }
30
+ * modal.show({
31
+ * title: 'حذف المنتج؟',
32
+ * message: 'هذا الإجراء لا يمكن التراجع عنه',
33
+ * primaryAction: { label: 'حذف', variant: 'destructive' },
34
+ * secondaryAction: { label: 'إلغاء' },
35
+ * onConfirm: async () => {
36
+ * await deleteProduct();
37
+ * modal.hide();
38
+ * },
39
+ * });
41
40
  * ```
42
41
  *
43
- * @returns Object with open and close methods
42
+ * @returns Object with show and hide methods
44
43
  */
45
44
  export declare function useModal(): UseModalResult;
46
45
  //# sourceMappingURL=useModal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModal.d.ts","sourceRoot":"","sources":["../../src/hooks/useModal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,QAAQ,IAAI,cAAc,CAezC"}
1
+ {"version":3,"file":"useModal.d.ts","sourceRoot":"","sources":["../../src/hooks/useModal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,IAAI,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,8BAA8B;IAC9B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,IAAI,cAAc,CAyCzC"}
@@ -1,48 +1,55 @@
1
1
  /**
2
2
  * Hook for managing modal dialogs
3
3
  */
4
- import { useCallback } from 'react';
4
+ import { useCallback, useEffect, useRef } from 'react';
5
5
  import { useAppBridge } from './useAppBridge.js';
6
- import { openModal, closeModal } from '../utils/actions.js';
6
+ import { ActionType, openModal, closeModal } from '../utils/actions.js';
7
7
  /**
8
8
  * Hook to manage modal dialogs
9
9
  *
10
10
  * @example
11
11
  * ```tsx
12
- * import { useModal } from '@qumra/app-bridge-react';
12
+ * const modal = useModal();
13
13
  *
14
- * export function MyComponent() {
15
- * const modal = useModal();
16
- *
17
- * const handleOpenSettings = () => {
18
- * modal.open({
19
- * title: 'Settings',
20
- * url: '/settings',
21
- * size: 'large',
22
- * primaryAction: {
23
- * content: 'Save',
24
- * onAction: () => {
25
- * // Handle save
26
- * modal.close();
27
- * }
28
- * }
29
- * });
30
- * };
31
- *
32
- * return <button onClick={handleOpenSettings}>Open Settings</button>;
33
- * }
14
+ * modal.show({
15
+ * title: 'حذف المنتج؟',
16
+ * message: 'هذا الإجراء لا يمكن التراجع عنه',
17
+ * primaryAction: { label: 'حذف', variant: 'destructive' },
18
+ * secondaryAction: { label: 'إلغاء' },
19
+ * onConfirm: async () => {
20
+ * await deleteProduct();
21
+ * modal.hide();
22
+ * },
23
+ * });
34
24
  * ```
35
25
  *
36
- * @returns Object with open and close methods
26
+ * @returns Object with show and hide methods
37
27
  */
38
28
  export function useModal() {
39
29
  const bridge = useAppBridge();
40
- const open = useCallback((options) => {
41
- bridge.dispatch(openModal(options));
30
+ const optionsRef = useRef();
31
+ useEffect(() => {
32
+ const unsubConfirm = bridge.subscribe(ActionType.MODAL_CONFIRM, () => {
33
+ optionsRef.current?.onConfirm?.();
34
+ });
35
+ const unsubCancel = bridge.subscribe(ActionType.MODAL_CANCEL, () => {
36
+ optionsRef.current?.onCancel?.();
37
+ optionsRef.current = undefined;
38
+ });
39
+ return () => {
40
+ unsubConfirm();
41
+ unsubCancel();
42
+ };
43
+ }, [bridge]);
44
+ const show = useCallback((options) => {
45
+ optionsRef.current = options;
46
+ const { onConfirm, onCancel, ...modalOptions } = options;
47
+ bridge.dispatch(openModal(modalOptions));
42
48
  }, [bridge]);
43
- const close = useCallback(() => {
49
+ const hide = useCallback(() => {
50
+ optionsRef.current = undefined;
44
51
  bridge.dispatch(closeModal());
45
52
  }, [bridge]);
46
- return { open, close };
53
+ return { show, hide };
47
54
  }
48
55
  //# sourceMappingURL=useModal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModal.js","sourceRoot":"","sources":["../../src/hooks/useModal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAa5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,MAAM,IAAI,GAAG,WAAW,CACtB,CAAC,OAAqB,EAAE,EAAE;QACxB,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACtC,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"useModal.js","sourceRoot":"","sources":["../../src/hooks/useModal.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAuBxE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,MAAM,EAAoB,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CACnC,UAAU,CAAC,aAAa,EACxB,GAAG,EAAE;YACH,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC;QACpC,CAAC,CACF,CAAC;QAEF,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAClC,UAAU,CAAC,YAAY,EACvB,GAAG,EAAE;YACH,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC;YACjC,UAAU,CAAC,OAAO,GAAG,SAAS,CAAC;QACjC,CAAC,CACF,CAAC;QAEF,OAAO,GAAG,EAAE;YACV,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,IAAI,GAAG,WAAW,CACtB,CAAC,OAAyB,EAAE,EAAE;QAC5B,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;QACzD,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC3C,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,UAAU,CAAC,OAAO,GAAG,SAAS,CAAC;QAC/B,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Hook for managing the navigation menu in the Qumra Admin host.
3
+ */
4
+ import type { NavigationMenuItem } from '../utils/actions.js';
5
+ /**
6
+ * Partial update for a navigation menu item (url cannot be changed)
7
+ */
8
+ export type NavigationMenuItemUpdate = Partial<Omit<NavigationMenuItem, 'url'>>;
9
+ /**
10
+ * Navigation menu utility functions
11
+ */
12
+ export interface UseNavigationMenuResult {
13
+ /** Current navigation pages */
14
+ readonly pages: NavigationMenuItem[];
15
+ /** Replace all navigation pages */
16
+ set: (pages: NavigationMenuItem[]) => void;
17
+ /** Add a navigation item. If an item with the same url exists, it will be updated */
18
+ add: (item: NavigationMenuItem) => void;
19
+ /** Remove a navigation item by url */
20
+ remove: (url: string) => void;
21
+ /** Update properties of a single item by url */
22
+ update: (url: string, partial: NavigationMenuItemUpdate) => void;
23
+ /** Tell the host which page is currently active */
24
+ setActive: (path: string) => void;
25
+ }
26
+ /**
27
+ * Hook to manage the navigation menu shown in the Qumra Admin host.
28
+ *
29
+ * The returned object is **stable** — safe to use in useEffect dependencies
30
+ * without causing infinite loops.
31
+ *
32
+ * Navigation events from the host (HOST::NAVIGATION::NAVIGATE) are handled
33
+ * at the Provider level, so they work even when components unmount/remount.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * function App() {
38
+ * const navigation = useNavigationMenu();
39
+ *
40
+ * useEffect(() => {
41
+ * navigation.set([
42
+ * { label: 'الرئيسية', url: '/', icon: 'home' },
43
+ * { label: 'الطلبات', url: '/orders', icon: 'orders', badge: 3 },
44
+ * ]);
45
+ * }, [navigation]); // safe — navigation is stable
46
+ *
47
+ * // Update badge dynamically
48
+ * navigation.update('/orders', { badge: 5 });
49
+ * }
50
+ * ```
51
+ *
52
+ * @returns Stable object with navigation management methods
53
+ */
54
+ export declare function useNavigationMenu(): UseNavigationMenuResult;
55
+ //# sourceMappingURL=useNavigationMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavigationMenu.d.ts","sourceRoot":"","sources":["../../src/hooks/useNavigationMenu.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IACrC,mCAAmC;IACnC,GAAG,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,IAAI,CAAC;IAC3C,qFAAqF;IACrF,GAAG,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACxC,sCAAsC;IACtC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,gDAAgD;IAChD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACjE,mDAAmD;IACnD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iBAAiB,IAAI,uBAAuB,CA+E3D"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Hook for managing the navigation menu in the Qumra Admin host.
3
+ */
4
+ import { useCallback, useMemo, useRef } from 'react';
5
+ import { useAppBridge } from './useAppBridge.js';
6
+ import { ActionType } from '../utils/actions.js';
7
+ /**
8
+ * Hook to manage the navigation menu shown in the Qumra Admin host.
9
+ *
10
+ * The returned object is **stable** — safe to use in useEffect dependencies
11
+ * without causing infinite loops.
12
+ *
13
+ * Navigation events from the host (HOST::NAVIGATION::NAVIGATE) are handled
14
+ * at the Provider level, so they work even when components unmount/remount.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function App() {
19
+ * const navigation = useNavigationMenu();
20
+ *
21
+ * useEffect(() => {
22
+ * navigation.set([
23
+ * { label: 'الرئيسية', url: '/', icon: 'home' },
24
+ * { label: 'الطلبات', url: '/orders', icon: 'orders', badge: 3 },
25
+ * ]);
26
+ * }, [navigation]); // safe — navigation is stable
27
+ *
28
+ * // Update badge dynamically
29
+ * navigation.update('/orders', { badge: 5 });
30
+ * }
31
+ * ```
32
+ *
33
+ * @returns Stable object with navigation management methods
34
+ */
35
+ export function useNavigationMenu() {
36
+ const bridge = useAppBridge();
37
+ const pagesRef = useRef([]);
38
+ // Dispatch the full pages list to the host and update the ref
39
+ const dispatchPages = useCallback((nextPages) => {
40
+ pagesRef.current = nextPages;
41
+ bridge.dispatch({
42
+ type: ActionType.NAVIGATION_UPDATE,
43
+ payload: { pages: nextPages },
44
+ });
45
+ }, [bridge]);
46
+ const set = useCallback((nextPages) => {
47
+ dispatchPages(nextPages);
48
+ }, [dispatchPages]);
49
+ const add = useCallback((item) => {
50
+ const current = pagesRef.current;
51
+ const index = current.findIndex((i) => i.url === item.url);
52
+ const nextPages = index >= 0
53
+ ? current.map((i) => (i.url === item.url ? { ...i, ...item } : i))
54
+ : [...current, item];
55
+ dispatchPages(nextPages);
56
+ }, [dispatchPages]);
57
+ const remove = useCallback((url) => {
58
+ dispatchPages(pagesRef.current.filter((i) => i.url !== url));
59
+ }, [dispatchPages]);
60
+ const update = useCallback((url, partial) => {
61
+ dispatchPages(pagesRef.current.map((i) => i.url === url ? { ...i, ...partial } : i));
62
+ }, [dispatchPages]);
63
+ const setActive = useCallback((path) => {
64
+ bridge.dispatch({
65
+ type: ActionType.NAVIGATION_SET_ACTIVE,
66
+ payload: { path },
67
+ });
68
+ }, [bridge]);
69
+ // Stable object — all deps are stable (bridge never changes)
70
+ return useMemo(() => ({
71
+ get pages() {
72
+ return pagesRef.current;
73
+ },
74
+ set,
75
+ add,
76
+ remove,
77
+ update,
78
+ setActive,
79
+ }), [set, add, remove, update, setActive]);
80
+ }
81
+ //# sourceMappingURL=useNavigationMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavigationMenu.js","sourceRoot":"","sources":["../../src/hooks/useNavigationMenu.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,37 @@
1
1
  /**
2
2
  * Hook for managing the save bar (unsaved changes indicator)
3
3
  */
4
- import type { SaveBarOptions } from '../utils/actions.js';
4
+ /**
5
+ * Helpers passed to onSave for manual control over save result
6
+ */
7
+ export interface SaveBarHelpers {
8
+ /** Signal that the save completed successfully */
9
+ complete: () => void;
10
+ /** Signal that the save failed */
11
+ error: (message?: string) => void;
12
+ }
13
+ /**
14
+ * Options passed to saveBar.show()
15
+ */
16
+ export interface SaveBarShowOptions {
17
+ /**
18
+ * Callback when the save button is clicked.
19
+ *
20
+ * Receives `{ complete, error }` helpers for manual control.
21
+ * - If you call `complete()` or `error()`, you control the result.
22
+ * - If you don't call either, the hook auto-dispatches based on the promise:
23
+ * resolves → SAVE_COMPLETE, throws → SAVE_ERROR.
24
+ */
25
+ onSave?: (helpers: SaveBarHelpers) => void | Promise<void>;
26
+ /** Callback when the discard button is clicked */
27
+ onDiscard?: () => void;
28
+ }
5
29
  /**
6
30
  * Save bar utility functions
7
31
  */
8
32
  export interface UseSaveBarResult {
9
33
  /** Show the save bar */
10
- show: (options?: SaveBarOptions) => void;
34
+ show: (options?: SaveBarShowOptions) => void;
11
35
  /** Hide the save bar */
12
36
  hide: () => void;
13
37
  }
@@ -15,31 +39,31 @@ export interface UseSaveBarResult {
15
39
  * Hook to manage the save bar for indicating unsaved changes
16
40
  *
17
41
  * @example
42
+ * Simple (auto mode):
18
43
  * ```tsx
19
- * import { useSaveBar } from '@qumra/app-bridge-react';
20
- *
21
- * export function MyComponent() {
22
- * const saveBar = useSaveBar();
23
- * const [hasChanges, setHasChanges] = React.useState(false);
24
- *
25
- * const handleChange = () => {
26
- * setHasChanges(true);
27
- * saveBar.show();
28
- * };
29
- *
30
- * const handleSave = async () => {
44
+ * saveBar.show({
45
+ * onSave: async () => {
31
46
  * await saveData();
32
- * setHasChanges(false);
33
- * saveBar.hide();
34
- * };
47
+ * // resolves → SAVE_COMPLETE, throws → SAVE_ERROR
48
+ * },
49
+ * onDiscard: () => resetForm(),
50
+ * });
51
+ * ```
35
52
  *
36
- * return (
37
- * <>
38
- * <input onChange={handleChange} />
39
- * <button onClick={handleSave}>Save</button>
40
- * </>
41
- * );
42
- * }
53
+ * @example
54
+ * Dynamic (manual control):
55
+ * ```tsx
56
+ * saveBar.show({
57
+ * onSave: async ({ complete, error }) => {
58
+ * const res = await fetch('/api/save', { method: 'POST' });
59
+ * if (res.ok) {
60
+ * complete();
61
+ * } else {
62
+ * error('فشل الحفظ');
63
+ * }
64
+ * },
65
+ * onDiscard: () => resetForm(),
66
+ * });
43
67
  * ```
44
68
  *
45
69
  * @returns Object with show and hide methods
@@ -1 +1 @@
1
- {"version":3,"file":"useSaveBar.d.ts","sourceRoot":"","sources":["../../src/hooks/useSaveBar.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,wBAAwB;IACxB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CAe7C"}
1
+ {"version":3,"file":"useSaveBar.d.ts","sourceRoot":"","sources":["../../src/hooks/useSaveBar.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kCAAkC;IAClC,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,wBAAwB;IACxB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CAqE7C"}