namirasoft-account-react 1.4.431 → 1.4.433

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