namirasoft-account-react 1.5.37 → 1.5.39

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 (147) hide show
  1. package/.env.template +15 -15
  2. package/SKILL.md +514 -514
  3. package/config-overrides.js +72 -72
  4. package/dist/App.d.ts +1 -1
  5. package/dist/FieldCacheService.js +4 -0
  6. package/dist/FieldCacheService.js.map +1 -1
  7. package/dist/NSARouterMaker.js +1 -1
  8. package/dist/NSARouterMaker.js.map +1 -1
  9. package/dist/Router.d.ts +1 -1
  10. package/dist/UseParams.d.ts +1 -1
  11. package/dist/components/NSAAccessListDialog.d.ts +2 -2
  12. package/dist/components/NSAAccessListDialog.module.css +71 -71
  13. package/dist/components/NSABoxSecret.d.ts +1 -1
  14. package/dist/components/NSABoxSecret.module.css +23 -23
  15. package/dist/components/NSAMasterMenu.d.ts +1 -1
  16. package/dist/components/NSAMasterMenu.js +4 -0
  17. package/dist/components/NSAMasterMenu.js.map +1 -1
  18. package/dist/components/NSAMasterMenu.module.css +250 -250
  19. package/dist/components/NSAMasterMenuItem.d.ts +1 -1
  20. package/dist/components/NSAMessageListDialog.d.ts +1 -1
  21. package/dist/components/NSAMessageListDialog.module.css +99 -99
  22. package/dist/components/NSANavigationGuard.d.ts +1 -1
  23. package/dist/components/NSAProductListDialog.d.ts +1 -1
  24. package/dist/components/NSAProductListDialog.module.css +44 -44
  25. package/dist/components/NSATaskBar.d.ts +1 -1
  26. package/dist/components/NSATaskBar.module.css +57 -57
  27. package/dist/components/NSAUserDialog.d.ts +1 -1
  28. package/dist/components/NSAUserDialog.module.css +67 -67
  29. package/dist/components/NSAWorkspaceListDialog.d.ts +1 -1
  30. package/dist/components/NSAWorkspaceListDialog.module.css +41 -41
  31. package/dist/components/NSFBoxCategoryScope.d.ts +1 -1
  32. package/dist/components/NSFBoxField.d.ts +1 -1
  33. package/dist/components/NSFBoxSlot.d.ts +1 -1
  34. package/dist/components/NSFBoxWrapper.d.ts +1 -1
  35. package/dist/components/NSLabelSecret.d.ts +1 -1
  36. package/dist/components/quickfilter/NSAFilterBoxEnum.module.css +51 -51
  37. package/dist/components/quickfilter/NSAQuickFilterBar.d.ts +1 -1
  38. package/dist/components/quickfilter/NSAQuickFilterBar.module.css +20 -20
  39. package/dist/components/quickfilter/NSAQuickFilterDialog.d.ts +1 -1
  40. package/dist/components/quickfilter/NSAQuickFilterDialog.module.css +74 -74
  41. package/dist/layouts/NSALayout.d.ts +1 -1
  42. package/dist/layouts/NSASectionEdit.d.ts +1 -1
  43. package/dist/layouts/NSASectionEditTabPage.d.ts +1 -1
  44. package/dist/layouts/NSASectionEditTabPage.module.css +4 -4
  45. package/dist/layouts/NSASectionList.d.ts +1 -1
  46. package/dist/layouts/NSASectionList.module.css +24 -24
  47. package/dist/layouts/NSASectionView.d.ts +1 -1
  48. package/dist/layouts/NSASectionViewTabMore.d.ts +1 -1
  49. package/dist/layouts/NSASectionViewTabPage.d.ts +1 -1
  50. package/dist/layouts/NSASectionViewTabPage.module.css +47 -47
  51. package/dist/pages/NSAConsentPage.module.css +26 -26
  52. package/dist/pages/NSAEmailVerificationPage.d.ts +1 -1
  53. package/dist/pages/NSAHomePage.d.ts +1 -1
  54. package/dist/pages/NSAHomePage.module.css +41 -41
  55. package/dist/pages/NSALoginPage.d.ts +1 -1
  56. package/dist/pages/NSALoginPage.module.css +26 -26
  57. package/dist/pages/NSAPhoneVerificationPage.d.ts +1 -1
  58. package/dist/pages/NSAPhoneVerificationPage.js +11 -3
  59. package/dist/pages/NSAPhoneVerificationPage.js.map +1 -1
  60. package/dist/pages/NSAVerificationPage.d.ts +1 -1
  61. package/dist/pages/NSAVerificationPage.module.css +31 -31
  62. package/package.json +94 -94
  63. package/public/index.html +21 -21
  64. package/src/App.tsx +72 -72
  65. package/src/CTFRow.ts +7 -7
  66. package/src/FieldCacheService.tsx +113 -108
  67. package/src/IEntityInfo.ts +33 -33
  68. package/src/NSACacheService.ts +126 -126
  69. package/src/NSAFilterOperators.ts +94 -94
  70. package/src/NSARouterMaker.tsx +141 -141
  71. package/src/NSARouterMakerProps.ts +23 -23
  72. package/src/NSARouterMakerResult.ts +24 -24
  73. package/src/NSFRouterMaker.tsx +35 -35
  74. package/src/NSFRouterProps.ts +7 -7
  75. package/src/NSFRoutes.ts +30 -30
  76. package/src/Router.tsx +48 -48
  77. package/src/UseParams.tsx +17 -17
  78. package/src/components/NSAAccessListDialog.module.css +71 -71
  79. package/src/components/NSAAccessListDialog.tsx +178 -178
  80. package/src/components/NSABoxSecret.module.css +23 -23
  81. package/src/components/NSABoxSecret.tsx +264 -264
  82. package/src/components/NSAMasterMenu.module.css +250 -250
  83. package/src/components/NSAMasterMenu.tsx +174 -169
  84. package/src/components/NSAMasterMenuItem.tsx +250 -250
  85. package/src/components/NSAMessageListDialog.module.css +99 -99
  86. package/src/components/NSAMessageListDialog.tsx +144 -144
  87. package/src/components/NSANavigationGuard.tsx +79 -79
  88. package/src/components/NSAProductListDialog.module.css +44 -44
  89. package/src/components/NSAProductListDialog.tsx +88 -88
  90. package/src/components/NSATable.tsx +494 -494
  91. package/src/components/NSATaskBar.module.css +57 -57
  92. package/src/components/NSATaskBar.tsx +193 -193
  93. package/src/components/NSAUserDialog.module.css +67 -67
  94. package/src/components/NSAUserDialog.tsx +122 -122
  95. package/src/components/NSAWorkspaceListDialog.module.css +41 -41
  96. package/src/components/NSAWorkspaceListDialog.tsx +147 -147
  97. package/src/components/NSFBoxCategoryScope.tsx +52 -52
  98. package/src/components/NSFBoxField.tsx +37 -37
  99. package/src/components/NSFBoxSlot.tsx +73 -73
  100. package/src/components/NSFBoxWrapper.tsx +69 -69
  101. package/src/components/NSLabelSecret.tsx +37 -37
  102. package/src/components/quickfilter/NSAFilterBoxBase.tsx +110 -110
  103. package/src/components/quickfilter/NSAFilterBoxBoolean.tsx +72 -72
  104. package/src/components/quickfilter/NSAFilterBoxDate.tsx +110 -110
  105. package/src/components/quickfilter/NSAFilterBoxDateTime.tsx +110 -110
  106. package/src/components/quickfilter/NSAFilterBoxEnum.module.css +51 -51
  107. package/src/components/quickfilter/NSAFilterBoxEnum.tsx +103 -103
  108. package/src/components/quickfilter/NSAFilterBoxNumber.tsx +145 -145
  109. package/src/components/quickfilter/NSAFilterBoxString.tsx +83 -83
  110. package/src/components/quickfilter/NSAFilterBoxTime.tsx +106 -106
  111. package/src/components/quickfilter/NSAQuickFilterBar.module.css +20 -20
  112. package/src/components/quickfilter/NSAQuickFilterBar.tsx +142 -142
  113. package/src/components/quickfilter/NSAQuickFilterDialog.module.css +74 -74
  114. package/src/components/quickfilter/NSAQuickFilterDialog.tsx +297 -297
  115. package/src/css.d.ts +13 -13
  116. package/src/formatters/SecretFormatter.tsx +24 -24
  117. package/src/index.tsx +27 -27
  118. package/src/layouts/Actions.ts +146 -146
  119. package/src/layouts/CFTUtil.ts +18 -18
  120. package/src/layouts/NSALayout.tsx +750 -750
  121. package/src/layouts/NSALayout_Field.ts +125 -125
  122. package/src/layouts/NSASectionEdit.tsx +279 -279
  123. package/src/layouts/NSASectionEditTabPage.module.css +4 -4
  124. package/src/layouts/NSASectionEditTabPage.tsx +256 -256
  125. package/src/layouts/NSASectionList.module.css +24 -24
  126. package/src/layouts/NSASectionList.tsx +165 -165
  127. package/src/layouts/NSASectionView.tsx +98 -98
  128. package/src/layouts/NSASectionViewTabMore.tsx +78 -78
  129. package/src/layouts/NSASectionViewTabPage.module.css +47 -47
  130. package/src/layouts/NSASectionViewTabPage.tsx +400 -400
  131. package/src/main.ts +52 -52
  132. package/src/pages/NSAConsentPage.module.css +26 -26
  133. package/src/pages/NSAConsentPage.tsx +120 -120
  134. package/src/pages/NSAEmailVerificationPage.tsx +30 -30
  135. package/src/pages/NSAHomePage.module.css +41 -41
  136. package/src/pages/NSAHomePage.tsx +113 -113
  137. package/src/pages/NSALoginPage.module.css +26 -26
  138. package/src/pages/NSALoginPage.tsx +87 -87
  139. package/src/pages/NSAPhoneVerificationPage.tsx +106 -94
  140. package/src/pages/NSAVerificationPage.module.css +31 -31
  141. package/src/pages/NSAVerificationPage.tsx +185 -185
  142. package/src/pages/NSFCategoryEditPage.tsx +87 -87
  143. package/src/pages/NSFCategoryListPage.tsx +23 -23
  144. package/src/pages/NSFCategoryViewPage.tsx +23 -23
  145. package/src/pages/NSFEntities.ts +58 -58
  146. package/src/pages/PaymentRequired.tsx +80 -80
  147. package/tsconfig.json +43 -43
@@ -1,751 +1,751 @@
1
- import { NamirasoftAccountClient } from 'namirasoft-account-client';
2
- import { ProductFullRow } from 'namirasoft-api-product';
3
- import { CacheService, EnvService, IStorageLocal, NamingConvention } from 'namirasoft-core';
4
- import { NamirasoftMessageServer } from 'namirasoft-message';
5
- import { NamirasoftMap } from 'namirasoft-site-map';
6
- import { IBackgroundProps, IBaseComponentProps, IHeaderIconProps, IHeaderRightProps, NSBarActionProps, NSBarAlertProps, NSBarTitleProps, NSDialogDelete, NSLayout, NSLoading, ProductCacheService } from 'namirasoft-site-react';
7
- import { IHeaderLeftProps } from 'namirasoft-site-react/dist/props/IHeaderLeftProps';
8
- import { Component, createRef, ReactElement } from 'react';
9
- import { Helmet } from 'react-helmet';
10
- import { NSAAccessListDialog } from '../components/NSAAccessListDialog';
11
- import NSAMasterMenu from '../components/NSAMasterMenu';
12
- import { NSAMessageListDialog } from '../components/NSAMessageListDialog';
13
- import { NSAProductListDialog } from '../components/NSAProductListDialog';
14
- import { NSATaskBar } from '../components/NSATaskBar';
15
- import { NSAUserDialog } from '../components/NSAUserDialog';
16
- import { NSAWorkspaceListDialog } from '../components/NSAWorkspaceListDialog';
17
- import { FieldCacheService } from '../FieldCacheService';
18
- import { IEntityInfo } from '../IEntityInfo';
19
- import { NSACacheService } from '../NSACacheService';
20
- import { NSARouterMakerResult } from '../NSARouterMakerResult';
21
- import { UseParams } from '../UseParams';
22
- import { Actions } from './Actions';
23
- import { NSALayout_Field } from './NSALayout_Field';
24
- import { NSASectionEdit, NSASectionEditCustomProps } from './NSASectionEdit';
25
- import { NSASectionList, NSASectionListCustomProps } from './NSASectionList';
26
- import { NSASectionView, NSASectionViewCutomProps } from './NSASectionView';
27
-
28
- let REACT_APP_BASE_URL_ACCOUNT_CONSOLE = new EnvService("REACT_APP_BASE_URL_ACCOUNT_CONSOLE").getString();
29
- let client = new NamirasoftAccountClient(REACT_APP_BASE_URL_ACCOUNT_CONSOLE);
30
- client.onLoggedin = () => { window.location.reload(); };
31
- client.onLoggedout = () => { window.location.reload(); };
32
-
33
- export interface NSALayoutPagesProps<RowType extends { id: string }, RowTypeInput = RowType>
34
- {
35
- entity: IEntityInfo<RowType, RowTypeInput>;
36
- ui?: {
37
- action?: {
38
- menus: {
39
- _new?: boolean;
40
- list?: boolean;
41
- view?: boolean;
42
- view_history?: boolean;
43
- copy?: boolean;
44
- edit?: boolean;
45
- delete?: boolean;
46
- apply?: boolean;
47
- };
48
- children?: JSX.Element | JSX.Element[];
49
- };
50
- }
51
- list?: NSASectionListCustomProps<RowType, RowTypeInput>;
52
- edit?: NSASectionEditCustomProps<RowType, RowTypeInput> & {
53
- ui: {
54
- action?: {
55
- menus: {
56
- apply?: boolean,
57
- }
58
- },
59
- }
60
- };
61
- view?: NSASectionViewCutomProps<RowType>;
62
- }
63
- export interface NSALayoutCustomProps<RowType extends { id: string }, RowTypeInput = RowType> extends IBaseComponentProps, NSARouterMakerResult
64
- {
65
- action?: NSBarActionProps;
66
- title?: NSBarTitleProps;
67
- alerts?: NSBarAlertProps[];
68
- pages?: NSALayoutPagesProps<RowType, RowTypeInput>;
69
- children: ReactElement;
70
- onEnterPressed?: () => void;
71
- onDidMounted?: () => void;
72
- }
73
- export interface NSALayoutProps<RowType extends { id: string }, RowTypeInput = RowType> extends NSALayoutCustomProps<RowType, RowTypeInput>
74
- {
75
- model?: {
76
- id: string | null;
77
- }
78
- scope?: string;
79
- logo?: string;
80
- master_menu?: boolean;
81
- background?: IBackgroundProps;
82
- header?: {
83
- enabled?: boolean,
84
- right?: IHeaderRightProps;
85
- left?: IHeaderLeftProps;
86
- built_in?: {
87
- logout?: boolean;
88
- message?: boolean;
89
- access?: boolean;
90
- workspace?: boolean;
91
- product?: boolean;
92
- user?: boolean;
93
- };
94
- mega_menu?: boolean;
95
- };
96
- }
97
-
98
- interface NSALayoutState
99
- {
100
- parmas: any | null;
101
- product: ProductFullRow | null;
102
- refresher: number;
103
- message_count: number;
104
- showDeleteDialog: boolean;
105
- delete_ids: string[];
106
- delete_title: string;
107
- showMessageListDialog: boolean;
108
- showAccessListDialog: boolean;
109
- showProductListDialog: boolean;
110
- showWorkspaceListDialog: boolean;
111
- showUserDialog: boolean;
112
- showMasterMenu: boolean;
113
- }
114
-
115
- export class NSALayout<RowType extends { id: string }, RowTypeInput = RowType> extends Component<NSALayoutProps<RowType, RowTypeInput>, NSALayoutState>
116
- {
117
- private onDidMounted_Called: boolean = false;
118
- private onDidMounted_Ready: boolean = false;
119
- private hasCFT: boolean;
120
- private isLoggedIn: boolean;
121
- private cache_product: CacheService<ProductFullRow>;
122
- private cache_message: CacheService<number>;
123
- private cache_master_menu: CacheService<{ open: boolean }>;
124
- NSASectionList_Ref = createRef<NSASectionList<RowType, RowTypeInput>>();
125
- NSASectionEdit_Ref = createRef<NSASectionEdit<RowType, RowTypeInput>>();
126
- NSASectionView_Ref = createRef<NSASectionView<RowType, RowTypeInput>>();
127
- NSATaskBar_Ref = createRef<NSATaskBar<string>>();
128
-
129
- constructor(props: NSALayoutProps<RowType, RowTypeInput>)
130
- {
131
- super(props);
132
-
133
- // Account
134
- this.isLoggedIn = this.props.account?.token_manager?.exists() ?? false;
135
-
136
- // CFT
137
- this.hasCFT = false;
138
- if (this.props.pages?.entity)
139
- try
140
- {
141
- let namirasoft = new NamirasoftMap(this.props.account.token_manager, props.notifier.onError);
142
- let table = namirasoft.getByProductID(this.props.product_id).tables[this.props.pages.entity.name];
143
- this.hasCFT = table.hasFieldTables();
144
- } catch (error)
145
- { }
146
-
147
- // Cache Product
148
- this.cache_product = ProductCacheService.get(this.props.product_id, console.error);
149
-
150
- // Cache Message
151
- this.cache_message = new CacheService<number>(
152
- "ns-messsage-count",
153
- new IStorageLocal(),
154
- 15,
155
- async () => "1.0.0",
156
- async () =>
157
- {
158
- let REACT_APP_BASE_URL_MESSAGE = new EnvService("REACT_APP_BASE_URL_MESSAGE", true).getString();
159
- let server = new NamirasoftMessageServer(REACT_APP_BASE_URL_MESSAGE, this.props.account.token_manager, console.error);
160
- let res = await server.message.GetNewCount();
161
- return res.count;
162
- }
163
- );
164
-
165
- this.cache_master_menu = NSACacheService.getMasterMenuStatus();
166
- this.cache_message.onExpired = () => { this.reloadMessageCount(true); };
167
-
168
- // State
169
- this.state = {
170
- parmas: null,
171
- product: null,
172
- refresher: 0,
173
- message_count: 0,
174
- showDeleteDialog: false,
175
- delete_ids: [],
176
- delete_title: "",
177
- showMessageListDialog: false,
178
- showAccessListDialog: false,
179
- showProductListDialog: false,
180
- showWorkspaceListDialog: false,
181
- showUserDialog: false,
182
- showMasterMenu: false,
183
- };
184
- }
185
- public reloadMessageCount(force: boolean)
186
- {
187
- if (force)
188
- this.cache_message.del();
189
- this.cache_message.get().then(count =>
190
- {
191
- this.setState(prev => ({ ...prev, message_count: count }));
192
- }).catch(() => { });
193
- }
194
-
195
- private async runDelete(ids: string[], name: string): Promise<void>
196
- {
197
- if (!this.props.pages?.entity.server.delete)
198
- return;
199
-
200
- await new Promise<void>(resolve => this.setState(prev => ({ ...prev, delete_ids: ids, delete_title: `Deleting ${name}` }), resolve));
201
-
202
- const result = await this.NSATaskBar_Ref.current?.run();
203
- if (!result)
204
- return;
205
-
206
- const deletedIds = result.succeeded;
207
-
208
- if (deletedIds.length === 0)
209
- return;
210
-
211
- if (this.props.pages?.list)
212
- {
213
- let table = this.NSASectionList_Ref.current?.NSATable_Main.current;
214
- if (table)
215
- {
216
- let selected_ids = table.getSelectedIDs().filter(id => !deletedIds.includes(id));
217
- let selected_rows = table.getSelectedRows().filter(row => !deletedIds.includes(row.id));
218
- table.setSelectedIDs(selected_ids, selected_rows);
219
- table.reload(null, null);
220
- }
221
- }
222
- else if (this.props.pages?.edit)
223
- {
224
- if (this.props.pages.entity.client.getListURL)
225
- this.props.url.redirect(this.props.pages.entity.client.getListURL(), {});
226
- else if (this.props.pages.entity.client.getViewURL)
227
- this.props.url.redirect(this.props.pages.entity.client.getViewURL(deletedIds[0]), {});
228
- }
229
- else if (this.props.pages?.view)
230
- {
231
- if (this.props.pages.entity.client.getListURL)
232
- this.props.url.redirect(this.props.pages.entity.client.getListURL(), {});
233
- }
234
- }
235
-
236
- override componentDidMount(): void
237
- {
238
- this.onDidMounted_Called = false;
239
- this.cache_product.get().then(product =>
240
- {
241
- this.setState(prev => ({ ...prev, product }));
242
- })
243
-
244
- this.cache_master_menu.get().then(status =>
245
- {
246
- this.setState(() => ({ showMasterMenu: status.open }));
247
- })
248
-
249
- if (this.isLoggedIn)
250
- this.reloadMessageCount(false);
251
-
252
- client.start();
253
- }
254
- override componentDidUpdate(): void
255
- {
256
- if (!this.onDidMounted_Called)
257
- if (this.onDidMounted_Ready)
258
- {
259
- this.props.onDidMounted?.();
260
- this.onDidMounted_Called = true;
261
- }
262
- }
263
- override componentWillUnmount(): void
264
- {
265
- client.stop();
266
- }
267
- override render()
268
- {
269
- this.onDidMounted_Ready = false;
270
- let id = this.props.model?.id ?? null;
271
- if (!id)
272
- {
273
- if (this.state.parmas === null)
274
- return <UseParams onLoaded={parmas =>
275
- {
276
- this.setState({ parmas });
277
- }} />;
278
- id = this.state.parmas.id;
279
- }
280
-
281
- const hideDeleteDialog = async () => { this.setState(prev => ({ ...prev, showDeleteDialog: false })); };
282
- const showDeleteDialog = async () => { this.setState(prev => ({ ...prev, showDeleteDialog: true })); };
283
-
284
- let icons: IHeaderIconProps[] = [];
285
-
286
- if (this.isLoggedIn)
287
- {
288
- if (this.props.header?.built_in?.message ?? true)
289
- icons.push({
290
- src: "https://static.namirasoft.com/image/namirasoft/message/logo/base.png", alt: "Message", onClicked: () =>
291
- {
292
- this.setState(prev => ({ ...prev, showMessageListDialog: true }));
293
- this.reloadMessageCount(true);
294
- },
295
- children: <>
296
- {
297
- (
298
- this.state.showMessageListDialog &&
299
- <NSAMessageListDialog {...this.props}
300
- onClose={() => { this.setState(prev => ({ ...prev, showMessageListDialog: false })); }}
301
- onSeenAll={() =>
302
- {
303
- this.reloadMessageCount(true);
304
- }}
305
- onMessageClick={() =>
306
- {
307
- this.reloadMessageCount(true);
308
- }}
309
- />
310
- )
311
- }
312
- </>,
313
- count: this.state.message_count,
314
- tooltip: "Message"
315
- });
316
- }
317
- if (this.isLoggedIn)
318
- if (this.props.header?.built_in?.access ?? true)
319
- icons.push({
320
- src: "https://static.namirasoft.com/image/namirasoft/access/logo/base.png", alt: "Access", onClicked: () =>
321
- {
322
- this.setState(prev => ({ ...prev, showAccessListDialog: true }));
323
- },
324
- children: <>
325
- {
326
- (this.state.showAccessListDialog) && <NSAAccessListDialog {...this.props} onClose={() =>
327
- {
328
- this.setState(prev => ({ ...prev, showAccessListDialog: false }));
329
- }} />
330
- }
331
- </>,
332
- tooltip: "Access"
333
- });
334
- if (this.isLoggedIn)
335
- if (this.props.header?.built_in?.workspace ?? true)
336
- icons.push({
337
- src: "https://static.namirasoft.com/image/namirasoft/workspace/logo/base.png", alt: "Workspace", onClicked: () =>
338
- {
339
- this.setState(prev => ({ ...prev, showWorkspaceListDialog: true }));
340
- },
341
- children: <>
342
- {
343
- (this.state.showWorkspaceListDialog) && <NSAWorkspaceListDialog {...this.props} onClose={() =>
344
- {
345
- this.setState(prev => ({ ...prev, showWorkspaceListDialog: false }));
346
- }} />
347
- }
348
- </>,
349
- tooltip: "Workspace List"
350
- });
351
- if (this.props.header?.built_in?.product ?? true)
352
- icons.push({
353
- src: "https://static.namirasoft.com/image/concept/product-list/white.svg", alt: "Product", onClicked: () =>
354
- {
355
- this.setState(prev => ({ ...prev, showProductListDialog: true }));
356
- },
357
- children: <>
358
- {
359
- (this.state.showProductListDialog) && <NSAProductListDialog {...this.props} onClose={() =>
360
- {
361
- this.setState(prev => ({ ...prev, showProductListDialog: false }));
362
- }} >
363
- <></>
364
- </NSAProductListDialog>
365
- }
366
- </>,
367
- tooltip: "Product List"
368
- });
369
-
370
- if (this.isLoggedIn)
371
- if (this.props.header?.built_in?.user ?? true)
372
- icons.push({
373
- src: "https://static.namirasoft.com/image/concept/user/white.svg",
374
- alt: "User",
375
- onClicked: () =>
376
- {
377
- this.setState(prev => ({ ...prev, showUserDialog: true }));
378
- },
379
- children: <>
380
- {
381
- (this.state.showUserDialog) &&
382
- <NSAUserDialog
383
- {...this.props}
384
- client={client}
385
- onClose={() => this.setState(prev => ({ ...prev, showUserDialog: false }))}
386
- >
387
- <></>
388
- </NSAUserDialog>
389
- }
390
- </>,
391
- tooltip: "User"
392
- });
393
-
394
- let name = "";
395
- if (this.props.pages)
396
- name = NamingConvention.lower_case_underscore.convert(this.props.pages.entity.name, NamingConvention.Pascal_Case_Space);
397
-
398
- let getIDs: (() => string[]) | null = null;
399
- let getRows: (() => RowType[]) | null = null;
400
- if (this.props.pages?.list)
401
- {
402
- getIDs = () => { return this.NSASectionList_Ref.current?.getSelectedIDs() ?? []; };
403
- getRows = () => { return this.NSASectionList_Ref.current?.getSelectedRows() ?? []; };
404
- }
405
- if (this.props.pages?.edit)
406
- {
407
- getIDs = () => [id].filter(id => id) as string[];
408
- getRows = () => [this.NSASectionEdit_Ref.current?.getRow()].filter(row => row) as RowType[];
409
- }
410
- if (this.props.pages?.view)
411
- {
412
- getIDs = () => [id].filter(id => id) as string[];
413
- getRows = () => [this.NSASectionView_Ref.current?.getRow()].filter(row => row) as RowType[];
414
- }
415
-
416
- let action: NSBarActionProps | undefined = { title: "", description: "", menus: {} };
417
- if (this.props.action?.title)
418
- action.title = this.props.action.title;
419
- if (this.props.action?.description)
420
- action.description = this.props.action.description;
421
- if (this.props.action?.menus != null)
422
- action.menus = { ...this.props.action?.menus };
423
-
424
- if (getIDs && getRows)
425
- if (this.props.pages?.entity.client.getMenus)
426
- {
427
- let menus = this.props.pages?.entity.client.getMenus(getIDs, getRows);
428
- for (let key of Object.keys(menus))
429
- {
430
- if (!action.menus)
431
- action.menus = {};
432
- if (!action.menus[key])
433
- action.menus[key] = [];
434
- action.menus[key].push(...menus[key]);
435
- }
436
- }
437
-
438
- let name_apply = "Apply";
439
- let name_actions = "Actions";
440
- if (!action.menus)
441
- action.menus = {};
442
- action.menus[name_apply] = [];
443
- action.menus[name_actions] = [];
444
-
445
- if (this.props.pages?.edit)
446
- {
447
- if (!action.title)
448
- action.title = (this.props.pages.edit.isEdit ? "Edit" : "New") + " " + name;
449
-
450
- // Menu
451
- if (this.props.pages.edit.ui.action?.menus.apply ?? true)
452
- action.menus[name_apply].push(Actions.apply({
453
- action: (onFinished) =>
454
- {
455
- if (this.props.pages?.edit)
456
- this.NSASectionEdit_Ref.current?.onApply(onFinished);
457
- }
458
- }));
459
- }
460
- else if (this.props.pages)
461
- {
462
- if (this.props.pages.entity.client.getNewURL)
463
- if (this.props.pages.entity.server.create)
464
- if (this.props.pages.ui?.action?.menus._new ?? true)
465
- action.menus[name_apply].push(Actions._new(this.props, this.props.pages.entity));
466
- }
467
-
468
- if (getIDs)
469
- if (this.props.pages)
470
- {
471
- if (this.props.pages.entity.client.getListURL)
472
- if (this.props.pages.entity.server.list)
473
- if (this.props.pages.ui?.action?.menus.list ?? true)
474
- action.menus[name_actions].push(Actions.list(this.props, this.props.pages.entity));
475
-
476
- if (this.props.pages.entity.client.getViewURL)
477
- if (this.props.pages.entity.server.get)
478
- if (this.props.pages.ui?.action?.menus.view ?? true)
479
- action.menus[name_actions].push(Actions.view(this.props, this.props.pages.entity, getIDs));
480
-
481
- if (this.props.pages.ui?.action?.menus.view_history ?? true)
482
- action.menus[name_actions].push(Actions.viewHistory(getIDs));
483
-
484
- if (this.props.pages.entity.client.getCopyURL)
485
- if (this.props.pages.entity.server.create)
486
- if (this.props.pages.ui?.action?.menus.copy ?? true)
487
- action.menus[name_actions].push(Actions.copy(this.props, this.props.pages.entity, getIDs));
488
-
489
- if (this.props.pages.entity.client.getEditURL)
490
- if (this.props.pages.entity.server.update)
491
- if (this.props.pages.ui?.action?.menus.edit ?? true)
492
- action.menus[name_actions].push(Actions.edit(this.props, this.props.pages.entity, getIDs));
493
-
494
- if (this.props.pages.entity.server.delete)
495
- if (this.props.pages.ui?.action?.menus.delete ?? true)
496
- action.menus[name_actions].push(Actions.delete(getIDs, {
497
- action: showDeleteDialog,
498
- showLoading: false
499
- }));
500
- }
501
-
502
- let action_name = "";
503
- let content;
504
- if (this.props.pages?.list)
505
- {
506
- action_name = "List";
507
- content = <FieldCacheService
508
- {...this.props}
509
- render={rows =>
510
- {
511
- if (this.props.pages?.list)
512
- return <NSASectionList<RowType, RowTypeInput>
513
- ref={this.NSASectionList_Ref}
514
- {...this.props}
515
- {...this.props.pages.list}
516
- entity={this.props.pages.entity}
517
- onIDsChanged={() =>
518
- {
519
- this.setState(prev => ({ ...prev }));
520
- this.props.pages?.list?.onIDsChanged?.();
521
- }}
522
- ctf={{
523
- enabled: this.hasCFT,
524
- rows
525
- }}
526
- >
527
- {this.props.children}
528
- </NSASectionList>;
529
- return <></>;
530
- }}
531
- />;
532
- }
533
- else if (this.props.pages?.edit)
534
- {
535
- if (this.props.pages.edit.isEdit)
536
- action_name = "Edit";
537
- else
538
- action_name = "New";
539
- content = <FieldCacheService
540
- {...this.props}
541
- render={rows =>
542
- {
543
- if (this.props.pages?.edit)
544
- return <NSASectionEdit
545
- ref={this.NSASectionEdit_Ref}
546
- {...this.props}
547
- {...this.props.pages.edit}
548
- entity={this.props.pages.entity}
549
- ctf={{
550
- enabled: this.hasCFT,
551
- rows
552
- }}
553
- onRowLoaded={(row) =>
554
- {
555
- this.setState(prev => ({ ...prev }), () =>
556
- {
557
- this.props.pages?.edit?.onRowLoaded?.(row);
558
- });
559
- }}
560
- model={{
561
- id,
562
- row: null
563
- }}
564
- >
565
- {this.props.children}
566
- </NSASectionEdit>;
567
- return <></>;
568
- }}
569
- />;
570
- }
571
- else if (this.props.pages?.view)
572
- {
573
- action_name = "View";
574
-
575
- content = <FieldCacheService
576
- {...this.props}
577
- render={rows =>
578
- {
579
- if (this.props.pages?.view)
580
- return <NSASectionView
581
- ref={this.NSASectionView_Ref}
582
- {...this.props}
583
- {...this.props.pages.view}
584
- entity={this.props.pages.entity}
585
- ctf={{
586
- enabled: this.hasCFT,
587
- rows
588
- }}
589
- onRowLoaded={(row) =>
590
- {
591
- this.setState(prev => ({ ...prev }), () =>
592
- {
593
- this.props.pages?.view?.onRowLoaded?.(row);
594
- });
595
- }}
596
- model={{
597
- id,
598
- row: null
599
- }}
600
- >
601
- {this.props.children}
602
- </NSASectionView>;
603
- return <></>;
604
- }}
605
- />;
606
- }
607
- else
608
- content = this.props.children;
609
-
610
- let fullname = "";
611
- if (this.props.pages?.entity.name)
612
- {
613
- let Name_Pascal_Space = NamingConvention.lower_case_underscore.convert(this.props.pages.entity.name, NamingConvention.Pascal_Case_Space);
614
- if (action_name === "List")
615
- fullname = Name_Pascal_Space + " " + action_name;
616
- else
617
- fullname = action_name + " " + Name_Pascal_Space;
618
- }
619
-
620
- if (!this.state.product)
621
- return <NSLoading title='Loading Namirasoft Product ...' />;
622
-
623
- let action_title = action?.title ?? "";
624
- if (!action_title)
625
- action_title = fullname;
626
- if (!action_title)
627
- action_title = this.state.product?.name
628
-
629
- let action_description = action?.description ?? "";
630
- if (!action_description)
631
- action_description = this.state.product?.description ?? "";
632
-
633
- let scope = this.props.scope ?? `${this.state.product.name} Console`;
634
- let logo = this.props.logo ?? this.state.product.logo ?? "";
635
- let background = this.props.background?.image ?? this.state.product.background ?? "";
636
-
637
- this.onDidMounted_Ready = true;
638
-
639
- let user_id = this.props.account.token_manager.getUserData(x => x.id, "");
640
- let user_first_name = this.props.account.token_manager.getUserData(x => x.first_name, "");
641
- let user_last_name = this.props.account.token_manager.getUserData(x => x.last_name, "");
642
- let user_email = this.props.account.token_manager.getUserData(x => x.email, "");
643
- let user_phone = this.props.account.token_manager.getUserData(x => x.phone ?? "", "");
644
- return (
645
- <div className="d-flex">
646
- {
647
- (this.props.master_menu ?? true) &&
648
- <NSAMasterMenu
649
- {...this.props}
650
- open={this.state.showMasterMenu}
651
- setOpen={(open) =>
652
- {
653
- this.cache_master_menu.set({ open });
654
- this.setState(() => ({ showMasterMenu: open }));
655
- }}
656
- />
657
- }
658
- <FieldCacheService
659
- {...this.props}
660
- render={(ctf_rows) => (
661
- <NSLayout
662
- {...this.props}
663
- scope={scope}
664
- logo={logo}
665
- background={{ image: background, attachment: this.props.background?.attachment, size: this.props.background?.size }}
666
- header={{
667
- enabled: this.props.header?.enabled,
668
- right: {
669
- icons: [...icons, ...(this.props.header?.right?.icons ?? [])],
670
- },
671
- left: {
672
- title: this.props.header?.left?.title
673
- },
674
- mega_menu: this.props.header?.mega_menu,
675
- onCollapse: () =>
676
- {
677
- this.setState((prev) => ({ showMasterMenu: !prev.showMasterMenu }))
678
- },
679
- filters_postprocessor: (filters) => NSALayout_Field.postprocess(filters, ctf_rows),
680
- }}
681
- action={{
682
- title: action_title,
683
- description: action_description,
684
- menus: action.menus,
685
- children: this.props.action?.children
686
- }}
687
- isLoggedIn={this.props.account?.token_manager?.exists}
688
- notifications={this.props.notifier.getNotifications()}
689
- >
690
- <Helmet>
691
- <meta name="ns-account-user-id" content={user_id} />
692
- <meta name="ns-account-user-first-name" content={user_first_name} />
693
- <meta name="ns-account-user-last-name" content={user_last_name} />
694
- <meta name="ns-account-user-email" content={user_email} />
695
- <meta name="ns-account-user-phone" content={user_phone} />
696
- </Helmet>
697
- {
698
- this.state.showDeleteDialog &&
699
- <NSDialogDelete
700
- title={"Delete Confirmation"}
701
- description={
702
- (getIDs && getIDs().length > 1)
703
- ? `Are you sure you want to delete ${getIDs().length} ${name}s?`
704
- : `Are you sure you want to delete ${name}?`
705
- }
706
- onClose={hideDeleteDialog}
707
- onNo={{ action: hideDeleteDialog, showLoading: false }}
708
- show_as_code={false}
709
- onYes={{
710
- showLoading: false,
711
- action: (onFinished) =>
712
- {
713
- try
714
- {
715
- let ids = getIDs ? getIDs() : [];
716
- if (ids.length === 0)
717
- throw new Error("Nothing was found to delete.");
718
- this.runDelete(ids, name);
719
- }
720
- catch (error: any)
721
- {
722
- this.props.notifier.onError(error);
723
- }
724
- this.setState(prev => ({ ...prev, showDeleteDialog: false }));
725
- onFinished();
726
- }
727
- }}
728
- >
729
- <></>
730
- </NSDialogDelete>}
731
- {
732
- content
733
- }
734
- </NSLayout>
735
- )}
736
- />
737
- <NSATaskBar<string>
738
- ref={this.NSATaskBar_Ref}
739
- title={this.state.delete_title}
740
- items={this.state.delete_ids}
741
- handler={async (id) =>
742
- {
743
- const del = this.props.pages?.entity.server.delete;
744
- if (del)
745
- await del(id);
746
- }}
747
- />
748
- </div >
749
- );
750
- }
1
+ import { NamirasoftAccountClient } from 'namirasoft-account-client';
2
+ import { ProductFullRow } from 'namirasoft-api-product';
3
+ import { CacheService, EnvService, IStorageLocal, NamingConvention } from 'namirasoft-core';
4
+ import { NamirasoftMessageServer } from 'namirasoft-message';
5
+ import { NamirasoftMap } from 'namirasoft-site-map';
6
+ import { IBackgroundProps, IBaseComponentProps, IHeaderIconProps, IHeaderRightProps, NSBarActionProps, NSBarAlertProps, NSBarTitleProps, NSDialogDelete, NSLayout, NSLoading, ProductCacheService } from 'namirasoft-site-react';
7
+ import { IHeaderLeftProps } from 'namirasoft-site-react/dist/props/IHeaderLeftProps';
8
+ import { Component, createRef, ReactElement } from 'react';
9
+ import { Helmet } from 'react-helmet';
10
+ import { NSAAccessListDialog } from '../components/NSAAccessListDialog';
11
+ import NSAMasterMenu from '../components/NSAMasterMenu';
12
+ import { NSAMessageListDialog } from '../components/NSAMessageListDialog';
13
+ import { NSAProductListDialog } from '../components/NSAProductListDialog';
14
+ import { NSATaskBar } from '../components/NSATaskBar';
15
+ import { NSAUserDialog } from '../components/NSAUserDialog';
16
+ import { NSAWorkspaceListDialog } from '../components/NSAWorkspaceListDialog';
17
+ import { FieldCacheService } from '../FieldCacheService';
18
+ import { IEntityInfo } from '../IEntityInfo';
19
+ import { NSACacheService } from '../NSACacheService';
20
+ import { NSARouterMakerResult } from '../NSARouterMakerResult';
21
+ import { UseParams } from '../UseParams';
22
+ import { Actions } from './Actions';
23
+ import { NSALayout_Field } from './NSALayout_Field';
24
+ import { NSASectionEdit, NSASectionEditCustomProps } from './NSASectionEdit';
25
+ import { NSASectionList, NSASectionListCustomProps } from './NSASectionList';
26
+ import { NSASectionView, NSASectionViewCutomProps } from './NSASectionView';
27
+
28
+ let REACT_APP_BASE_URL_ACCOUNT_CONSOLE = new EnvService("REACT_APP_BASE_URL_ACCOUNT_CONSOLE").getString();
29
+ let client = new NamirasoftAccountClient(REACT_APP_BASE_URL_ACCOUNT_CONSOLE);
30
+ client.onLoggedin = () => { window.location.reload(); };
31
+ client.onLoggedout = () => { window.location.reload(); };
32
+
33
+ export interface NSALayoutPagesProps<RowType extends { id: string }, RowTypeInput = RowType>
34
+ {
35
+ entity: IEntityInfo<RowType, RowTypeInput>;
36
+ ui?: {
37
+ action?: {
38
+ menus: {
39
+ _new?: boolean;
40
+ list?: boolean;
41
+ view?: boolean;
42
+ view_history?: boolean;
43
+ copy?: boolean;
44
+ edit?: boolean;
45
+ delete?: boolean;
46
+ apply?: boolean;
47
+ };
48
+ children?: JSX.Element | JSX.Element[];
49
+ };
50
+ }
51
+ list?: NSASectionListCustomProps<RowType, RowTypeInput>;
52
+ edit?: NSASectionEditCustomProps<RowType, RowTypeInput> & {
53
+ ui: {
54
+ action?: {
55
+ menus: {
56
+ apply?: boolean,
57
+ }
58
+ },
59
+ }
60
+ };
61
+ view?: NSASectionViewCutomProps<RowType>;
62
+ }
63
+ export interface NSALayoutCustomProps<RowType extends { id: string }, RowTypeInput = RowType> extends IBaseComponentProps, NSARouterMakerResult
64
+ {
65
+ action?: NSBarActionProps;
66
+ title?: NSBarTitleProps;
67
+ alerts?: NSBarAlertProps[];
68
+ pages?: NSALayoutPagesProps<RowType, RowTypeInput>;
69
+ children: ReactElement;
70
+ onEnterPressed?: () => void;
71
+ onDidMounted?: () => void;
72
+ }
73
+ export interface NSALayoutProps<RowType extends { id: string }, RowTypeInput = RowType> extends NSALayoutCustomProps<RowType, RowTypeInput>
74
+ {
75
+ model?: {
76
+ id: string | null;
77
+ }
78
+ scope?: string;
79
+ logo?: string;
80
+ master_menu?: boolean;
81
+ background?: IBackgroundProps;
82
+ header?: {
83
+ enabled?: boolean,
84
+ right?: IHeaderRightProps;
85
+ left?: IHeaderLeftProps;
86
+ built_in?: {
87
+ logout?: boolean;
88
+ message?: boolean;
89
+ access?: boolean;
90
+ workspace?: boolean;
91
+ product?: boolean;
92
+ user?: boolean;
93
+ };
94
+ mega_menu?: boolean;
95
+ };
96
+ }
97
+
98
+ interface NSALayoutState
99
+ {
100
+ parmas: any | null;
101
+ product: ProductFullRow | null;
102
+ refresher: number;
103
+ message_count: number;
104
+ showDeleteDialog: boolean;
105
+ delete_ids: string[];
106
+ delete_title: string;
107
+ showMessageListDialog: boolean;
108
+ showAccessListDialog: boolean;
109
+ showProductListDialog: boolean;
110
+ showWorkspaceListDialog: boolean;
111
+ showUserDialog: boolean;
112
+ showMasterMenu: boolean;
113
+ }
114
+
115
+ export class NSALayout<RowType extends { id: string }, RowTypeInput = RowType> extends Component<NSALayoutProps<RowType, RowTypeInput>, NSALayoutState>
116
+ {
117
+ private onDidMounted_Called: boolean = false;
118
+ private onDidMounted_Ready: boolean = false;
119
+ private hasCFT: boolean;
120
+ private isLoggedIn: boolean;
121
+ private cache_product: CacheService<ProductFullRow>;
122
+ private cache_message: CacheService<number>;
123
+ private cache_master_menu: CacheService<{ open: boolean }>;
124
+ NSASectionList_Ref = createRef<NSASectionList<RowType, RowTypeInput>>();
125
+ NSASectionEdit_Ref = createRef<NSASectionEdit<RowType, RowTypeInput>>();
126
+ NSASectionView_Ref = createRef<NSASectionView<RowType, RowTypeInput>>();
127
+ NSATaskBar_Ref = createRef<NSATaskBar<string>>();
128
+
129
+ constructor(props: NSALayoutProps<RowType, RowTypeInput>)
130
+ {
131
+ super(props);
132
+
133
+ // Account
134
+ this.isLoggedIn = this.props.account?.token_manager?.exists() ?? false;
135
+
136
+ // CFT
137
+ this.hasCFT = false;
138
+ if (this.props.pages?.entity)
139
+ try
140
+ {
141
+ let namirasoft = new NamirasoftMap(this.props.account.token_manager, props.notifier.onError);
142
+ let table = namirasoft.getByProductID(this.props.product_id).tables[this.props.pages.entity.name];
143
+ this.hasCFT = table.hasFieldTables();
144
+ } catch (error)
145
+ { }
146
+
147
+ // Cache Product
148
+ this.cache_product = ProductCacheService.get(this.props.product_id, console.error);
149
+
150
+ // Cache Message
151
+ this.cache_message = new CacheService<number>(
152
+ "ns-messsage-count",
153
+ new IStorageLocal(),
154
+ 15,
155
+ async () => "1.0.0",
156
+ async () =>
157
+ {
158
+ let REACT_APP_BASE_URL_MESSAGE = new EnvService("REACT_APP_BASE_URL_MESSAGE", true).getString();
159
+ let server = new NamirasoftMessageServer(REACT_APP_BASE_URL_MESSAGE, this.props.account.token_manager, console.error);
160
+ let res = await server.message.GetNewCount();
161
+ return res.count;
162
+ }
163
+ );
164
+
165
+ this.cache_master_menu = NSACacheService.getMasterMenuStatus();
166
+ this.cache_message.onExpired = () => { this.reloadMessageCount(true); };
167
+
168
+ // State
169
+ this.state = {
170
+ parmas: null,
171
+ product: null,
172
+ refresher: 0,
173
+ message_count: 0,
174
+ showDeleteDialog: false,
175
+ delete_ids: [],
176
+ delete_title: "",
177
+ showMessageListDialog: false,
178
+ showAccessListDialog: false,
179
+ showProductListDialog: false,
180
+ showWorkspaceListDialog: false,
181
+ showUserDialog: false,
182
+ showMasterMenu: false,
183
+ };
184
+ }
185
+ public reloadMessageCount(force: boolean)
186
+ {
187
+ if (force)
188
+ this.cache_message.del();
189
+ this.cache_message.get().then(count =>
190
+ {
191
+ this.setState(prev => ({ ...prev, message_count: count }));
192
+ }).catch(() => { });
193
+ }
194
+
195
+ private async runDelete(ids: string[], name: string): Promise<void>
196
+ {
197
+ if (!this.props.pages?.entity.server.delete)
198
+ return;
199
+
200
+ await new Promise<void>(resolve => this.setState(prev => ({ ...prev, delete_ids: ids, delete_title: `Deleting ${name}` }), resolve));
201
+
202
+ const result = await this.NSATaskBar_Ref.current?.run();
203
+ if (!result)
204
+ return;
205
+
206
+ const deletedIds = result.succeeded;
207
+
208
+ if (deletedIds.length === 0)
209
+ return;
210
+
211
+ if (this.props.pages?.list)
212
+ {
213
+ let table = this.NSASectionList_Ref.current?.NSATable_Main.current;
214
+ if (table)
215
+ {
216
+ let selected_ids = table.getSelectedIDs().filter(id => !deletedIds.includes(id));
217
+ let selected_rows = table.getSelectedRows().filter(row => !deletedIds.includes(row.id));
218
+ table.setSelectedIDs(selected_ids, selected_rows);
219
+ table.reload(null, null);
220
+ }
221
+ }
222
+ else if (this.props.pages?.edit)
223
+ {
224
+ if (this.props.pages.entity.client.getListURL)
225
+ this.props.url.redirect(this.props.pages.entity.client.getListURL(), {});
226
+ else if (this.props.pages.entity.client.getViewURL)
227
+ this.props.url.redirect(this.props.pages.entity.client.getViewURL(deletedIds[0]), {});
228
+ }
229
+ else if (this.props.pages?.view)
230
+ {
231
+ if (this.props.pages.entity.client.getListURL)
232
+ this.props.url.redirect(this.props.pages.entity.client.getListURL(), {});
233
+ }
234
+ }
235
+
236
+ override componentDidMount(): void
237
+ {
238
+ this.onDidMounted_Called = false;
239
+ this.cache_product.get().then(product =>
240
+ {
241
+ this.setState(prev => ({ ...prev, product }));
242
+ })
243
+
244
+ this.cache_master_menu.get().then(status =>
245
+ {
246
+ this.setState(() => ({ showMasterMenu: status.open }));
247
+ })
248
+
249
+ if (this.isLoggedIn)
250
+ this.reloadMessageCount(false);
251
+
252
+ client.start();
253
+ }
254
+ override componentDidUpdate(): void
255
+ {
256
+ if (!this.onDidMounted_Called)
257
+ if (this.onDidMounted_Ready)
258
+ {
259
+ this.props.onDidMounted?.();
260
+ this.onDidMounted_Called = true;
261
+ }
262
+ }
263
+ override componentWillUnmount(): void
264
+ {
265
+ client.stop();
266
+ }
267
+ override render()
268
+ {
269
+ this.onDidMounted_Ready = false;
270
+ let id = this.props.model?.id ?? null;
271
+ if (!id)
272
+ {
273
+ if (this.state.parmas === null)
274
+ return <UseParams onLoaded={parmas =>
275
+ {
276
+ this.setState({ parmas });
277
+ }} />;
278
+ id = this.state.parmas.id;
279
+ }
280
+
281
+ const hideDeleteDialog = async () => { this.setState(prev => ({ ...prev, showDeleteDialog: false })); };
282
+ const showDeleteDialog = async () => { this.setState(prev => ({ ...prev, showDeleteDialog: true })); };
283
+
284
+ let icons: IHeaderIconProps[] = [];
285
+
286
+ if (this.isLoggedIn)
287
+ {
288
+ if (this.props.header?.built_in?.message ?? true)
289
+ icons.push({
290
+ src: "https://static.namirasoft.com/image/namirasoft/message/logo/base.png", alt: "Message", onClicked: () =>
291
+ {
292
+ this.setState(prev => ({ ...prev, showMessageListDialog: true }));
293
+ this.reloadMessageCount(true);
294
+ },
295
+ children: <>
296
+ {
297
+ (
298
+ this.state.showMessageListDialog &&
299
+ <NSAMessageListDialog {...this.props}
300
+ onClose={() => { this.setState(prev => ({ ...prev, showMessageListDialog: false })); }}
301
+ onSeenAll={() =>
302
+ {
303
+ this.reloadMessageCount(true);
304
+ }}
305
+ onMessageClick={() =>
306
+ {
307
+ this.reloadMessageCount(true);
308
+ }}
309
+ />
310
+ )
311
+ }
312
+ </>,
313
+ count: this.state.message_count,
314
+ tooltip: "Message"
315
+ });
316
+ }
317
+ if (this.isLoggedIn)
318
+ if (this.props.header?.built_in?.access ?? true)
319
+ icons.push({
320
+ src: "https://static.namirasoft.com/image/namirasoft/access/logo/base.png", alt: "Access", onClicked: () =>
321
+ {
322
+ this.setState(prev => ({ ...prev, showAccessListDialog: true }));
323
+ },
324
+ children: <>
325
+ {
326
+ (this.state.showAccessListDialog) && <NSAAccessListDialog {...this.props} onClose={() =>
327
+ {
328
+ this.setState(prev => ({ ...prev, showAccessListDialog: false }));
329
+ }} />
330
+ }
331
+ </>,
332
+ tooltip: "Access"
333
+ });
334
+ if (this.isLoggedIn)
335
+ if (this.props.header?.built_in?.workspace ?? true)
336
+ icons.push({
337
+ src: "https://static.namirasoft.com/image/namirasoft/workspace/logo/base.png", alt: "Workspace", onClicked: () =>
338
+ {
339
+ this.setState(prev => ({ ...prev, showWorkspaceListDialog: true }));
340
+ },
341
+ children: <>
342
+ {
343
+ (this.state.showWorkspaceListDialog) && <NSAWorkspaceListDialog {...this.props} onClose={() =>
344
+ {
345
+ this.setState(prev => ({ ...prev, showWorkspaceListDialog: false }));
346
+ }} />
347
+ }
348
+ </>,
349
+ tooltip: "Workspace List"
350
+ });
351
+ if (this.props.header?.built_in?.product ?? true)
352
+ icons.push({
353
+ src: "https://static.namirasoft.com/image/concept/product-list/white.svg", alt: "Product", onClicked: () =>
354
+ {
355
+ this.setState(prev => ({ ...prev, showProductListDialog: true }));
356
+ },
357
+ children: <>
358
+ {
359
+ (this.state.showProductListDialog) && <NSAProductListDialog {...this.props} onClose={() =>
360
+ {
361
+ this.setState(prev => ({ ...prev, showProductListDialog: false }));
362
+ }} >
363
+ <></>
364
+ </NSAProductListDialog>
365
+ }
366
+ </>,
367
+ tooltip: "Product List"
368
+ });
369
+
370
+ if (this.isLoggedIn)
371
+ if (this.props.header?.built_in?.user ?? true)
372
+ icons.push({
373
+ src: "https://static.namirasoft.com/image/concept/user/white.svg",
374
+ alt: "User",
375
+ onClicked: () =>
376
+ {
377
+ this.setState(prev => ({ ...prev, showUserDialog: true }));
378
+ },
379
+ children: <>
380
+ {
381
+ (this.state.showUserDialog) &&
382
+ <NSAUserDialog
383
+ {...this.props}
384
+ client={client}
385
+ onClose={() => this.setState(prev => ({ ...prev, showUserDialog: false }))}
386
+ >
387
+ <></>
388
+ </NSAUserDialog>
389
+ }
390
+ </>,
391
+ tooltip: "User"
392
+ });
393
+
394
+ let name = "";
395
+ if (this.props.pages)
396
+ name = NamingConvention.lower_case_underscore.convert(this.props.pages.entity.name, NamingConvention.Pascal_Case_Space);
397
+
398
+ let getIDs: (() => string[]) | null = null;
399
+ let getRows: (() => RowType[]) | null = null;
400
+ if (this.props.pages?.list)
401
+ {
402
+ getIDs = () => { return this.NSASectionList_Ref.current?.getSelectedIDs() ?? []; };
403
+ getRows = () => { return this.NSASectionList_Ref.current?.getSelectedRows() ?? []; };
404
+ }
405
+ if (this.props.pages?.edit)
406
+ {
407
+ getIDs = () => [id].filter(id => id) as string[];
408
+ getRows = () => [this.NSASectionEdit_Ref.current?.getRow()].filter(row => row) as RowType[];
409
+ }
410
+ if (this.props.pages?.view)
411
+ {
412
+ getIDs = () => [id].filter(id => id) as string[];
413
+ getRows = () => [this.NSASectionView_Ref.current?.getRow()].filter(row => row) as RowType[];
414
+ }
415
+
416
+ let action: NSBarActionProps | undefined = { title: "", description: "", menus: {} };
417
+ if (this.props.action?.title)
418
+ action.title = this.props.action.title;
419
+ if (this.props.action?.description)
420
+ action.description = this.props.action.description;
421
+ if (this.props.action?.menus != null)
422
+ action.menus = { ...this.props.action?.menus };
423
+
424
+ if (getIDs && getRows)
425
+ if (this.props.pages?.entity.client.getMenus)
426
+ {
427
+ let menus = this.props.pages?.entity.client.getMenus(getIDs, getRows);
428
+ for (let key of Object.keys(menus))
429
+ {
430
+ if (!action.menus)
431
+ action.menus = {};
432
+ if (!action.menus[key])
433
+ action.menus[key] = [];
434
+ action.menus[key].push(...menus[key]);
435
+ }
436
+ }
437
+
438
+ let name_apply = "Apply";
439
+ let name_actions = "Actions";
440
+ if (!action.menus)
441
+ action.menus = {};
442
+ action.menus[name_apply] = [];
443
+ action.menus[name_actions] = [];
444
+
445
+ if (this.props.pages?.edit)
446
+ {
447
+ if (!action.title)
448
+ action.title = (this.props.pages.edit.isEdit ? "Edit" : "New") + " " + name;
449
+
450
+ // Menu
451
+ if (this.props.pages.edit.ui.action?.menus.apply ?? true)
452
+ action.menus[name_apply].push(Actions.apply({
453
+ action: (onFinished) =>
454
+ {
455
+ if (this.props.pages?.edit)
456
+ this.NSASectionEdit_Ref.current?.onApply(onFinished);
457
+ }
458
+ }));
459
+ }
460
+ else if (this.props.pages)
461
+ {
462
+ if (this.props.pages.entity.client.getNewURL)
463
+ if (this.props.pages.entity.server.create)
464
+ if (this.props.pages.ui?.action?.menus._new ?? true)
465
+ action.menus[name_apply].push(Actions._new(this.props, this.props.pages.entity));
466
+ }
467
+
468
+ if (getIDs)
469
+ if (this.props.pages)
470
+ {
471
+ if (this.props.pages.entity.client.getListURL)
472
+ if (this.props.pages.entity.server.list)
473
+ if (this.props.pages.ui?.action?.menus.list ?? true)
474
+ action.menus[name_actions].push(Actions.list(this.props, this.props.pages.entity));
475
+
476
+ if (this.props.pages.entity.client.getViewURL)
477
+ if (this.props.pages.entity.server.get)
478
+ if (this.props.pages.ui?.action?.menus.view ?? true)
479
+ action.menus[name_actions].push(Actions.view(this.props, this.props.pages.entity, getIDs));
480
+
481
+ if (this.props.pages.ui?.action?.menus.view_history ?? true)
482
+ action.menus[name_actions].push(Actions.viewHistory(getIDs));
483
+
484
+ if (this.props.pages.entity.client.getCopyURL)
485
+ if (this.props.pages.entity.server.create)
486
+ if (this.props.pages.ui?.action?.menus.copy ?? true)
487
+ action.menus[name_actions].push(Actions.copy(this.props, this.props.pages.entity, getIDs));
488
+
489
+ if (this.props.pages.entity.client.getEditURL)
490
+ if (this.props.pages.entity.server.update)
491
+ if (this.props.pages.ui?.action?.menus.edit ?? true)
492
+ action.menus[name_actions].push(Actions.edit(this.props, this.props.pages.entity, getIDs));
493
+
494
+ if (this.props.pages.entity.server.delete)
495
+ if (this.props.pages.ui?.action?.menus.delete ?? true)
496
+ action.menus[name_actions].push(Actions.delete(getIDs, {
497
+ action: showDeleteDialog,
498
+ showLoading: false
499
+ }));
500
+ }
501
+
502
+ let action_name = "";
503
+ let content;
504
+ if (this.props.pages?.list)
505
+ {
506
+ action_name = "List";
507
+ content = <FieldCacheService
508
+ {...this.props}
509
+ render={rows =>
510
+ {
511
+ if (this.props.pages?.list)
512
+ return <NSASectionList<RowType, RowTypeInput>
513
+ ref={this.NSASectionList_Ref}
514
+ {...this.props}
515
+ {...this.props.pages.list}
516
+ entity={this.props.pages.entity}
517
+ onIDsChanged={() =>
518
+ {
519
+ this.setState(prev => ({ ...prev }));
520
+ this.props.pages?.list?.onIDsChanged?.();
521
+ }}
522
+ ctf={{
523
+ enabled: this.hasCFT,
524
+ rows
525
+ }}
526
+ >
527
+ {this.props.children}
528
+ </NSASectionList>;
529
+ return <></>;
530
+ }}
531
+ />;
532
+ }
533
+ else if (this.props.pages?.edit)
534
+ {
535
+ if (this.props.pages.edit.isEdit)
536
+ action_name = "Edit";
537
+ else
538
+ action_name = "New";
539
+ content = <FieldCacheService
540
+ {...this.props}
541
+ render={rows =>
542
+ {
543
+ if (this.props.pages?.edit)
544
+ return <NSASectionEdit
545
+ ref={this.NSASectionEdit_Ref}
546
+ {...this.props}
547
+ {...this.props.pages.edit}
548
+ entity={this.props.pages.entity}
549
+ ctf={{
550
+ enabled: this.hasCFT,
551
+ rows
552
+ }}
553
+ onRowLoaded={(row) =>
554
+ {
555
+ this.setState(prev => ({ ...prev }), () =>
556
+ {
557
+ this.props.pages?.edit?.onRowLoaded?.(row);
558
+ });
559
+ }}
560
+ model={{
561
+ id,
562
+ row: null
563
+ }}
564
+ >
565
+ {this.props.children}
566
+ </NSASectionEdit>;
567
+ return <></>;
568
+ }}
569
+ />;
570
+ }
571
+ else if (this.props.pages?.view)
572
+ {
573
+ action_name = "View";
574
+
575
+ content = <FieldCacheService
576
+ {...this.props}
577
+ render={rows =>
578
+ {
579
+ if (this.props.pages?.view)
580
+ return <NSASectionView
581
+ ref={this.NSASectionView_Ref}
582
+ {...this.props}
583
+ {...this.props.pages.view}
584
+ entity={this.props.pages.entity}
585
+ ctf={{
586
+ enabled: this.hasCFT,
587
+ rows
588
+ }}
589
+ onRowLoaded={(row) =>
590
+ {
591
+ this.setState(prev => ({ ...prev }), () =>
592
+ {
593
+ this.props.pages?.view?.onRowLoaded?.(row);
594
+ });
595
+ }}
596
+ model={{
597
+ id,
598
+ row: null
599
+ }}
600
+ >
601
+ {this.props.children}
602
+ </NSASectionView>;
603
+ return <></>;
604
+ }}
605
+ />;
606
+ }
607
+ else
608
+ content = this.props.children;
609
+
610
+ let fullname = "";
611
+ if (this.props.pages?.entity.name)
612
+ {
613
+ let Name_Pascal_Space = NamingConvention.lower_case_underscore.convert(this.props.pages.entity.name, NamingConvention.Pascal_Case_Space);
614
+ if (action_name === "List")
615
+ fullname = Name_Pascal_Space + " " + action_name;
616
+ else
617
+ fullname = action_name + " " + Name_Pascal_Space;
618
+ }
619
+
620
+ if (!this.state.product)
621
+ return <NSLoading title='Loading Namirasoft Product ...' />;
622
+
623
+ let action_title = action?.title ?? "";
624
+ if (!action_title)
625
+ action_title = fullname;
626
+ if (!action_title)
627
+ action_title = this.state.product?.name
628
+
629
+ let action_description = action?.description ?? "";
630
+ if (!action_description)
631
+ action_description = this.state.product?.description ?? "";
632
+
633
+ let scope = this.props.scope ?? `${this.state.product.name} Console`;
634
+ let logo = this.props.logo ?? this.state.product.logo ?? "";
635
+ let background = this.props.background?.image ?? this.state.product.background ?? "";
636
+
637
+ this.onDidMounted_Ready = true;
638
+
639
+ let user_id = this.props.account.token_manager.getUserData(x => x.id, "");
640
+ let user_first_name = this.props.account.token_manager.getUserData(x => x.first_name, "");
641
+ let user_last_name = this.props.account.token_manager.getUserData(x => x.last_name, "");
642
+ let user_email = this.props.account.token_manager.getUserData(x => x.email, "");
643
+ let user_phone = this.props.account.token_manager.getUserData(x => x.phone ?? "", "");
644
+ return (
645
+ <div className="d-flex">
646
+ {
647
+ (this.props.master_menu ?? true) &&
648
+ <NSAMasterMenu
649
+ {...this.props}
650
+ open={this.state.showMasterMenu}
651
+ setOpen={(open) =>
652
+ {
653
+ this.cache_master_menu.set({ open });
654
+ this.setState(() => ({ showMasterMenu: open }));
655
+ }}
656
+ />
657
+ }
658
+ <FieldCacheService
659
+ {...this.props}
660
+ render={(ctf_rows) => (
661
+ <NSLayout
662
+ {...this.props}
663
+ scope={scope}
664
+ logo={logo}
665
+ background={{ image: background, attachment: this.props.background?.attachment, size: this.props.background?.size }}
666
+ header={{
667
+ enabled: this.props.header?.enabled,
668
+ right: {
669
+ icons: [...icons, ...(this.props.header?.right?.icons ?? [])],
670
+ },
671
+ left: {
672
+ title: this.props.header?.left?.title
673
+ },
674
+ mega_menu: this.props.header?.mega_menu,
675
+ onCollapse: () =>
676
+ {
677
+ this.setState((prev) => ({ showMasterMenu: !prev.showMasterMenu }))
678
+ },
679
+ filters_postprocessor: (filters) => NSALayout_Field.postprocess(filters, ctf_rows),
680
+ }}
681
+ action={{
682
+ title: action_title,
683
+ description: action_description,
684
+ menus: action.menus,
685
+ children: this.props.action?.children
686
+ }}
687
+ isLoggedIn={this.props.account?.token_manager?.exists}
688
+ notifications={this.props.notifier.getNotifications()}
689
+ >
690
+ <Helmet>
691
+ <meta name="ns-account-user-id" content={user_id} />
692
+ <meta name="ns-account-user-first-name" content={user_first_name} />
693
+ <meta name="ns-account-user-last-name" content={user_last_name} />
694
+ <meta name="ns-account-user-email" content={user_email} />
695
+ <meta name="ns-account-user-phone" content={user_phone} />
696
+ </Helmet>
697
+ {
698
+ this.state.showDeleteDialog &&
699
+ <NSDialogDelete
700
+ title={"Delete Confirmation"}
701
+ description={
702
+ (getIDs && getIDs().length > 1)
703
+ ? `Are you sure you want to delete ${getIDs().length} ${name}s?`
704
+ : `Are you sure you want to delete ${name}?`
705
+ }
706
+ onClose={hideDeleteDialog}
707
+ onNo={{ action: hideDeleteDialog, showLoading: false }}
708
+ show_as_code={false}
709
+ onYes={{
710
+ showLoading: false,
711
+ action: (onFinished) =>
712
+ {
713
+ try
714
+ {
715
+ let ids = getIDs ? getIDs() : [];
716
+ if (ids.length === 0)
717
+ throw new Error("Nothing was found to delete.");
718
+ this.runDelete(ids, name);
719
+ }
720
+ catch (error: any)
721
+ {
722
+ this.props.notifier.onError(error);
723
+ }
724
+ this.setState(prev => ({ ...prev, showDeleteDialog: false }));
725
+ onFinished();
726
+ }
727
+ }}
728
+ >
729
+ <></>
730
+ </NSDialogDelete>}
731
+ {
732
+ content
733
+ }
734
+ </NSLayout>
735
+ )}
736
+ />
737
+ <NSATaskBar<string>
738
+ ref={this.NSATaskBar_Ref}
739
+ title={this.state.delete_title}
740
+ items={this.state.delete_ids}
741
+ handler={async (id) =>
742
+ {
743
+ const del = this.props.pages?.entity.server.delete;
744
+ if (del)
745
+ await del(id);
746
+ }}
747
+ />
748
+ </div >
749
+ );
750
+ }
751
751
  }