@isi-ui7/bos7-shared 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/api.d.ts +4 -0
  2. package/dist/auth7/client.d.ts +26 -0
  3. package/dist/auth7/delegated-proxy.d.ts +33 -0
  4. package/dist/auth7/hooks/useAuth.d.ts +10 -0
  5. package/dist/auth7/hooks/useSession.d.ts +12 -0
  6. package/dist/auth7/index.d.ts +12 -0
  7. package/dist/auth7/provider.d.ts +13 -0
  8. package/dist/auth7/scope-guard.d.ts +5 -0
  9. package/dist/auth7/service-account.d.ts +2 -0
  10. package/dist/auth7/token-exchange.d.ts +11 -0
  11. package/dist/auth7/types.d.ts +63 -0
  12. package/dist/backend.d.ts +20 -0
  13. package/dist/crud-components.d.ts +47 -0
  14. package/dist/crud-hooks.d.ts +15 -0
  15. package/dist/crud-types.d.ts +86 -0
  16. package/dist/data-table/cursor-adapter.d.ts +74 -0
  17. package/dist/event-bus/index.d.ts +20 -0
  18. package/dist/event-bus/useEventBus.d.ts +21 -0
  19. package/dist/event-bus.d.ts +1 -0
  20. package/dist/export.d.ts +7 -0
  21. package/dist/form-layout.d.ts +39 -0
  22. package/dist/form-numeric.d.ts +35 -0
  23. package/dist/form-renderer-utils.d.ts +10 -0
  24. package/dist/form-renderer.d.ts +18 -0
  25. package/dist/form-rules.d.ts +12 -0
  26. package/dist/form-types.d.ts +130 -0
  27. package/dist/html2canvas.esm-d2sM-0Wm.js +4870 -0
  28. package/dist/i18n.d.ts +35 -0
  29. package/dist/index.d.ts +18 -0
  30. package/dist/index.es-Bylk5fh-.js +5649 -0
  31. package/dist/index.js +20779 -0
  32. package/dist/jspdf.es.min-Dzm5j8wC.js +9977 -0
  33. package/dist/jspdf.plugin.autotable-DcntYaes.js +1078 -0
  34. package/dist/modal-layout.d.ts +45 -0
  35. package/dist/nats/client.d.ts +54 -0
  36. package/dist/notif7.d.ts +21 -0
  37. package/dist/notification/routing.d.ts +4 -0
  38. package/dist/notifications-bff.d.ts +22 -0
  39. package/dist/observability/otel-init.d.ts +1 -0
  40. package/dist/page-layout.d.ts +24 -0
  41. package/dist/purify.es-CiEWEeUM.js +605 -0
  42. package/dist/shell/app-shell-layout.d.ts +70 -0
  43. package/dist/shell/branch.d.ts +6 -0
  44. package/dist/shell/interaction.d.ts +2 -0
  45. package/dist/shell/provider.d.ts +15 -0
  46. package/dist/style-contract.d.ts +72 -0
  47. package/dist/types/core.d.ts +39 -0
  48. package/dist/types/notification.d.ts +30 -0
  49. package/dist/workflow/api-client.d.ts +2 -0
  50. package/dist/workflow/backend.d.ts +9 -0
  51. package/dist/workflow/crud-client.d.ts +3 -0
  52. package/dist/workflow/notif7.d.ts +6 -0
  53. package/dist/workflow/starter.d.ts +44 -0
  54. package/dist/workflow/use-crud-form.d.ts +27 -0
  55. package/dist/workflow/use-workflow-tracker.d.ts +40 -0
  56. package/package.json +92 -0
  57. package/src/api.ts +40 -0
  58. package/src/auth7/client.ts +248 -0
  59. package/src/auth7/delegated-proxy.ts +80 -0
  60. package/src/auth7/hooks/useAuth.ts +21 -0
  61. package/src/auth7/hooks/useSession.ts +75 -0
  62. package/src/auth7/index.ts +14 -0
  63. package/src/auth7/provider.tsx +256 -0
  64. package/src/auth7/scope-guard.ts +54 -0
  65. package/src/auth7/service-account.ts +71 -0
  66. package/src/auth7/token-exchange.ts +125 -0
  67. package/src/auth7/types.ts +72 -0
  68. package/src/backend.ts +96 -0
  69. package/src/crud-components.tsx +580 -0
  70. package/src/crud-hooks.test.ts +32 -0
  71. package/src/crud-hooks.ts +31 -0
  72. package/src/crud-types.ts +64 -0
  73. package/src/data-table/cursor-adapter.ts +239 -0
  74. package/src/event-bus/index.ts +90 -0
  75. package/src/event-bus/useEventBus.ts +93 -0
  76. package/src/event-bus.ts +1 -0
  77. package/src/export.ts +62 -0
  78. package/src/form-contract.css +52 -0
  79. package/src/form-layout.tsx +90 -0
  80. package/src/form-numeric.ts +173 -0
  81. package/src/form-renderer-utils.ts +139 -0
  82. package/src/form-renderer.tsx +624 -0
  83. package/src/form-rules.ts +54 -0
  84. package/src/form-types.ts +126 -0
  85. package/src/i18n.tsx +92 -0
  86. package/src/index.ts +20 -0
  87. package/src/modal-layout.tsx +60 -0
  88. package/src/nats/client.ts +130 -0
  89. package/src/notif7.ts +76 -0
  90. package/src/notification/routing.ts +64 -0
  91. package/src/notifications-bff.ts +273 -0
  92. package/src/observability/otel-init.ts +41 -0
  93. package/src/page-layout.tsx +69 -0
  94. package/src/shell/app-shell-layout.tsx +494 -0
  95. package/src/shell/branch.ts +48 -0
  96. package/src/shell/interaction.ts +17 -0
  97. package/src/shell/provider.tsx +34 -0
  98. package/src/style-contract.test.ts +59 -0
  99. package/src/style-contract.ts +132 -0
  100. package/src/types/core.ts +46 -0
  101. package/src/types/notification.ts +35 -0
  102. package/src/workflow/api-client.ts +7 -0
  103. package/src/workflow/backend.ts +53 -0
  104. package/src/workflow/crud-client.ts +43 -0
  105. package/src/workflow/notif7.ts +28 -0
  106. package/src/workflow/starter.ts +153 -0
  107. package/src/workflow/use-crud-form.ts +176 -0
  108. package/src/workflow/use-workflow-tracker.ts +109 -0
@@ -0,0 +1,70 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface AppShellAuthState {
4
+ isAuthenticated: boolean;
5
+ isLoading: boolean;
6
+ user: {
7
+ name?: string | null;
8
+ email?: string | null;
9
+ roles?: string[] | null;
10
+ branch_id?: string | null;
11
+ } | null;
12
+ login: () => void;
13
+ logout: () => Promise<void> | void;
14
+ }
15
+ export type ShellNavItemLike = any;
16
+ export interface ShellNotificationLike {
17
+ id?: string;
18
+ read?: boolean;
19
+ [key: string]: unknown;
20
+ }
21
+ interface RouterLike {
22
+ push: (href: string) => void;
23
+ }
24
+ interface ThemeLike {
25
+ displayName?: string;
26
+ }
27
+ interface NotificationBellLike {
28
+ badgeCount: number;
29
+ markAllRead: () => void;
30
+ onItemClick: (n: ShellNotificationLike) => void;
31
+ fetchBadge: () => void;
32
+ }
33
+ interface NotificationBellOptionsLike {
34
+ apiBase?: string;
35
+ enabled?: boolean;
36
+ onNavigate?: (href: string) => void;
37
+ }
38
+ export type AppSwitchMode = "push" | "location";
39
+ export interface AppShellLayoutDeps {
40
+ usePathname: () => string;
41
+ useRouter: () => RouterLike;
42
+ useTheme: () => {
43
+ theme: ThemeLike;
44
+ };
45
+ useNotificationBell: (opts: NotificationBellOptionsLike) => NotificationBellLike;
46
+ GlobalTheme: any;
47
+ ModalProvider: any;
48
+ UiShell: any;
49
+ useAuth?: () => AppShellAuthState;
50
+ }
51
+ export interface AppShellLayoutProps {
52
+ children: ReactNode;
53
+ deps: AppShellLayoutDeps;
54
+ navItems: ShellNavItemLike[];
55
+ markActive: (items: ShellNavItemLike[], pathname: string) => ShellNavItemLike[];
56
+ appNameFallback: string;
57
+ auth7UiUrlFallback?: string;
58
+ portalUrlFallback?: string;
59
+ roleFallback?: string;
60
+ appSwitchMode?: AppSwitchMode;
61
+ resolveNotificationHref?: (href: string, navigate: (href: string) => void) => void;
62
+ notificationsViewAllHref?: string;
63
+ notificationsViewAllTarget?: string;
64
+ hideMenuButton?: boolean;
65
+ disableAppsMenu?: boolean;
66
+ sideNavMode?: "drilldown" | "overlay";
67
+ sideNavItems?: ShellNavItemLike[];
68
+ }
69
+ export declare function AppShellLayout({ children, deps, navItems, markActive, appNameFallback, auth7UiUrlFallback, portalUrlFallback, roleFallback, appSwitchMode, resolveNotificationHref, notificationsViewAllHref, notificationsViewAllTarget, hideMenuButton, disableAppsMenu, sideNavMode, sideNavItems, }: AppShellLayoutProps): import("react/jsx-runtime").JSX.Element;
70
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare function switchBranchById(branchId: string, endpoint?: string): Promise<boolean>;
2
+ export declare function setupBranchVisibilitySync(currentBranchId: string | null | undefined, options?: {
3
+ cookieName?: string;
4
+ endpoint?: string;
5
+ onSynced?: () => void;
6
+ }): () => void;
@@ -0,0 +1,2 @@
1
+ export type ShellProfileAction = "profile" | "change-password" | "logout" | string;
2
+ export declare function handleShellProfileAction(action: ShellProfileAction, auth7UiUrl: string, onLogout: () => void): void;
@@ -0,0 +1,15 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+
3
+ type ProviderLayoutProps = {
4
+ children: ReactNode;
5
+ };
6
+ type SharedShellProviderProps = {
7
+ children: ReactNode;
8
+ Layout: ComponentType<ProviderLayoutProps>;
9
+ };
10
+ type SharedShellProviderOptions = {
11
+ withAuth?: boolean;
12
+ dictionaries?: Partial<Record<"id" | "en", Record<string, string>>>;
13
+ };
14
+ export declare function createShellProvider(options?: SharedShellProviderOptions): ({ children, Layout }: SharedShellProviderProps) => import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,72 @@
1
+ export type Ui7FormDensity = "compact" | "comfortable";
2
+ export type Ui7ThemeDensity = Ui7FormDensity | "normal";
3
+ export interface Ui7FormVisualTokens {
4
+ labelSpacing: "6px";
5
+ labelFontSize: "12px";
6
+ labelFontWeight: 500;
7
+ labelLineHeight: "16px";
8
+ inputFontSize: "14px";
9
+ inputFontWeight: 400;
10
+ inputLineHeight: "20px";
11
+ controlHeight: "40px";
12
+ textareaMinHeight: "88px";
13
+ rowGap: "16px";
14
+ sectionGap: "24px";
15
+ columnGap: "16px";
16
+ readonlyBackground: "#f4f4f4";
17
+ readonlyCursor: "default";
18
+ }
19
+ export declare const UI7_FORM_VISUAL_TOKENS: Ui7FormVisualTokens;
20
+ export declare const UI7_FORM_VISUAL_CSS_VARS: {
21
+ readonly labelSpacing: "--ui7-form-label-gap";
22
+ readonly labelFontSize: "--ui7-form-label-font-size";
23
+ readonly labelFontWeight: "--ui7-form-label-font-weight";
24
+ readonly labelLineHeight: "--ui7-form-label-line-height";
25
+ readonly inputFontSize: "--ui7-form-input-font-size";
26
+ readonly inputFontWeight: "--ui7-form-input-font-weight";
27
+ readonly inputLineHeight: "--ui7-form-input-line-height";
28
+ readonly controlHeight: "--ui7-form-control-height";
29
+ readonly textareaMinHeight: "--ui7-form-textarea-min-height";
30
+ readonly rowGap: "--ui7-form-row-gap";
31
+ readonly sectionGap: "--ui7-form-section-gap";
32
+ readonly columnGap: "--ui7-form-column-gap";
33
+ readonly density: "--ui7-form-density";
34
+ readonly readonlyBackground: "--ui7-form-readonly-background";
35
+ readonly readonlyCursor: "--ui7-form-readonly-cursor";
36
+ };
37
+ export declare const UI7_FORM_VISUAL_CLASSNAMES: {
38
+ readonly scope: "ui7-form-contract";
39
+ readonly densityCompact: "ui7-form-contract--density-compact";
40
+ readonly densityComfortable: "ui7-form-contract--density-comfortable";
41
+ readonly readonly: "ui7-form-contract--readonly";
42
+ readonly row: "ui7-form-row";
43
+ readonly section: "ui7-form-section";
44
+ readonly column: "ui7-form-column";
45
+ readonly label: "ui7-form-label";
46
+ readonly labelRequired: "ui7-form-label__required";
47
+ readonly control: "ui7-form-control";
48
+ readonly controlReadonly: "ui7-form-control--readonly";
49
+ readonly helper: "ui7-form-helper";
50
+ readonly readonlyValue: "ui7-form-readonly-value";
51
+ };
52
+ export declare const UI7_FORM_DENSITY_TOKENS: {
53
+ readonly compact: {
54
+ readonly controlHeight: "2rem";
55
+ readonly rowGap: "0.75rem";
56
+ readonly sectionGap: "1rem";
57
+ readonly columnGap: "0.75rem";
58
+ };
59
+ readonly comfortable: {
60
+ readonly controlHeight: "2.5rem";
61
+ readonly rowGap: "1rem";
62
+ readonly sectionGap: "1.5rem";
63
+ readonly columnGap: "1rem";
64
+ };
65
+ };
66
+ export declare function normalizeUi7FormDensity(density?: Ui7ThemeDensity | null, fallback?: Ui7FormDensity): Ui7FormDensity;
67
+ export declare function getUi7FormDensityClassName(density?: Ui7ThemeDensity | null, fallback?: Ui7FormDensity): string;
68
+ export declare function getUi7FormContractClassName(options?: {
69
+ density?: Ui7ThemeDensity | null;
70
+ readonly?: boolean;
71
+ className?: string | null;
72
+ }): string;
@@ -0,0 +1,39 @@
1
+ export interface QueryParams {
2
+ cursor?: string;
3
+ pageSize?: number;
4
+ sortBy?: string;
5
+ sortDir?: 'asc' | 'desc';
6
+ search?: string;
7
+ filters?: Record<string, unknown>;
8
+ }
9
+ export interface QueryResult<T> {
10
+ data: T[];
11
+ nextCursor?: string;
12
+ prevCursor?: string;
13
+ total?: number;
14
+ }
15
+ export interface OffsetPage<T> {
16
+ data: T[];
17
+ total: number;
18
+ page: number;
19
+ per_page: number;
20
+ total_pages: number;
21
+ }
22
+ export interface OffsetQueryParams {
23
+ page?: string;
24
+ per_page?: string;
25
+ sort_by?: string;
26
+ sort_dir?: 'asc' | 'desc';
27
+ search?: string;
28
+ [key: string]: string | undefined;
29
+ }
30
+ export type ServiceAction = 'get' | 'simpan' | 'hapus';
31
+ export interface ServiceRequest<T = unknown> {
32
+ payload?: T;
33
+ }
34
+ export interface ServiceResponse<T = unknown> {
35
+ ok: boolean;
36
+ data?: T;
37
+ error?: string;
38
+ }
39
+ export type { Notification, Notif7Raw, NotificationListResponse, UnreadCountResponse } from '../types/notification';
@@ -0,0 +1,30 @@
1
+ export interface Notification {
2
+ id: string;
3
+ user_id: string;
4
+ event_type: string;
5
+ title: string;
6
+ message: string;
7
+ is_read: boolean;
8
+ action_url?: string;
9
+ created_at: string;
10
+ }
11
+ export interface Notif7Raw {
12
+ ID: string;
13
+ UserID: string;
14
+ EventType: string;
15
+ Title: string;
16
+ Body: string;
17
+ IsRead: boolean;
18
+ RefURL?: string;
19
+ CreatedAt: string;
20
+ Source?: string;
21
+ Channel?: string;
22
+ }
23
+ export interface NotificationListResponse {
24
+ data: Notification[];
25
+ next_cursor?: string;
26
+ total?: number;
27
+ }
28
+ export interface UnreadCountResponse {
29
+ unread_count: number;
30
+ }
@@ -0,0 +1,2 @@
1
+ declare const apiClient: import('axios').AxiosInstance;
2
+ export default apiClient;
@@ -0,0 +1,9 @@
1
+ export interface WorkflowBackendRequestOptions {
2
+ query?: URLSearchParams;
3
+ body?: string;
4
+ contentType?: string;
5
+ accessToken?: string;
6
+ }
7
+ declare function request(method: string, path: string, options?: WorkflowBackendRequestOptions): Promise<Response>;
8
+ export { request as backendRequest };
9
+ export declare function safeProxyJson(res: Response): Promise<Response>;
@@ -0,0 +1,3 @@
1
+ export declare function fetchDetailData<T>(url: string): Promise<T>;
2
+ export declare function submitWorkflowForm<TPayload>(url: string, method: 'POST' | 'PUT' | 'PATCH', payload: TPayload): Promise<string>;
3
+ export declare function submitWorkflowDelete(url: string): Promise<string>;
@@ -0,0 +1,6 @@
1
+ export interface WorkflowNotif7RequestOptions {
2
+ accessToken?: string;
3
+ query?: URLSearchParams;
4
+ body?: string;
5
+ }
6
+ export declare function notif7Request(method: string, path: string, options?: WorkflowNotif7RequestOptions): Promise<Response>;
@@ -0,0 +1,44 @@
1
+ type CookieReqLike = Request & {
2
+ cookies?: {
3
+ get?: (name: string) => {
4
+ value?: string;
5
+ } | undefined;
6
+ };
7
+ };
8
+ /** Read the user access_token cookie from a Next.js API route request. */
9
+ export declare function getUserAccessToken(req: CookieReqLike | Request): string | undefined;
10
+ /** UUID idempotency key — used as external_ref and embedded in workflow payload. */
11
+ export declare function newWfIdempotencyKey(): string;
12
+ export type StartWorkflowInput = {
13
+ flowId: string;
14
+ payload: Record<string, unknown>;
15
+ externalRef?: string;
16
+ initiatorContext?: {
17
+ user_id?: string;
18
+ branch_id?: string;
19
+ };
20
+ /** User access token dari auth7 — diforward ke workflow7 sebagai Bearer. */
21
+ userAccessToken: string;
22
+ };
23
+ export type StartWorkflowResult = {
24
+ ok: boolean;
25
+ instanceId?: string;
26
+ status?: string;
27
+ error?: string;
28
+ };
29
+ export declare function startWorkflow(input: StartWorkflowInput): Promise<StartWorkflowResult>;
30
+ export type WfStartedBody = {
31
+ instance_id?: string;
32
+ wf_instance_id: string;
33
+ status: string;
34
+ };
35
+ /**
36
+ * Standard 202 envelope for "workflow started" responses.
37
+ * `instanceId` is workflow7's instance UUID (used by SSE RefID match);
38
+ * `idempotencyKey` is the external_ref/wf_instance_id (financing service uses it
39
+ * as upsert idempotency key).
40
+ */
41
+ export declare function respondWfStarted(result: StartWorkflowResult, idempotencyKey: string): Response;
42
+ /** Standard 401 envelope when access_token cookie missing. */
43
+ export declare function respondUnauthorized(): Response;
44
+ export {};
@@ -0,0 +1,27 @@
1
+ import { CrudForm, FormMode } from '../form-types';
2
+
3
+ export type UseCrudFormOptions<TData extends Record<string, unknown>> = {
4
+ schema: CrudForm<TData>;
5
+ mode: FormMode;
6
+ /** Route params used for API path interpolation, e.g. { id: "123" }. Memoize to avoid re-loads. */
7
+ params?: Record<string, string>;
8
+ /** Called with workflow instance_id on successful submit. */
9
+ onSuccess?: (instanceId: string) => void;
10
+ };
11
+ export type UseCrudFormReturn<TData extends Record<string, unknown>> = {
12
+ form: TData;
13
+ setForm: (patch: Partial<TData>) => void;
14
+ loading: boolean;
15
+ saving: boolean;
16
+ error: string | null;
17
+ fieldErrors: Partial<Record<keyof TData, string>>;
18
+ isDirty: boolean;
19
+ page: number;
20
+ pageCount: number;
21
+ isLastPage: boolean;
22
+ canGoPrev: boolean;
23
+ goNext: () => Promise<void>;
24
+ goPrev: () => void;
25
+ handleSave: () => Promise<void>;
26
+ };
27
+ export declare function useCrudForm<TData extends Record<string, unknown>>({ schema, mode, params, onSuccess, }: UseCrudFormOptions<TData>): UseCrudFormReturn<TData>;
@@ -0,0 +1,40 @@
1
+ export type WfNotifEvent = {
2
+ EventType?: string;
3
+ event_type?: string;
4
+ RefID?: string;
5
+ ref_id?: string;
6
+ Payload?: {
7
+ instance_id?: string;
8
+ workflow_id?: string;
9
+ result?: string;
10
+ reason?: string;
11
+ };
12
+ payload?: {
13
+ instance_id?: string;
14
+ workflow_id?: string;
15
+ result?: string;
16
+ reason?: string;
17
+ };
18
+ };
19
+ export type WfCompletionResult = {
20
+ status: 'success' | 'error';
21
+ /** Human-readable detail from payload.result (success) or payload.reason (error). */
22
+ message?: string;
23
+ };
24
+ export type UseWorkflowTrackerOptions = {
25
+ /** SSE endpoint. Default: `/api/notifications/stream`. */
26
+ streamUrl?: string;
27
+ /** Event types treated as "workflow done". Default: all three terminal event types. */
28
+ completionEventTypes?: string[];
29
+ /** Fired when a tracked instance reaches a terminal state. */
30
+ onComplete?: (instanceId: string, result: WfCompletionResult) => void;
31
+ /** Disable subscription (e.g. when the page is not visible). Default: true. */
32
+ enabled?: boolean;
33
+ };
34
+ export type UseWorkflowTrackerReturn = {
35
+ /** Register a workflow instance UUID to watch for completion. */
36
+ track: (instanceId: string | undefined) => void;
37
+ /** Read-only snapshot of currently-tracked instance IDs. */
38
+ pending: () => string[];
39
+ };
40
+ export declare function useWorkflowTracker(options?: UseWorkflowTrackerOptions): UseWorkflowTrackerReturn;
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@isi-ui7/bos7-shared",
3
+ "version": "0.2.0",
4
+ "description": "Shared auth7 and layout primitives for bos7 applications.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/ihsansolusi/ui7.git",
9
+ "directory": "packages/bos7-shared"
10
+ },
11
+ "type": "module",
12
+ "main": "dist/index.js",
13
+ "module": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "exports": {
16
+ ".": "./dist/index.js"
17
+ },
18
+ "publishConfig": {
19
+ "registry": "https://registry.npmjs.org",
20
+ "access": "public"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src"
25
+ ],
26
+ "peerDependencies": {
27
+ "react": ">=18",
28
+ "react-dom": ">=18",
29
+ "next": "16.x",
30
+ "@isi-ui7/corporate-themes": ">=0.2.0",
31
+ "@isi-ui7/i18n": ">=0.1.0",
32
+ "@isi-ui7/ui-shell": ">=0.2.0",
33
+ "@isi-ui7/modal-manager": ">=0.2.0",
34
+ "@isi-ui7/realtime": ">=0.2.0",
35
+ "@carbon/react": "^1.97.0",
36
+ "@carbon/icons-react": "^11.71.0",
37
+ "@isi-ui7/data-table": ">=0.2.0",
38
+ "@isi-ui7/lookup-input": ">=0.2.0"
39
+ },
40
+ "devDependencies": {
41
+ "@eslint/eslintrc": "^3.3.1",
42
+ "@eslint/js": "^9.39.0",
43
+ "@types/react": "^19.0.0",
44
+ "@types/react-dom": "^19.0.0",
45
+ "@typescript-eslint/eslint-plugin": "^8.52.0",
46
+ "@typescript-eslint/parser": "^8.52.0",
47
+ "@vitejs/plugin-react": "^4.2.0",
48
+ "@testing-library/dom": "^10.0.0",
49
+ "@testing-library/react": "^16.0.0",
50
+ "@testing-library/user-event": "^14.5.0",
51
+ "eslint": "9.39.2",
52
+ "eslint-config-prettier": "^9.1.0",
53
+ "eslint-plugin-jsx-a11y": "^6.7.1",
54
+ "eslint-plugin-react": "^7.33.2",
55
+ "eslint-plugin-react-hooks": "^4.6.2",
56
+ "jsdom": "^22.1.0",
57
+ "typescript": "^5.3.3",
58
+ "vite": "^5.0.0",
59
+ "vite-plugin-dts": "^3.6.0",
60
+ "vitest": "^1.0.0",
61
+ "next": "16.1.1",
62
+ "@carbon/react": "^1.97.0",
63
+ "@carbon/icons-react": "^11.71.0",
64
+ "@isi-ui7/i18n": "0.2.0",
65
+ "@isi-ui7/data-table": "0.2.1",
66
+ "@isi-ui7/realtime": "0.2.2",
67
+ "@isi-ui7/corporate-themes": "0.2.2",
68
+ "@isi-ui7/lookup-input": "0.2.1",
69
+ "@isi-ui7/ui-shell": "0.2.2",
70
+ "@isi-ui7/modal-manager": "0.2.1"
71
+ },
72
+ "dependencies": {
73
+ "@carbon/icons-react": "^11.71.0",
74
+ "@carbon/react": "^1.97.0",
75
+ "jose": "^5.9.6",
76
+ "xlsx": "^0.18.5",
77
+ "jspdf": "^4.2.1",
78
+ "jspdf-autotable": "^5.0.7",
79
+ "axios": "^1.7.7",
80
+ "@opentelemetry/sdk-node": "^0.57.0",
81
+ "@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
82
+ "@opentelemetry/auto-instrumentations-node": "^0.57.0",
83
+ "@isi-ui7/data-table": "0.2.1",
84
+ "@isi-ui7/lookup-input": "0.2.1"
85
+ },
86
+ "scripts": {
87
+ "build": "vite build",
88
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
89
+ "test": "vitest run --passWithNoTests",
90
+ "typecheck": "tsc --noEmit"
91
+ }
92
+ }
package/src/api.ts ADDED
@@ -0,0 +1,40 @@
1
+ // Shared API abstraction layer for BOS7 apps.
2
+ // Browser always talks to Next.js BFF routes via relative /api/* path.
3
+
4
+ import type { QueryParams, QueryResult, ServiceAction, ServiceRequest, ServiceResponse } from './types/core';
5
+
6
+ const BASE_URL = '';
7
+
8
+ export async function queryList<T>(domain: string, params: QueryParams): Promise<QueryResult<T>> {
9
+ const res = await fetch(`${BASE_URL}/api/${domain}`, {
10
+ method: 'POST',
11
+ headers: { 'Content-Type': 'application/json' },
12
+ body: JSON.stringify(params),
13
+ });
14
+
15
+ if (!res.ok) {
16
+ throw new Error(`queryList(${domain}) gagal: ${res.status} ${res.statusText}`);
17
+ }
18
+
19
+ return res.json() as Promise<QueryResult<T>>;
20
+ }
21
+
22
+ export async function callService<T>(
23
+ domain: string,
24
+ action: ServiceAction,
25
+ payload?: unknown
26
+ ): Promise<ServiceResponse<T>> {
27
+ const body: ServiceRequest = { payload };
28
+
29
+ const res = await fetch(`${BASE_URL}/api/${domain}/service/${action}`, {
30
+ method: 'POST',
31
+ headers: { 'Content-Type': 'application/json' },
32
+ body: JSON.stringify(body),
33
+ });
34
+
35
+ if (!res.ok) {
36
+ throw new Error(`callService(${domain}, ${action}) gagal: ${res.status} ${res.statusText}`);
37
+ }
38
+
39
+ return res.json() as Promise<ServiceResponse<T>>;
40
+ }