@canmingir/link 1.2.23 → 1.2.25

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 (60) hide show
  1. package/package.json +3 -1
  2. package/src/Platform.jsx +10 -14
  3. package/src/context/Context.js +98 -0
  4. package/src/context/reducer.js +590 -10
  5. package/src/layouts/auth/modern.jsx +2 -2
  6. package/src/lib/APIDialogAction/APIDialogAction.jsx +109 -0
  7. package/src/lib/APIDialogAction/index.js +1 -0
  8. package/src/lib/APIDialogAction/styles.js +6 -0
  9. package/src/lib/APIParams/APIParams.jsx +57 -0
  10. package/src/lib/APIParams/index.js +1 -0
  11. package/src/lib/APIPath/APIPath.jsx +82 -0
  12. package/src/lib/APIPath/index.js +1 -0
  13. package/src/lib/APIPath/styles.js +19 -0
  14. package/src/lib/APITree/APITree.jsx +409 -0
  15. package/src/lib/APITree/Arrow.jsx +21 -0
  16. package/src/lib/APITree/DeleteMethodDialog.jsx +41 -0
  17. package/src/lib/APITree/index.js +1 -0
  18. package/src/lib/APITree/styles.js +19 -0
  19. package/src/lib/APITypes/APITypes.jsx +141 -0
  20. package/src/lib/APITypes/TypeEditor.jsx +46 -0
  21. package/src/lib/APITypes/TypeList.jsx +180 -0
  22. package/src/lib/APITypes/index.js +1 -0
  23. package/src/lib/BlankTreeMessage/BlankTreeMessage.jsx +39 -0
  24. package/src/lib/BlankTreeMessage/index.js +1 -0
  25. package/src/lib/DialogTootip/DialogTooltip.jsx +67 -0
  26. package/src/lib/DialogTootip/index.js +1 -0
  27. package/src/lib/DialogTootip/styles.js +9 -0
  28. package/src/lib/NewApiBody/NewAPIBody.jsx +97 -0
  29. package/src/lib/NewApiBody/ParamView.jsx +38 -0
  30. package/src/lib/NucDialog/NucDialog.jsx +108 -0
  31. package/src/lib/NucDialog/index.js +1 -0
  32. package/src/lib/ParamTable/ParamTable.jsx +133 -0
  33. package/src/lib/ParamTable/TypeMenu.jsx +102 -0
  34. package/src/lib/ParamTable/defaults.js +47 -0
  35. package/src/lib/ParamTable/index.js +1 -0
  36. package/src/lib/ParamTable/styles.js +12 -0
  37. package/src/lib/ResourceMenu/AlertMassage.jsx +28 -0
  38. package/src/lib/ResourceMenu/DeleteResourceDialog.jsx +60 -0
  39. package/src/lib/ResourceMenu/ResourceMenu.jsx +156 -0
  40. package/src/lib/ResourceMenu/index.js +1 -0
  41. package/src/lib/ResourceMenu/styles.js +5 -0
  42. package/src/lib/Schema/Schema.jsx +204 -0
  43. package/src/lib/Schema/index.js +1 -0
  44. package/src/lib/SchemaEditor/SchemaEditor.jsx +258 -0
  45. package/src/lib/SchemaEditor/SchemaEditor.test.js +193 -0
  46. package/src/lib/SchemaEditor/SchemaPropertyEditor.jsx +135 -0
  47. package/src/lib/SchemaEditor/SchemaUtils.js +152 -0
  48. package/src/lib/SchemaEditor/index.js +1 -0
  49. package/src/lib/ToggleableMenu/ToggleableMenu.jsx +35 -0
  50. package/src/lib/ToggleableMenu/index.js +1 -0
  51. package/src/lib/index.js +14 -0
  52. package/src/pages/Callback.jsx +2 -4
  53. package/src/pages/LoginPage.jsx +3 -12
  54. package/src/stories/APITree.stories.jsx +429 -0
  55. package/src/stories/FlowChart.stories.jsx +1 -1
  56. package/src/templates/ActionTemplate.js +24 -0
  57. package/src/widgets/Login/CognitoLogin.jsx +2 -2
  58. package/src/widgets/Login/DemoLogin.jsx +1 -1
  59. package/src/widgets/LoginForm/LoginForm.jsx +8 -3
  60. package/src/widgets/SettingsDialog.jsx +33 -11
@@ -1,5 +1,9 @@
1
+ import ActionTemplate from "../templates/ActionTemplate.js";
2
+ import Context from "./Context.js";
1
3
  import { jwtDecode } from "jwt-decode";
4
+ import { publish } from "@nucleoidai/react-event";
2
5
  import { storage } from "@nucleoidjs/webstorage";
6
+ import { v4 as uuid } from "uuid";
3
7
 
4
8
  let login = true;
5
9
  const itemId = storage.get("itemId");
@@ -14,39 +18,615 @@ try {
14
18
  login = false;
15
19
  }
16
20
 
17
- export const initialState = {
21
+ export const initialState = new Context({
18
22
  login,
19
23
  itemId,
20
- };
24
+ specification: {
25
+ api: [],
26
+ functions: [],
27
+ types: [],
28
+ declarations: [],
29
+ },
30
+ pages: {
31
+ api: {
32
+ selected: { path: "/", method: "get" },
33
+ dialog: {
34
+ type: null,
35
+ action: null,
36
+ open: false,
37
+ view: null,
38
+ map: {},
39
+ params: {},
40
+ },
41
+ resourceMenu: {
42
+ open: false,
43
+ anchor: null,
44
+ path: null,
45
+ },
46
+ },
47
+ functions: {
48
+ selected: null,
49
+ dialog: {
50
+ type: null,
51
+ open: false,
52
+ },
53
+ AIDialog: {
54
+ open: false,
55
+ },
56
+ },
57
+ logic: {
58
+ selected: null,
59
+ AIDialog: {
60
+ open: false,
61
+ },
62
+ },
63
+ query: {
64
+ results: null,
65
+ },
66
+ },
67
+ });
68
+
69
+ export const reducer = (context, action) => {
70
+ context = Context.copy(context);
21
71
 
22
- export const reducer = (state, action) => {
23
- state = { ...state };
72
+ const { specification, pages } = context;
24
73
 
25
74
  switch (action.type) {
26
75
  case "LOGIN": {
27
- state.login = true;
76
+ context.login = true;
28
77
  break;
29
78
  }
30
79
  case "LOGOUT": {
31
80
  storage.clear();
32
- state.login = false;
81
+ context.login = false;
33
82
  break;
34
83
  }
35
84
 
36
85
  case "ITEM_SELECT": {
37
- state.itemId = action.payload;
38
- storage.set("itemId", state.itemId);
86
+ context.itemId = action.payload;
87
+ storage.set("itemId", context.itemId);
39
88
  break;
40
89
  }
41
90
 
42
91
  case "ITEM_DELETE": {
43
92
  storage.remove("itemId");
44
- state.itemId = null;
93
+ context.itemId = null;
94
+ break;
95
+ }
96
+
97
+ case "SET_PROJECT": {
98
+ specification.api = action.payload.project.specification.api;
99
+ specification.functions = action.payload.project.specification.functions;
100
+ specification.types = action.payload.project.specification.types;
101
+ pages.api.selected.path = "/";
102
+ break;
103
+ }
104
+
105
+ case "OPEN_API_DIALOG": {
106
+ const { type, action: dialogAction } = action.payload;
107
+ pages.api.dialog.type = type;
108
+ pages.api.dialog.action = dialogAction;
109
+ pages.api.dialog.open = true;
110
+ break;
111
+ }
112
+
113
+ case "SAVE_API_DIALOG": {
114
+ const {
115
+ path,
116
+ method,
117
+ request,
118
+ response,
119
+ params,
120
+ types,
121
+ summary,
122
+ description,
123
+ } = action.payload;
124
+
125
+ const newApi = {
126
+ path,
127
+ method,
128
+ request,
129
+ response,
130
+ params,
131
+ summary,
132
+ description,
133
+ action: ActionTemplate[method.toUpperCase()],
134
+ };
135
+ pages.api.dialog.open = false;
136
+ specification.api.push(newApi);
137
+ pages.api.selected = { path, method };
138
+ publish("SELECTED_API_CHANGED", {
139
+ path: path,
140
+ method: method,
141
+ });
142
+
143
+ specification.types = types;
144
+ break;
145
+ }
146
+
147
+ case "CLOSE_API_DIALOG": {
148
+ pages.api.dialog.open = false;
149
+ break;
150
+ }
151
+
152
+ case "SET_API_DIALOG_VIEW":
153
+ pages.api.dialog.view = action.payload.view;
154
+ break;
155
+
156
+ case "SET_SELECTED_API": {
157
+ if (action.payload.method === null) {
158
+ const endpoint = specification.api.find(
159
+ (endpoint) => endpoint.path === action.payload.path
160
+ );
161
+
162
+ if (endpoint && endpoint.method) {
163
+ action.payload.method = endpoint.method;
164
+ }
165
+ }
166
+ pages.api.selected = {
167
+ path: action.payload.path,
168
+ method: action.payload.method,
169
+ };
170
+
171
+ publish("SELECTED_API_CHANGED", {
172
+ path: action.payload.path,
173
+ method: action.payload.method,
174
+ });
175
+
176
+ break;
177
+ }
178
+
179
+ case "SET_SELECTED_FUNCTION": {
180
+ pages.functions.selected = action.payload.function;
181
+ break;
182
+ }
183
+
184
+ case "SET_SELECTED_LOGIC": {
185
+ pages.logic.selected = action.payload.logic;
186
+ break;
187
+ }
188
+
189
+ // ===== Resource Menu Actions =====
190
+ case "OPEN_RESOURCE_MENU":
191
+ pages.api.resourceMenu.open = true;
192
+ pages.api.resourceMenu.anchor = action.payload.anchor;
193
+ pages.api.resourceMenu.path = action.payload.path;
194
+ break;
195
+
196
+ case "DELETE_RESOURCE": {
197
+ const { path } = action.payload;
198
+
199
+ specification.api = specification.api.filter(
200
+ (api) => !api.path.startsWith(path)
201
+ );
202
+
203
+ if (pages.api.selected.path.startsWith(path)) {
204
+ if (specification.api.length > 0) {
205
+ pages.api.selected.path = specification.api[0].path;
206
+ pages.api.selected.method = specification.api[0].method;
207
+ } else {
208
+ pages.api.selected.path = "/";
209
+ pages.api.selected.method = "get";
210
+ }
211
+
212
+ publish("SELECTED_API_CHANGED", {
213
+ path: pages.api.selected.path,
214
+ method: pages.api.selected.method,
215
+ });
216
+ }
217
+
218
+ break;
219
+ }
220
+
221
+ case "DELETE_METHOD": {
222
+ const { path, method } = pages.api.selected;
223
+
224
+ const routeIndex = specification.api.findIndex(
225
+ (route) =>
226
+ route.path === path &&
227
+ route.method.toLowerCase() === method.toLowerCase()
228
+ );
229
+
230
+ if (routeIndex !== -1) {
231
+ specification.api.splice(routeIndex, 1);
232
+ }
233
+
234
+ const samePathRoutes = specification.api.filter(
235
+ (route) => route.path === path
236
+ );
237
+ if (samePathRoutes.length > 0) {
238
+ context.pages.api.selected.method = samePathRoutes[0].method;
239
+ } else {
240
+ context.pages.api.selected.method = null;
241
+ }
242
+
243
+ break;
244
+ }
245
+
246
+ case "CLOSE_RESOURCE_MENU":
247
+ pages.api.resourceMenu.open = false;
248
+ pages.api.resourceMenu = {};
249
+ break;
250
+
251
+ // ===== Function Dialog Actions =====
252
+ case "OPEN_FUNCTION_DIALOG": {
253
+ pages.functions.dialog.type = action.payload.type;
254
+ pages.functions.dialog.open = true;
255
+ break;
256
+ }
257
+
258
+ case "OPEN_AI_DIALOG": {
259
+ const page = action.payload.page;
260
+ pages[page].AIDialog.open = true;
261
+ break;
262
+ }
263
+
264
+ case "CLOSE_AI_DIALOG": {
265
+ const page = action.payload.page;
266
+ pages[page].AIDialog.open = false;
267
+ break;
268
+ }
269
+
270
+ case "SAVE_LOGIC_DIALOG": {
271
+ const { description, summary, definition } = action.payload;
272
+ const declarations = specification.declarations;
273
+
274
+ declarations.push({
275
+ description,
276
+ summary,
277
+ definition,
278
+ });
279
+
280
+ publish("LOGIC_ADDED", {
281
+ declaration: declarations[declarations.length - 1],
282
+ });
283
+
284
+ break;
285
+ }
286
+
287
+ case "SAVE_FUNCTION_DIALOG": {
288
+ const { path, type, definition, params, ext } = action.payload;
289
+ const functions = specification.functions;
290
+
291
+ functions.push({
292
+ path,
293
+ type,
294
+ ext,
295
+ definition,
296
+ params,
297
+ });
298
+
299
+ publish("CONTEXT_CHANGED", {
300
+ files: [{ key: `${path}.${ext}`, value: definition }],
301
+ });
302
+
303
+ break;
304
+ }
305
+
306
+ case "DELETE_FUNCTION": {
307
+ const { path } = action.payload;
308
+
309
+ if (specification.functions.length > 1) {
310
+ const index = specification.functions.findIndex(
311
+ (data) => data.path === path
312
+ );
313
+
314
+ const deletedFunction = specification.functions[index];
315
+ specification.functions.splice(index, 1);
316
+
317
+ const remainingFunctions = specification.functions.filter(
318
+ (data) => data.path !== path
319
+ );
320
+
321
+ const files = remainingFunctions.map((func) => ({
322
+ key: `${func.path}.ts`,
323
+ value: func,
324
+ }));
325
+
326
+ publish("CONTEXT_CHANGED", {
327
+ files: [{ key: `${deletedFunction.path}.ts` }, ...files],
328
+ });
329
+ context.pages.functions.selected = specification.functions[0].path;
330
+ }
331
+
332
+ break;
333
+ }
334
+
335
+ case "CLOSE_FUNCTION_DIALOG": {
336
+ pages.functions.dialog.open = false;
337
+ break;
338
+ }
339
+
340
+ // ===== Type Management Actions =====
341
+ case "UPDATE_TYPE": {
342
+ const { id, name, type } = action.payload;
343
+ const map = pages.api.dialog.map;
344
+
345
+ if (name !== undefined) map[id].name = name;
346
+ if (type !== undefined) {
347
+ if (map[id].type === "array") map[id].items.type = type;
348
+ else {
349
+ map[id].type = type;
350
+
351
+ if (type === "array") map[id].items = { type: "integer" };
352
+ else if (type === "object") map[id].properties = {};
353
+ }
354
+ }
355
+
356
+ break;
357
+ }
358
+
359
+ case "ADD_SCHEMA_PROPERTY": {
360
+ const { id } = action.payload;
361
+ const map = pages.api.dialog.map;
362
+ const key = uuid();
363
+ map[key] = map[id].properties[key] = {
364
+ id: key,
365
+ type: "integer",
366
+ };
367
+ break;
368
+ }
369
+
370
+ case "REMOVE_SCHEMA_PROPERTY": {
371
+ const { id } = action.payload;
372
+ const map = pages.api.dialog.map;
373
+ delete map[id];
374
+ break;
375
+ }
376
+
377
+ case "ADD_PARAM": {
378
+ const map = pages.api.dialog.map;
379
+ const id = uuid();
380
+ pages.api.dialog.params[id] = map[id] = {
381
+ id,
382
+ type: "string",
383
+ required: true,
384
+ };
385
+ break;
386
+ }
387
+
388
+ case "REMOVE_PARAM": {
389
+ const { id } = action.payload;
390
+ const map = pages.api.dialog.map;
391
+ delete pages.api.dialog.params[id];
392
+ delete map[id];
393
+ break;
394
+ }
395
+
396
+ case "QUERY_RESULTS": {
397
+ const query = context.get("pages.query");
398
+ query.results = action.payload.results;
399
+ break;
400
+ }
401
+
402
+ // ===== CRUD Actions =====
403
+ case "CREATE_SAMPLE_CRUD": {
404
+ const { api, functions } = specification;
405
+ const className = action.payload.resource;
406
+ const resource = action.payload.resource.toLowerCase() + "s";
407
+
408
+ const template = {
409
+ request: { type: "object", properties: {} },
410
+ response: { type: "object", properties: {} },
411
+ };
412
+
413
+ api[`/${resource}`] = {
414
+ get: {
415
+ ...template,
416
+ summary: `Get ${className} list`,
417
+ description: `Get ${className} list`,
418
+ action: `function action(req) {\n return ${className};\n}\n`,
419
+ },
420
+ post: {
421
+ ...template,
422
+ summary: `Create ${className}`,
423
+ description: `Create ${className}`,
424
+ action: `function action(req) {\n return new ${className}();\n}\n`,
425
+ },
426
+ };
427
+ api[`/${resource}/{${resource}}`] = {
428
+ get: {
429
+ ...template,
430
+ summary: `Get ${className}`,
431
+ description: `Get ${className}`,
432
+ action: `function action(req) {\n const ${resource} = req.params.${resource};\n return ${className}[${resource}];\n}\n`,
433
+ },
434
+ put: {
435
+ ...template,
436
+ summary: `Update ${className}`,
437
+ description: `Update ${className}`,
438
+ action: `function action(req) {\n const ${resource} = req.params.${resource};\n return ${className}[${resource}];\n}\n`,
439
+ },
440
+ del: {
441
+ ...template,
442
+ summary: `Delete ${className}`,
443
+ description: `Delete ${className}`,
444
+ action: `function action(req) {\n const ${resource} = req.params.${resource};\n delete ${className}[${resource}];\n}\n`,
445
+ },
446
+ };
447
+
448
+ const sample = {
449
+ definition: `class ${className} {\n constructor() {\n }\n}\n`,
450
+ ext: "ts",
451
+ params: [],
452
+ path: `/${className}`,
453
+ type: "CLASS",
454
+ };
455
+
456
+ functions.push(sample);
457
+
458
+ publish("CONTEXT_CHANGED", {
459
+ files: [
460
+ { key: `${sample.path}.${sample.ext}`, value: sample.definition },
461
+ ],
462
+ });
463
+
464
+ break;
465
+ }
466
+
467
+ case "UPDATE_API_SCHEMAS": {
468
+ const { path, method, requestSchema, responseSchema } = action.payload;
469
+ const apiIndex = specification.api.findIndex(
470
+ (api) => api.path === path && api.method === method
471
+ );
472
+
473
+ if (apiIndex !== -1) {
474
+ specification.api[apiIndex].request = {
475
+ ...specification.api[apiIndex].request,
476
+ schema: requestSchema,
477
+ };
478
+ specification.api[apiIndex].response = {
479
+ ...specification.api[apiIndex].response,
480
+ schema: responseSchema,
481
+ };
482
+ }
483
+ break;
484
+ }
485
+
486
+ case "UPDATE_API_SUMMARY": {
487
+ const { path, method, newSummary } = action.payload;
488
+ const apiEndpoint = specification.api.find(
489
+ (api) => api.path === path && api.method === method
490
+ );
491
+ if (apiEndpoint) {
492
+ apiEndpoint.summary = newSummary;
493
+ }
494
+ break;
495
+ }
496
+
497
+ case "UPDATE_API_DESCRIPTION": {
498
+ const { path, method, newDescription } = action.payload;
499
+ const apiEndpoint = specification.api.find(
500
+ (api) => api.path === path && api.method === method
501
+ );
502
+ if (apiEndpoint) {
503
+ apiEndpoint.description = newDescription;
504
+ }
505
+ break;
506
+ }
507
+
508
+ case "DELETE_API": {
509
+ const selectedIndex = specification.api.findIndex(
510
+ (api) =>
511
+ api.path === pages.api.selected.path &&
512
+ api.method === pages.api.selected.method
513
+ );
514
+
515
+ if (selectedIndex > -1) {
516
+ specification.api.splice(selectedIndex, 1);
517
+ if (specification.api.length > 0) {
518
+ pages.api.selected.path = specification.api[0].path;
519
+ pages.api.selected.method = specification.api[0].method;
520
+ } else {
521
+ pages.api.selected.path = "/";
522
+ pages.api.selected.method = "get";
523
+ }
524
+ }
525
+ pages.api.dialog.open = false;
526
+
527
+ break;
528
+ }
529
+
530
+ case "UPDATE_API_TYPES": {
531
+ const { updatedTypes } = action.payload;
532
+ const typeIndex = specification.types.findIndex(
533
+ (type) => type.name === updatedTypes.name
534
+ );
535
+ const updatedType = {
536
+ ...specification.types[typeIndex],
537
+ schema: {
538
+ ...updatedTypes,
539
+ },
540
+ };
541
+ if (typeIndex !== -1) {
542
+ specification.types[typeIndex] = updatedType;
543
+ } else {
544
+ specification.types.push(updatedTypes);
545
+ }
546
+ break;
547
+ }
548
+
549
+ case "ADD_TYPE": {
550
+ const { typeName } = action.payload;
551
+ const newType = {
552
+ name: typeName,
553
+ schema: {
554
+ name: typeName,
555
+ type: "object",
556
+ properties: [
557
+ { type: "string", name: "id" },
558
+ { type: "string", name: "name" },
559
+ ],
560
+ },
561
+ };
562
+ specification.types.push(newType);
563
+ break;
564
+ }
565
+
566
+ case "DELETE_TYPE": {
567
+ const { typeName } = action.payload;
568
+ const typeIndex = specification.types.findIndex(
569
+ (type) => type.name === typeName
570
+ );
571
+
572
+ if (typeIndex !== -1) {
573
+ specification.types.splice(typeIndex, 1);
574
+ }
575
+ break;
576
+ }
577
+
578
+ case "UPDATE_TYPE_NAME": {
579
+ const { oldTypeName, newTypeName } = action.payload;
580
+ const typeIndex = specification.types.findIndex(
581
+ (type) => type.name === oldTypeName
582
+ );
583
+
584
+ if (typeIndex !== -1) {
585
+ specification.types[typeIndex].name = newTypeName;
586
+ specification.types[typeIndex].schema.name = newTypeName;
587
+ }
588
+ break;
589
+ }
590
+
591
+ case "SAVE_API_PARAMS": {
592
+ console.log("SAVE_API_PARAMS", action.payload);
593
+ const { path, method, params } = action.payload;
594
+ const apiIndex = specification.api.findIndex(
595
+ (api) => api.path === path && api.method === method
596
+ );
597
+
598
+ if (apiIndex !== -1) {
599
+ specification.api[apiIndex].params = params;
600
+ }
601
+ break;
602
+ }
603
+
604
+ case "UPDATE_API_PATH_METHOD": {
605
+ const { path, method } = action.payload;
606
+
607
+ const apiIndex = specification.api.findIndex(
608
+ (api) =>
609
+ api.path === pages.api.selected.path &&
610
+ api.method === pages.api.selected.method
611
+ );
612
+
613
+ if (apiIndex !== -1) {
614
+ specification.api[apiIndex].path = path;
615
+ specification.api[apiIndex].method = method;
616
+ }
617
+
618
+ pages.api.selected = {
619
+ ...pages.api.selected,
620
+ path: path,
621
+ method: method,
622
+ };
623
+
45
624
  break;
46
625
  }
47
626
 
48
627
  default:
49
628
  }
50
629
 
51
- return state;
630
+ console.debug("reducer", action.type, context);
631
+ return context;
52
632
  };
@@ -28,7 +28,7 @@ export default function AuthModernLayout({ image }) {
28
28
  <Logo
29
29
  maxSize={140}
30
30
  sx={{
31
- mb: { xs: 1, md: 2 },
31
+ mb: 1,
32
32
  width: 80,
33
33
  height: 80,
34
34
  }}
@@ -37,7 +37,7 @@ export default function AuthModernLayout({ image }) {
37
37
  <Card
38
38
  sx={{
39
39
  width: 1,
40
- py: { xs: 6, md: 8 },
40
+ py: { xs: 6, md: 4 },
41
41
  px: { xs: 4, md: 6 },
42
42
  boxShadow: {
43
43
  xs: (theme) =>