namirasoft-account-react 1.5.4 → 1.5.6

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