@gnar-engine/cli 1.0.3 → 1.0.5

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 (259) hide show
  1. package/bootstrap/deploy.localdev.yml +44 -30
  2. package/bootstrap/secrets.localdev.yml +20 -14
  3. package/bootstrap/services/control/src/config.js +4 -0
  4. package/bootstrap/services/{agent → page}/Dockerfile +3 -3
  5. package/bootstrap/services/page/package.json +16 -0
  6. package/bootstrap/services/page/src/app.js +50 -0
  7. package/bootstrap/services/page/src/commands/block.handler.js +94 -0
  8. package/bootstrap/services/page/src/commands/page.handler.js +167 -0
  9. package/bootstrap/services/page/src/config.js +62 -0
  10. package/bootstrap/services/page/src/controllers/block.http.controller.js +87 -0
  11. package/bootstrap/services/page/src/controllers/message.controller.js +51 -0
  12. package/bootstrap/services/page/src/controllers/page.http.controller.js +89 -0
  13. package/bootstrap/services/page/src/policies/block.policy.js +50 -0
  14. package/bootstrap/services/page/src/policies/page.policy.js +49 -0
  15. package/bootstrap/services/page/src/schema/page.schema.js +139 -0
  16. package/bootstrap/services/page/src/services/block.service.js +83 -0
  17. package/bootstrap/services/page/src/services/page.service.js +83 -0
  18. package/bootstrap/services/portal/Dockerfile +7 -10
  19. package/bootstrap/services/portal/README.md +66 -15
  20. package/bootstrap/services/portal/index.html +13 -0
  21. package/bootstrap/services/portal/nginx.conf +5 -12
  22. package/bootstrap/services/portal/package.json +27 -53
  23. package/bootstrap/services/portal/public/vite.svg +1 -0
  24. package/bootstrap/services/portal/react-router.config.js +7 -0
  25. package/bootstrap/services/portal/src/App.jsx +16 -0
  26. package/bootstrap/services/portal/src/assets/gnar-engine-white-logo.svg +9 -0
  27. package/bootstrap/services/portal/src/assets/icon-agent.svg +6 -0
  28. package/bootstrap/services/portal/src/assets/icon-cog.svg +4 -0
  29. package/bootstrap/services/portal/src/assets/icon-delete.svg +3 -0
  30. package/bootstrap/services/portal/src/assets/icon-home.svg +3 -0
  31. package/bootstrap/services/portal/src/assets/icon-padlock.svg +3 -0
  32. package/bootstrap/services/portal/src/assets/icon-page.svg +6 -0
  33. package/bootstrap/services/portal/src/assets/icon-reports.svg +3 -0
  34. package/bootstrap/services/portal/src/assets/icon-user.svg +3 -0
  35. package/bootstrap/services/portal/src/assets/icon-users.svg +3 -0
  36. package/bootstrap/services/portal/src/assets/login-green-rad-back-1.jpg +0 -0
  37. package/bootstrap/services/portal/src/assets/react.svg +1 -0
  38. package/bootstrap/services/portal/src/components/CrudList/CrudList.jsx +85 -0
  39. package/bootstrap/services/portal/src/components/CrudList/CrudList.less +59 -0
  40. package/bootstrap/services/portal/src/{ui/customSelect → components/CustomSelect}/CustomSelect.jsx +21 -3
  41. package/bootstrap/services/portal/src/components/LoginForm/LoginForm.jsx +58 -0
  42. package/bootstrap/services/portal/src/components/PageBlockSwitch/PageBlockSwitch.jsx +129 -0
  43. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.jsx +33 -0
  44. package/bootstrap/services/portal/src/components/Sidebar/Sidebar.less +37 -0
  45. package/bootstrap/services/portal/src/components/Topbar/Topbar.jsx +19 -0
  46. package/bootstrap/services/portal/src/components/Topbar/Topbar.less +22 -0
  47. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.jsx +33 -0
  48. package/bootstrap/services/portal/src/components/UserInfo/UserInfo.less +21 -0
  49. package/bootstrap/services/portal/src/css/style.css +446 -742
  50. package/bootstrap/services/portal/src/data/pages.data.js +10 -0
  51. package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.jsx +65 -0
  52. package/bootstrap/services/portal/src/{ui/customSelect/customSelect.less → elements/CustomSelect/CustomSelect.less} +17 -7
  53. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.jsx +115 -0
  54. package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.less +43 -0
  55. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.jsx +124 -0
  56. package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.less +0 -0
  57. package/bootstrap/services/portal/src/elements/Repeater/Repeater.jsx +52 -0
  58. package/bootstrap/services/portal/src/elements/Repeater/Repeater.less +70 -0
  59. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.jsx +18 -0
  60. package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.less +37 -0
  61. package/bootstrap/services/portal/src/elements/SaveButton/SaveButton.jsx +45 -0
  62. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.jsx +63 -0
  63. package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.less +23 -0
  64. package/bootstrap/services/portal/src/elements/TextInput/TextInput.jsx +17 -0
  65. package/bootstrap/services/portal/src/layouts/Card/Card.jsx +15 -0
  66. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.jsx +29 -0
  67. package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.less +49 -0
  68. package/bootstrap/services/portal/src/main.jsx +51 -0
  69. package/bootstrap/services/portal/src/pages/BlockSinglePage/BlockSinglePage.jsx +277 -0
  70. package/bootstrap/services/portal/src/pages/BlocksPage/BlocksPage.jsx +23 -0
  71. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.jsx +11 -0
  72. package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.less +0 -0
  73. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.jsx +21 -0
  74. package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.less +51 -0
  75. package/bootstrap/services/portal/src/pages/PageSinglePage/PageSinglePage.jsx +338 -0
  76. package/bootstrap/services/portal/src/pages/PagesPage/PagesPage.jsx +23 -0
  77. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.jsx +9 -0
  78. package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.less +0 -0
  79. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.jsx +25 -0
  80. package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.less +0 -0
  81. package/bootstrap/services/portal/src/services/block.js +28 -0
  82. package/bootstrap/services/portal/src/services/client.js +67 -0
  83. package/bootstrap/services/portal/src/services/gravatar.js +14 -0
  84. package/bootstrap/services/portal/src/services/page.js +28 -0
  85. package/bootstrap/services/portal/src/services/storage.js +62 -0
  86. package/bootstrap/services/portal/src/services/user.js +41 -0
  87. package/bootstrap/services/portal/src/slices/authSlice.js +44 -14
  88. package/bootstrap/services/portal/src/store/configureStore.js +1 -3
  89. package/bootstrap/services/portal/src/style/cards.less +57 -0
  90. package/bootstrap/services/portal/src/{styles → style}/global.less +90 -45
  91. package/bootstrap/services/portal/src/style/icons.less +21 -0
  92. package/bootstrap/services/portal/src/style/inputs.less +52 -0
  93. package/bootstrap/services/portal/src/style/main.less +28 -0
  94. package/bootstrap/services/portal/src/utils/utils.js +9 -0
  95. package/bootstrap/services/portal/vite.config.js +12 -0
  96. package/bootstrap/services/user/Dockerfile +1 -1
  97. package/bootstrap/services/user/src/app.js +6 -1
  98. package/bootstrap/services/user/src/commands/user.handler.js +0 -3
  99. package/bootstrap/services/user/src/config.js +5 -1
  100. package/bootstrap/services/user/src/policies/user.policy.js +3 -1
  101. package/bootstrap/services/user/src/tests/commands/user.test.js +22 -0
  102. package/install-from-clone.sh +30 -0
  103. package/package.json +1 -1
  104. package/src/cli.js +8 -0
  105. package/src/config.js +2 -1
  106. package/src/dev/commands.js +10 -2
  107. package/src/dev/dev.service.js +167 -52
  108. package/src/provisioner/Dockerfile +27 -0
  109. package/src/provisioner/package.json +19 -0
  110. package/src/provisioner/src/app.js +56 -0
  111. package/src/provisioner/src/services/mongodb.js +58 -0
  112. package/src/provisioner/src/services/mysql.js +51 -0
  113. package/src/provisioner/src/services/secrets.js +84 -0
  114. package/src/scaffolder/commands.js +12 -5
  115. package/src/scaffolder/scaffolder.handler.js +256 -58
  116. package/templates/service/Dockerfile.hbs +4 -1
  117. package/templates/service/package.json.hbs +14 -16
  118. package/templates/service/src/app.js.hbs +53 -0
  119. package/templates/service/{commands → src/commands}/{{serviceName}}.handler.js.hbs +2 -3
  120. package/templates/service/src/mongodb.config.js.hbs +49 -0
  121. package/templates/service/{config.js.hbs → src/mysql.config.js.hbs} +10 -0
  122. package/{bootstrap/services/agent/src/schema/Agent.schema.js → templates/service/src/schema/{{serviceName}}.schema.js.hbs} +3 -3
  123. package/templates/service/src/services/mongodb.{{serviceName}}.service.js.hbs +70 -0
  124. package/bootstrap/services/agent/notes.md +0 -28
  125. package/bootstrap/services/agent/package.json +0 -16
  126. package/bootstrap/services/agent/src/app.js +0 -52
  127. package/bootstrap/services/agent/src/commands/agent.handler.js +0 -104
  128. package/bootstrap/services/agent/src/config.js +0 -52
  129. package/bootstrap/services/agent/src/controllers/http.controller.js +0 -44
  130. package/bootstrap/services/agent/src/controllers/message.controller.js +0 -51
  131. package/bootstrap/services/agent/src/db/migrations/01-init.js +0 -50
  132. package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +0 -36
  133. package/bootstrap/services/agent/src/policies/agent.policy.js +0 -13
  134. package/bootstrap/services/agent/src/services/agent.service.js +0 -259
  135. package/bootstrap/services/agent/src/services/chatgpt.service.js +0 -46
  136. package/bootstrap/services/agent/src/services/manifest.service.js +0 -21
  137. package/bootstrap/services/portal/Dockerfile.remote +0 -40
  138. package/bootstrap/services/portal/public/favicon.ico +0 -0
  139. package/bootstrap/services/portal/public/gnar-white.png +0 -0
  140. package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
  141. package/bootstrap/services/portal/public/index.html +0 -43
  142. package/bootstrap/services/portal/public/logo192.png +0 -0
  143. package/bootstrap/services/portal/public/logo512.png +0 -0
  144. package/bootstrap/services/portal/public/manifest.json +0 -25
  145. package/bootstrap/services/portal/public/robots.txt +0 -3
  146. package/bootstrap/services/portal/src/App.js +0 -56
  147. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +0 -1
  148. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +0 -1
  149. package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +0 -1
  150. package/bootstrap/services/portal/src/assets/activity.svg +0 -3
  151. package/bootstrap/services/portal/src/assets/arrow.svg +0 -3
  152. package/bootstrap/services/portal/src/assets/bin-white.svg +0 -3
  153. package/bootstrap/services/portal/src/assets/bin.svg +0 -3
  154. package/bootstrap/services/portal/src/assets/check.svg +0 -3
  155. package/bootstrap/services/portal/src/assets/chevron.svg +0 -3
  156. package/bootstrap/services/portal/src/assets/contact.svg +0 -3
  157. package/bootstrap/services/portal/src/assets/dots-vertical.svg +0 -5
  158. package/bootstrap/services/portal/src/assets/eye-off.svg +0 -3
  159. package/bootstrap/services/portal/src/assets/eye.svg +0 -4
  160. package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +0 -47
  161. package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +0 -47
  162. package/bootstrap/services/portal/src/assets/gnar_engine.svg +0 -3
  163. package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
  164. package/bootstrap/services/portal/src/assets/home.svg +0 -3
  165. package/bootstrap/services/portal/src/assets/link.svg +0 -3
  166. package/bootstrap/services/portal/src/assets/lock.svg +0 -3
  167. package/bootstrap/services/portal/src/assets/package.svg +0 -4
  168. package/bootstrap/services/portal/src/assets/raffle.svg +0 -3
  169. package/bootstrap/services/portal/src/assets/settings.svg +0 -4
  170. package/bootstrap/services/portal/src/assets/shopping-bag.svg +0 -3
  171. package/bootstrap/services/portal/src/assets/user-black.svg +0 -3
  172. package/bootstrap/services/portal/src/assets/user.svg +0 -3
  173. package/bootstrap/services/portal/src/assets/users.svg +0 -3
  174. package/bootstrap/services/portal/src/assets/wallet.svg +0 -3
  175. package/bootstrap/services/portal/src/data/data.js +0 -70
  176. package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +0 -32
  177. package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +0 -160
  178. package/bootstrap/services/portal/src/features/crud/crudEdit.less +0 -230
  179. package/bootstrap/services/portal/src/features/crud/crudList.less +0 -134
  180. package/bootstrap/services/portal/src/features/crud/crudPage.less +0 -31
  181. package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +0 -108
  182. package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +0 -243
  183. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +0 -109
  184. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +0 -315
  185. package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +0 -104
  186. package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +0 -388
  187. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +0 -104
  188. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +0 -208
  189. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +0 -110
  190. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +0 -261
  191. package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +0 -107
  192. package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +0 -402
  193. package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +0 -30
  194. package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +0 -113
  195. package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +0 -56
  196. package/bootstrap/services/portal/src/features/loginForm/loginForm.less +0 -56
  197. package/bootstrap/services/portal/src/features/notes/Notes.jsx +0 -18
  198. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +0 -96
  199. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +0 -74
  200. package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +0 -102
  201. package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +0 -24
  202. package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +0 -99
  203. package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +0 -46
  204. package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +0 -64
  205. package/bootstrap/services/portal/src/features/sidebar/sidebar.less +0 -49
  206. package/bootstrap/services/portal/src/features/skus/Skus.jsx +0 -109
  207. package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +0 -44
  208. package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +0 -32
  209. package/bootstrap/services/portal/src/features/user/User.jsx +0 -54
  210. package/bootstrap/services/portal/src/features/user/user.less +0 -57
  211. package/bootstrap/services/portal/src/includes/utilities.js +0 -259
  212. package/bootstrap/services/portal/src/index.js +0 -14
  213. package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +0 -50
  214. package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +0 -17
  215. package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +0 -48
  216. package/bootstrap/services/portal/src/layouts/loginLayout.less +0 -33
  217. package/bootstrap/services/portal/src/layouts/portalLayout.less +0 -67
  218. package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +0 -199
  219. package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +0 -17
  220. package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +0 -10
  221. package/bootstrap/services/portal/src/pages/login/Login.jsx +0 -15
  222. package/bootstrap/services/portal/src/pages/login/login.less +0 -10
  223. package/bootstrap/services/portal/src/pages/orders/Orders.jsx +0 -199
  224. package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +0 -15
  225. package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +0 -15
  226. package/bootstrap/services/portal/src/pages/payments/Payments.jsx +0 -10
  227. package/bootstrap/services/portal/src/pages/portal/Portal.jsx +0 -43
  228. package/bootstrap/services/portal/src/pages/products/Products.jsx +0 -212
  229. package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +0 -124
  230. package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +0 -186
  231. package/bootstrap/services/portal/src/pages/reports/Reports.jsx +0 -10
  232. package/bootstrap/services/portal/src/pages/settings/Settings.jsx +0 -10
  233. package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +0 -199
  234. package/bootstrap/services/portal/src/pages/users/Users.jsx +0 -193
  235. package/bootstrap/services/portal/src/pages/users/users.less +0 -25
  236. package/bootstrap/services/portal/src/styles/inputs.less +0 -157
  237. package/bootstrap/services/portal/src/styles/main.less +0 -26
  238. package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +0 -97
  239. package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +0 -23
  240. package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +0 -17
  241. package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +0 -42
  242. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +0 -63
  243. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +0 -63
  244. package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +0 -19
  245. package/bootstrap/services/portal/src/ui/loader/Loader.jsx +0 -12
  246. package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +0 -23
  247. package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +0 -29
  248. package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +0 -69
  249. package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +0 -27
  250. package/bootstrap/services/user/src/tests/user.test.js +0 -126
  251. package/templates/service/app.js.hbs +0 -38
  252. package/templates/service/schema/{{serviceName}}.schema.js.hbs +0 -14
  253. /package/bootstrap/services/portal/src/{ui/saveButton/saveButton.less → components/CustomSelect/CustomSelect.less} +0 -0
  254. /package/templates/service/{controllers → src/controllers}/http.controller.js.hbs +0 -0
  255. /package/templates/service/{controllers → src/controllers}/message.controller.js.hbs +0 -0
  256. /package/templates/service/{db → src/mysql.db}/migrations/01-init.js.hbs +0 -0
  257. /package/templates/service/{db → src/mysql.db}/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +0 -0
  258. /package/templates/service/{policies → src/policies}/{{serviceName}}.policy.js.hbs +0 -0
  259. /package/templates/service/{services/{{serviceName}}.service.js.hbs → src/services/mysql.{{serviceName}}.service.js.hbs} +0 -0
@@ -0,0 +1,338 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import Card from '../../layouts/Card/Card';
3
+ import SelectRepeater from '../../elements/SelectRepeater/SelectRepeater';
4
+ import SaveButton from '../../elements/SaveButton/SaveButton';
5
+ import TextInput from '../../elements/TextInput/TextInput';
6
+ import CustomSelect from '../../elements/CustomSelect/CustomSelect';
7
+ import PageBlockSwitch from '../../components/PageBlockSwitch/PageBlockSwitch';
8
+ import { pages } from '../../services/page.js';
9
+ import { blocks } from '../../services/block.js';
10
+ import { fieldTypes } from '../../data/pages.data.js';
11
+ import { useParams } from "react-router-dom";
12
+ import { v4 as uuidv4 } from 'uuid';
13
+
14
+ function PageSinglePage() {
15
+
16
+ const { id } = useParams();
17
+ const [pageId, setPageId] = useState('');
18
+ const [pageDetails, setPageDetails] = useState({});
19
+ const [pageBlocks, setPageBlocks] = useState([]);
20
+ const [allBlocks, setAllBlocks] = useState([]);
21
+ const [errors, setErrors] = useState([]);
22
+ const [loading, setLoading] = useState(false);
23
+
24
+ useEffect(() => {
25
+ // fetch all blocks
26
+ (async () => {
27
+ try {
28
+ const blocksData = await blocks.getMany();
29
+ blocksData.blocks = blocksData.blocks.map(b => {
30
+ delete b.id;
31
+ return b;
32
+ })
33
+ setAllBlocks(blocksData.blocks || []);
34
+ } catch (error) {
35
+ console.error('Error fetching blocks data:', error);
36
+ setErrors([error]);
37
+ }
38
+ })();
39
+ }, []);
40
+
41
+ useEffect(() => {
42
+ console.log('Current page blocks:', JSON.stringify(pageBlocks));
43
+ }, [pageBlocks]);
44
+
45
+ // fetch data if editing existing
46
+ useEffect(() => {
47
+ if (id && id !== 'new') {
48
+ (async () => {
49
+ setLoading(true);
50
+ try {
51
+ let pageData = await pages.getSingle(id);
52
+ pageData = pageData.page;
53
+ setPageId(pageData.id);
54
+ setPageDetails({
55
+ pageName: pageData.name,
56
+ pageKey: pageData.key
57
+ });
58
+ setPageBlocks(pageData.blocks || []);
59
+ setErrors([]);
60
+ } catch (error) {
61
+ console.error('Error fetching page data:', error);
62
+ setErrors([error]);
63
+ }
64
+ setLoading(false);
65
+ })();
66
+ }
67
+ }, [id]);
68
+
69
+ const updatePageBlocks = (newBlock, parentInstanceId = null, fieldKey = null, remove = false) => {
70
+
71
+ if (!newBlock.instanceId) {
72
+ newBlock.instanceId = uuidv4();
73
+ }
74
+
75
+ let newPageBlocks = [];
76
+ let foundBlock = false;
77
+ const blocks = [...pageBlocks];
78
+
79
+ console.log('Remove', remove, newBlock);
80
+
81
+ const updateBlocksRecursively = (blocks) => {
82
+ blocks = blocks.map(block => {
83
+ // found the block, update it
84
+ if (newBlock.instanceId && block.instanceId === newBlock.instanceId) {
85
+ foundBlock = true;
86
+ if (remove) {
87
+ console.log('Removing block', newBlock);
88
+ return null;
89
+ }
90
+ return newBlock;
91
+ }
92
+
93
+ // check children recursively
94
+ if (block.fields && block.fields.length > 0) {
95
+ return {
96
+ ...block,
97
+ fields: block.fields.map(field => {
98
+ if (field.type === 'repeater' && Array.isArray(field.value)) {
99
+ return {
100
+ ...field,
101
+ value: updateBlocksRecursively(field.value)
102
+ };
103
+ }
104
+ return field;
105
+ })
106
+ };
107
+ }
108
+
109
+ // otherwise return as is
110
+ return block;
111
+ });
112
+
113
+ // filter out any nulls (removed blocks)
114
+ return blocks.filter(b => b !== null);
115
+ };
116
+
117
+ newPageBlocks = updateBlocksRecursively(blocks);
118
+
119
+ // add new block
120
+ if (!foundBlock) {
121
+ // add to parent
122
+ if (parentInstanceId && fieldKey) {
123
+ const addBlockToParentRecursively = (blocks) => {
124
+ return blocks.map(block => {
125
+ // found the parent block, add to its fields
126
+ if (block.instanceId === parentInstanceId) {
127
+ console.log('adding to parent instance ', block.instanceId, 'with', newBlock);
128
+ return {
129
+ ...block,
130
+ fields: block.fields.map(field => {
131
+ console.log('checking field', field, fieldKey, field.repeaterType)
132
+ if (field.repeaterType && field.repeaterType === fieldKey) {
133
+ if (!Array.isArray(field.value)) {
134
+ field.value = [];
135
+ }
136
+
137
+ console.log('WOOP');
138
+
139
+ return {
140
+ ...field,
141
+ value: [...field.value, newBlock]
142
+ };
143
+ }
144
+ return field;
145
+ })
146
+ };
147
+ }
148
+
149
+ // check children recursively
150
+ if (block.fields && block.fields.length > 0) {
151
+ return {
152
+ ...block,
153
+ fields: block.fields.map(field => {
154
+ if (field.type === 'repeater' && Array.isArray(field.value)) {
155
+ return {
156
+ ...field,
157
+ value: addBlockToParentRecursively(field.value)
158
+ };
159
+ }
160
+ return field;
161
+ })
162
+ };
163
+ }
164
+
165
+ // otherwise return as is
166
+ return block;
167
+ });
168
+ };
169
+
170
+ newPageBlocks = addBlockToParentRecursively(newPageBlocks);
171
+ }
172
+
173
+ // otherwise top level
174
+ else {
175
+ newPageBlocks.push(newBlock);
176
+ }
177
+ }
178
+
179
+ setPageBlocks(newPageBlocks);
180
+ }
181
+
182
+ const validateField = (e) => {
183
+ const value = e.target.value;
184
+ setErrors([]);
185
+
186
+ }
187
+
188
+ const save = async () => {
189
+ setLoading(true);
190
+ console.log('Saving...', { pageId });
191
+
192
+ // create new
193
+ if (!pageId) {
194
+ try {
195
+ await pages.create({
196
+ name: pageDetails.pageName,
197
+ key: pageDetails.pageKey,
198
+ blocks: pageBlocks
199
+ })
200
+ setErrors([]);
201
+ } catch (error) {
202
+ console.error('Error creating page:', error);
203
+ setErrors([error]);
204
+ }
205
+ }
206
+
207
+ // update existing
208
+ else {
209
+ try {
210
+ await pages.update(pageId, {
211
+ name: pageDetails.pageName,
212
+ key: pageDetails.pageKey,
213
+ blocks: pageBlocks
214
+ });
215
+ setErrors([]);
216
+ } catch (error) {
217
+ console.error('Error creating page:', error);
218
+ setErrors([error]);
219
+ }
220
+ }
221
+
222
+ setLoading(false);
223
+ }
224
+
225
+ return (
226
+ <div className="single-crud-page">
227
+ <h1>Create / Update Page</h1>
228
+
229
+ <div className="flex-row top-bar">
230
+ <p className="single-crud-id">{pageId || 'Creating new page...'}</p>
231
+ <div className="button-group">
232
+ <button onClick={() => window.history.back()} className="secondary-btn">Back</button>
233
+ <button onClick={save}>Save</button>
234
+ </div>
235
+ </div>
236
+
237
+ <div className="card-columns">
238
+ <div className="col-66">
239
+ <p className="instruction">Add blocks below, then add your content and save.</p>
240
+
241
+ {allBlocks &&
242
+ <SelectRepeater
243
+ items={pageBlocks}updatePageBlocks
244
+ setItem={(item, index, remove = false) => {
245
+ updatePageBlocks({
246
+ ...item,
247
+ fields: item.fields.map(f => ({ ...f }))
248
+ }, null, null, remove);
249
+ }}
250
+ selectLabel="Add Block"
251
+ selectText="Select a block to add"
252
+ selectOptions={allBlocks}
253
+ selectLabelKey="name"
254
+ renderRow= {(item, index, updateItem) => (
255
+ <Card
256
+ title={`Block - ${item.name}`}
257
+ >
258
+ <PageBlockSwitch
259
+ block={item}
260
+ pageBlocks={pageBlocks}
261
+ updatePageBlocks={updatePageBlocks}
262
+ blockIndex={index}
263
+ allBlocks={allBlocks}
264
+ parentBlockInstanceId={null}
265
+ />
266
+ </Card>
267
+ )}
268
+ />
269
+ }
270
+ </div>
271
+
272
+ <div className="col-33">
273
+ <Card
274
+ title="Page details"
275
+ >
276
+ <div className="flex-col">
277
+ <TextInput
278
+ label="Page Name"
279
+ placeholder="Enter page Name"
280
+ value={pageDetails.pageName || ''}
281
+ onChange={(e) => {
282
+ validateField(e)
283
+ setPageDetails({
284
+ ...pageDetails,
285
+ pageName: e.target.value
286
+ })
287
+ }}
288
+ errorMessage="Invalid page name"
289
+ isValid={true}
290
+ />
291
+
292
+ <TextInput
293
+ label="Page Key"
294
+ placeholder="Enter page key"
295
+ value={pageDetails.pageKey || ''}
296
+ onChange={(e) => {
297
+ validateField(e)
298
+ setPageDetails({
299
+ ...pageDetails,
300
+ pageKey: e.target.value
301
+ })
302
+ }}
303
+ errorMessage="Invalid page key"
304
+ isValid={true}
305
+ />
306
+ </div>
307
+ </Card>
308
+ </div>
309
+ </div>
310
+
311
+ <div className="flex-row bottom-bar">
312
+ <div className="button-group">
313
+ <SaveButton
314
+ onClick={save}
315
+ itemName="Page"
316
+ loading={loading}
317
+ error={errors.length > 0}
318
+ isNew={!pageId}
319
+ />
320
+ </div>
321
+ </div>
322
+
323
+ <div>
324
+ {errors.length > 0 && (
325
+ <div className="error-messages">
326
+ <ul>
327
+ {errors.map((error, index) => (
328
+ <li key={index}>{error.message}</li>
329
+ ))}
330
+ </ul>
331
+ </div>
332
+ )}
333
+ </div>
334
+ </div>
335
+ )
336
+ }
337
+
338
+ export default PageSinglePage;
@@ -0,0 +1,23 @@
1
+ import CrudList from '../../components/CrudList/CrudList';
2
+ import { pages } from '../../services/page.js';
3
+
4
+ function PagesPage() {
5
+
6
+ return (
7
+ <div>
8
+ <h1>Manage Pages</h1>
9
+ <CrudList
10
+ entityKey="pages"
11
+ fetchData={pages.getMany}
12
+ entitySingleName="Page"
13
+ entityPluralName="Pages"
14
+ columns={[
15
+ { key: 'id', label: 'ID' },
16
+ { key: 'name', label: 'Page name' }
17
+ ]}
18
+ />
19
+ </div>
20
+ )
21
+ }
22
+
23
+ export default PagesPage;
@@ -0,0 +1,9 @@
1
+
2
+ function UserSinglePage() {
3
+
4
+ return (
5
+ <div>User Single Page</div>
6
+ )
7
+ }
8
+
9
+ export default UserSinglePage;
@@ -0,0 +1,25 @@
1
+ import CrudList from '../../components/CrudList/CrudList';
2
+ import { user } from '../../services/user.js';
3
+
4
+ function UsersPage() {
5
+
6
+ return (
7
+ <div>
8
+ <h1>Manage Users</h1>
9
+ <CrudList
10
+ entityKey="users"
11
+ fetchData={user.getMany}
12
+ entitySingleName="User"
13
+ entityPluralName="Users"
14
+ columns={[
15
+ { key: 'id', label: 'ID' },
16
+ { key: 'username', label: 'Username' },
17
+ { key: 'email', label: 'Email' },
18
+ { key: 'role', label: 'Role' }
19
+ ]}
20
+ />
21
+ </div>
22
+ )
23
+ }
24
+
25
+ export default UsersPage;
@@ -0,0 +1,28 @@
1
+ import client from './client.js';
2
+
3
+ export const blocks = {
4
+ getMany: async () => {
5
+ const { data } = await client.get('/blocks/');
6
+ return data;
7
+ },
8
+
9
+ getSingle: async ( id ) => {
10
+ const { data } = await client.get(`/blocks/${id}`);
11
+ return data;
12
+ },
13
+
14
+ create: async ( block ) => {
15
+ const { data } = await client.post('/blocks/', { block });
16
+ return data;
17
+ },
18
+
19
+ update: async ( id, block ) => {
20
+ const { data } = await client.post(`/blocks/${id}`, { block });
21
+ return data;
22
+ },
23
+
24
+ delete: async ( id ) => {
25
+ await client.delete(`/users/${id}`);
26
+ }
27
+ };
28
+
@@ -0,0 +1,67 @@
1
+ import axios from 'axios';
2
+
3
+ import { getAuthToken, setAuthToken, removeAuthToken, removeAuthUser } from './storage.js';
4
+
5
+ // Determine the correct API URL based on the environment
6
+ const baseApiUrl = 'http://localhost';
7
+
8
+ const client = axios.create({
9
+ baseURL: baseApiUrl,
10
+ withCredentials: true,
11
+ });
12
+
13
+ // Attach authorisation header to requests
14
+ client.interceptors.request.use(
15
+ (config) => {
16
+ // Authorization header
17
+ let authToken = getAuthToken();
18
+
19
+ if (authToken) {
20
+ config.headers['Authorization'] = `Bearer ${authToken}`;
21
+ }
22
+
23
+ return config;
24
+ },
25
+ (error) => {
26
+ return Promise.reject(error);
27
+ }
28
+ );
29
+
30
+ client.interceptors.response.use(
31
+ (response) => {
32
+ // update auth token if new one provided
33
+ const newAuthToken = response.headers['authorization'];
34
+
35
+ if (newAuthToken) {
36
+ const token = newAuthToken.split(' ')[1];
37
+ setAuthToken(token);
38
+ console.log('refreshed token');
39
+ }
40
+
41
+ return response;
42
+ },
43
+ (error) => {
44
+ // Log out if 401
45
+ if (error.response.status === 401) {
46
+
47
+ // remove auth token and user from storage
48
+ removeAuthToken();
49
+ removeAuthUser();
50
+
51
+ // redirect to login page
52
+ window.location.href = '/portal/login'
53
+
54
+ }
55
+
56
+ if (error.response.data.message) {
57
+ return Promise.reject({
58
+ message: error.response.data.message
59
+ });
60
+ }
61
+
62
+ return Promise.reject(error);
63
+ }
64
+ );
65
+
66
+ export default client;
67
+
@@ -0,0 +1,14 @@
1
+ import CryptoJS from "crypto-js";
2
+
3
+ export function getGravatarUrl(email, options) {
4
+ const {
5
+ size = 200,
6
+ defaultImage = "mp",
7
+ rating = "g"
8
+ } = options;
9
+
10
+ const normalized = email.trim().toLowerCase();
11
+ const hash = CryptoJS.MD5(normalized).toString(CryptoJS.enc.Hex);
12
+
13
+ return `https://www.gravatar.com/avatar/${hash}?s=${size}&d=${defaultImage}&r=${rating}`;
14
+ }
@@ -0,0 +1,28 @@
1
+ import client from './client.js';
2
+
3
+ export const pages = {
4
+ getMany: async () => {
5
+ const { data } = await client.get('/pages/');
6
+ return data;
7
+ },
8
+
9
+ getSingle: async ( id ) => {
10
+ const { data } = await client.get(`/pages/${id}`);
11
+ return data;
12
+ },
13
+
14
+ create: async ( page ) => {
15
+ const { data } = await client.post('/pages/', { page });
16
+ return data;
17
+ },
18
+
19
+ update: async ( id, page ) => {
20
+ const { data } = await client.post(`/pages/${id}`, { page });
21
+ return data;
22
+ },
23
+
24
+ delete: async ( id ) => {
25
+ await client.delete(`/pages/${id}`);
26
+ }
27
+ };
28
+
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Auth token
3
+ */
4
+ export function getAuthToken() {
5
+ if (typeof window === 'undefined') return null;
6
+ try {
7
+ return localStorage.getItem('GE_AUTH_TOKEN');
8
+ } catch (error) {
9
+ console.error('Error getting auth token:', error);
10
+ return null;
11
+ }
12
+ }
13
+
14
+ export function setAuthToken(authToken) {
15
+ if (typeof window === 'undefined') return;
16
+ try {
17
+ localStorage.setItem('GE_AUTH_TOKEN', authToken);
18
+ } catch (error) {
19
+ console.error('Error setting auth token:', error);
20
+ }
21
+ }
22
+
23
+ export function removeAuthToken() {
24
+ if (typeof window === 'undefined') return;
25
+ try {
26
+ localStorage.removeItem('GE_AUTH_TOKEN');
27
+ } catch (error) {
28
+ console.error('Error removing auth token:', error);
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Auth user
34
+ */
35
+ export function getAuthUser() {
36
+ if (typeof window === 'undefined') return null;
37
+ try {
38
+ return localStorage.getItem('GE_AUTH_USER');
39
+ } catch (error) {
40
+ console.error('Error getting auth user:', error);
41
+ return null;
42
+ }
43
+ }
44
+
45
+ export function setAuthUser(authUser) {
46
+ if (typeof window === 'undefined') return;
47
+ try {
48
+ localStorage.setItem('GE_AUTH_USER', authUser);
49
+ } catch (error) {
50
+ console.error('Error setting auth user:', error);
51
+ }
52
+ }
53
+
54
+ export function removeAuthUser() {
55
+ if (typeof window === 'undefined') return;
56
+ try {
57
+ localStorage.removeItem('GE_AUTH_USER');
58
+ } catch (error) {
59
+ console.error('Error removing auth user:', error);
60
+ }
61
+ }
62
+
@@ -0,0 +1,41 @@
1
+ import client from './client.js';
2
+
3
+ export const user = {
4
+ authenticate: async ({ username, password }) => {
5
+ const { data } = await client.post('/authenticate/', { username, password });
6
+ return data;
7
+ },
8
+
9
+ getMany: async () => {
10
+ const { data } = await client.get('/users/');
11
+ return data;
12
+ },
13
+
14
+ getUser: async ({ userId }) => {
15
+ const { data } = await client.get(`/users/${userId}`);
16
+ return data;
17
+ },
18
+
19
+ createUser: async ({ user }) => {
20
+ const { data } = await client.post('/users/', { user });
21
+ return data;
22
+ },
23
+
24
+ update: async ({ id, user }) => {
25
+ const { data } = await client.post(`/users/${id}`, { user });
26
+ return data;
27
+ },
28
+
29
+ delete: async ({ userId }) => {
30
+ await client.delete(`/users/${userId}`);
31
+ },
32
+
33
+ sendPasswordReset: async ({ email }) => {
34
+ await client.post('/users/request-password-reset', { email });
35
+ },
36
+
37
+ changePassword: async ({ email, token, password }) => {
38
+ await client.post('/users/change-password', { email, token, password });
39
+ },
40
+ };
41
+