@gnar-engine/cli 1.0.0 → 1.0.2

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 (230) hide show
  1. package/assets/gnar-engine-logo-white.svg +9 -0
  2. package/bootstrap/deploy.localdev.yml +51 -0
  3. package/bootstrap/secrets.localdev.yml +27 -0
  4. package/bootstrap/services/agent/Dockerfile +23 -0
  5. package/bootstrap/services/agent/notes.md +28 -0
  6. package/bootstrap/services/agent/package.json +16 -0
  7. package/bootstrap/services/agent/src/app.js +52 -0
  8. package/bootstrap/services/agent/src/commands/agent.handler.js +104 -0
  9. package/bootstrap/services/agent/src/config.js +52 -0
  10. package/bootstrap/services/agent/src/controllers/http.controller.js +44 -0
  11. package/bootstrap/services/agent/src/controllers/message.controller.js +51 -0
  12. package/bootstrap/services/agent/src/db/migrations/01-init.js +50 -0
  13. package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +36 -0
  14. package/bootstrap/services/agent/src/policies/agent.policy.js +13 -0
  15. package/bootstrap/services/agent/src/schema/Agent.schema.js +17 -0
  16. package/bootstrap/services/agent/src/services/agent.service.js +259 -0
  17. package/bootstrap/services/agent/src/services/chatgpt.service.js +46 -0
  18. package/bootstrap/services/agent/src/services/manifest.service.js +21 -0
  19. package/bootstrap/services/control/Dockerfile +23 -0
  20. package/bootstrap/services/control/Dockerfile.prod +37 -0
  21. package/bootstrap/services/control/README.md +25 -0
  22. package/bootstrap/services/control/package.json +16 -0
  23. package/bootstrap/services/control/src/app.js +45 -0
  24. package/bootstrap/services/control/src/commands/control.handler.js +231 -0
  25. package/bootstrap/services/control/src/commands/service.handler.js +81 -0
  26. package/bootstrap/services/control/src/commands/task.handler.js +247 -0
  27. package/bootstrap/services/control/src/config.js +55 -0
  28. package/bootstrap/services/control/src/controllers/http.controller.js +228 -0
  29. package/bootstrap/services/control/src/controllers/message.controller.js +40 -0
  30. package/bootstrap/services/control/src/db/migrations/01-init.js +50 -0
  31. package/bootstrap/services/control/src/db/migrations/02-control-service-init.js +60 -0
  32. package/bootstrap/services/control/src/db/migrations/03-alter-tasks.js +29 -0
  33. package/bootstrap/services/control/src/policies/task.policy.js +53 -0
  34. package/bootstrap/services/control/src/schema/control.schema.js +42 -0
  35. package/bootstrap/services/control/src/services/registry.service.js +83 -0
  36. package/bootstrap/services/control/src/services/reset.service.js +28 -0
  37. package/bootstrap/services/control/src/services/task.service.js +153 -0
  38. package/bootstrap/services/control/src/tests/control.test.js +50 -0
  39. package/bootstrap/services/notification/Dockerfile +23 -0
  40. package/bootstrap/services/notification/Dockerfile.prod +37 -0
  41. package/bootstrap/services/notification/README.md +3 -0
  42. package/bootstrap/services/notification/package.json +34 -0
  43. package/bootstrap/services/notification/src/app.js +51 -0
  44. package/bootstrap/services/notification/src/commands/command-bus.js +20 -0
  45. package/bootstrap/services/notification/src/commands/handlers/control.handler.js +18 -0
  46. package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +157 -0
  47. package/bootstrap/services/notification/src/config.js +15 -0
  48. package/bootstrap/services/notification/src/controllers/message.controller.js +82 -0
  49. package/bootstrap/services/notification/src/services/logger.service.js +16 -0
  50. package/bootstrap/services/notification/src/services/ses.service.js +23 -0
  51. package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +136 -0
  52. package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +87 -0
  53. package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +132 -0
  54. package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +77 -0
  55. package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
  56. package/bootstrap/services/portal/Dockerfile +23 -0
  57. package/bootstrap/services/portal/Dockerfile.remote +40 -0
  58. package/bootstrap/services/portal/README.md +22 -0
  59. package/bootstrap/services/portal/nginx.conf +12 -0
  60. package/bootstrap/services/portal/package.json +59 -0
  61. package/bootstrap/services/portal/public/favicon.ico +0 -0
  62. package/bootstrap/services/portal/public/gnar-white.png +0 -0
  63. package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
  64. package/bootstrap/services/portal/public/index.html +43 -0
  65. package/bootstrap/services/portal/public/logo192.png +0 -0
  66. package/bootstrap/services/portal/public/logo512.png +0 -0
  67. package/bootstrap/services/portal/public/manifest.json +25 -0
  68. package/bootstrap/services/portal/public/robots.txt +3 -0
  69. package/bootstrap/services/portal/src/App.js +56 -0
  70. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +1 -0
  71. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +1 -0
  72. package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +1 -0
  73. package/bootstrap/services/portal/src/assets/activity.svg +3 -0
  74. package/bootstrap/services/portal/src/assets/arrow.svg +3 -0
  75. package/bootstrap/services/portal/src/assets/bin-white.svg +3 -0
  76. package/bootstrap/services/portal/src/assets/bin.svg +3 -0
  77. package/bootstrap/services/portal/src/assets/check.svg +3 -0
  78. package/bootstrap/services/portal/src/assets/chevron.svg +3 -0
  79. package/bootstrap/services/portal/src/assets/contact.svg +3 -0
  80. package/bootstrap/services/portal/src/assets/dots-vertical.svg +5 -0
  81. package/bootstrap/services/portal/src/assets/eye-off.svg +3 -0
  82. package/bootstrap/services/portal/src/assets/eye.svg +4 -0
  83. package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +47 -0
  84. package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +47 -0
  85. package/bootstrap/services/portal/src/assets/gnar_engine.svg +3 -0
  86. package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
  87. package/bootstrap/services/portal/src/assets/home.svg +3 -0
  88. package/bootstrap/services/portal/src/assets/link.svg +3 -0
  89. package/bootstrap/services/portal/src/assets/lock.svg +3 -0
  90. package/bootstrap/services/portal/src/assets/package.svg +4 -0
  91. package/bootstrap/services/portal/src/assets/raffle.svg +3 -0
  92. package/bootstrap/services/portal/src/assets/settings.svg +4 -0
  93. package/bootstrap/services/portal/src/assets/shopping-bag.svg +3 -0
  94. package/bootstrap/services/portal/src/assets/user-black.svg +3 -0
  95. package/bootstrap/services/portal/src/assets/user.svg +3 -0
  96. package/bootstrap/services/portal/src/assets/users.svg +3 -0
  97. package/bootstrap/services/portal/src/assets/wallet.svg +3 -0
  98. package/bootstrap/services/portal/src/css/style.css +1007 -0
  99. package/bootstrap/services/portal/src/data/data.js +70 -0
  100. package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +32 -0
  101. package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +160 -0
  102. package/bootstrap/services/portal/src/features/crud/crudEdit.less +230 -0
  103. package/bootstrap/services/portal/src/features/crud/crudList.less +134 -0
  104. package/bootstrap/services/portal/src/features/crud/crudPage.less +31 -0
  105. package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +108 -0
  106. package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +243 -0
  107. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +109 -0
  108. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +315 -0
  109. package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +104 -0
  110. package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +388 -0
  111. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +104 -0
  112. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +208 -0
  113. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +110 -0
  114. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +261 -0
  115. package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +107 -0
  116. package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +402 -0
  117. package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +30 -0
  118. package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +113 -0
  119. package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +56 -0
  120. package/bootstrap/services/portal/src/features/loginForm/loginForm.less +56 -0
  121. package/bootstrap/services/portal/src/features/notes/Notes.jsx +18 -0
  122. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +96 -0
  123. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +74 -0
  124. package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +102 -0
  125. package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +24 -0
  126. package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +99 -0
  127. package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +46 -0
  128. package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +64 -0
  129. package/bootstrap/services/portal/src/features/sidebar/sidebar.less +49 -0
  130. package/bootstrap/services/portal/src/features/skus/Skus.jsx +109 -0
  131. package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +44 -0
  132. package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +32 -0
  133. package/bootstrap/services/portal/src/features/user/User.jsx +54 -0
  134. package/bootstrap/services/portal/src/features/user/user.less +57 -0
  135. package/bootstrap/services/portal/src/includes/utilities.js +259 -0
  136. package/bootstrap/services/portal/src/index.js +14 -0
  137. package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +50 -0
  138. package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +17 -0
  139. package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +48 -0
  140. package/bootstrap/services/portal/src/layouts/loginLayout.less +33 -0
  141. package/bootstrap/services/portal/src/layouts/portalLayout.less +67 -0
  142. package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +199 -0
  143. package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +17 -0
  144. package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +10 -0
  145. package/bootstrap/services/portal/src/pages/login/Login.jsx +15 -0
  146. package/bootstrap/services/portal/src/pages/login/login.less +10 -0
  147. package/bootstrap/services/portal/src/pages/orders/Orders.jsx +199 -0
  148. package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +15 -0
  149. package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +15 -0
  150. package/bootstrap/services/portal/src/pages/payments/Payments.jsx +10 -0
  151. package/bootstrap/services/portal/src/pages/portal/Portal.jsx +43 -0
  152. package/bootstrap/services/portal/src/pages/products/Products.jsx +212 -0
  153. package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +124 -0
  154. package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +186 -0
  155. package/bootstrap/services/portal/src/pages/reports/Reports.jsx +10 -0
  156. package/bootstrap/services/portal/src/pages/settings/Settings.jsx +10 -0
  157. package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +199 -0
  158. package/bootstrap/services/portal/src/pages/users/Users.jsx +193 -0
  159. package/bootstrap/services/portal/src/pages/users/users.less +25 -0
  160. package/bootstrap/services/portal/src/slices/authSlice.js +71 -0
  161. package/bootstrap/services/portal/src/store/configureStore.js +12 -0
  162. package/bootstrap/services/portal/src/styles/global.less +159 -0
  163. package/bootstrap/services/portal/src/styles/inputs.less +157 -0
  164. package/bootstrap/services/portal/src/styles/main.less +26 -0
  165. package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +97 -0
  166. package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +23 -0
  167. package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +17 -0
  168. package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +42 -0
  169. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +63 -0
  170. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +63 -0
  171. package/bootstrap/services/portal/src/ui/customSelect/CustomSelect.jsx +63 -0
  172. package/bootstrap/services/portal/src/ui/customSelect/customSelect.less +92 -0
  173. package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +19 -0
  174. package/bootstrap/services/portal/src/ui/loader/Loader.jsx +12 -0
  175. package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +23 -0
  176. package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +29 -0
  177. package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +69 -0
  178. package/bootstrap/services/portal/src/ui/saveButton/saveButton.less +0 -0
  179. package/bootstrap/services/rabbit-mq/Dockerfile.prod +9 -0
  180. package/bootstrap/services/user/Dockerfile +23 -0
  181. package/bootstrap/services/user/Dockerfile.prod +37 -0
  182. package/bootstrap/services/user/README.md +8 -0
  183. package/bootstrap/services/user/package.json +16 -0
  184. package/bootstrap/services/user/src/app.js +45 -0
  185. package/bootstrap/services/user/src/commands/session.handler.js +23 -0
  186. package/bootstrap/services/user/src/commands/user.handler.js +286 -0
  187. package/bootstrap/services/user/src/config.js +73 -0
  188. package/bootstrap/services/user/src/controllers/http.controller.js +156 -0
  189. package/bootstrap/services/user/src/controllers/message.controller.js +51 -0
  190. package/bootstrap/services/user/src/db/migrations/01-init.js +50 -0
  191. package/bootstrap/services/user/src/db/migrations/02-user-service-init.js +63 -0
  192. package/bootstrap/services/user/src/db/migrations/03-unauth-sessions.js +43 -0
  193. package/bootstrap/services/user/src/db/seeders/development/01-root-user.js +29 -0
  194. package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +27 -0
  195. package/bootstrap/services/user/src/db/seeders/production/01-root-user.js +29 -0
  196. package/bootstrap/services/user/src/policies/user.policy.js +81 -0
  197. package/bootstrap/services/user/src/schema/user.schema.js +69 -0
  198. package/bootstrap/services/user/src/services/authentication.service.js +127 -0
  199. package/bootstrap/services/user/src/services/session.service.js +58 -0
  200. package/bootstrap/services/user/src/services/user.service.js +130 -0
  201. package/bootstrap/services/user/src/tests/user.test.js +126 -0
  202. package/package.json +3 -7
  203. package/src/agent/agent.client.js +28 -0
  204. package/src/agent/commands.js +48 -0
  205. package/src/cli.js +30 -0
  206. package/src/config.js +8 -0
  207. package/src/control/commands.js +156 -0
  208. package/src/control/control.client.js +127 -0
  209. package/src/dev/commands.js +71 -0
  210. package/src/dev/dev.service.js +320 -0
  211. package/src/engine/infra.js +142 -0
  212. package/src/helpers/helpers.js +63 -0
  213. package/src/profiles/command.js +170 -0
  214. package/src/profiles/profiles.client.js +101 -0
  215. package/src/scaffolder/commands.js +123 -0
  216. package/src/scaffolder/scaffolder.handler.js +252 -0
  217. package/src/services/client.js +174 -0
  218. package/templates/service/Dockerfile.hbs +20 -0
  219. package/templates/service/app.js.hbs +38 -0
  220. package/templates/service/commands/{{serviceName}}.handler.js.hbs +97 -0
  221. package/templates/service/config.js.hbs +48 -0
  222. package/templates/service/controllers/http.controller.js.hbs +87 -0
  223. package/templates/service/controllers/message.controller.js.hbs +51 -0
  224. package/templates/service/db/migrations/01-init.js.hbs +50 -0
  225. package/templates/service/db/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +23 -0
  226. package/templates/service/package.json.hbs +18 -0
  227. package/templates/service/policies/{{serviceName}}.policy.js.hbs +49 -0
  228. package/templates/service/schema/{{serviceName}}.schema.js.hbs +14 -0
  229. package/templates/service/services/{{serviceName}}.service.js.hbs +32 -0
  230. package/dist/cli.js +0 -18
@@ -0,0 +1,96 @@
1
+ import React, { useState } from "react"
2
+ import gnarEngine from "@gnar-engine/js-client";
3
+ import { Link } from "react-router-dom";
4
+
5
+ const PasswordResetForm = () => {
6
+
7
+ const [email, setEmail] = useState("");
8
+ const [password, setPassword] = useState("");
9
+ const [confirmPassword, setConfirmPassword] = useState("");
10
+ const [message, setMessage] = useState("");
11
+ const [success, setSuccess] = useState(false);
12
+ const [loading, setLoading] = useState(false);
13
+
14
+ const handlePasswordResetSubmit = async (e) => {
15
+
16
+ e.preventDefault();
17
+ setLoading(true);
18
+
19
+ // get token from url
20
+ const path = window.location.pathname;
21
+ const token = path.split('/').pop();
22
+
23
+ if (email === "") {
24
+ setMessage("Please enter your email address");
25
+ return;
26
+ }
27
+
28
+ if (token === "") {
29
+ setMessage("Invalid password reset link");
30
+ return;
31
+ }
32
+
33
+ if (password === "") {
34
+ setMessage("Please enter a new password");
35
+ return;
36
+ }
37
+
38
+ if (confirmPassword === "" || password !== confirmPassword) {
39
+ setMessage("Passwords do not match");
40
+ return;
41
+ }
42
+
43
+ (async () => {
44
+ try {
45
+ console.log('Resetting password for email:', email, 'with token:', token);
46
+ // change password
47
+ await gnarEngine.user.changePassword({email, token , password});
48
+ await gnarEngine.user.authenticate(email, password);
49
+
50
+ setMessage('');
51
+ setSuccess(true);
52
+ } catch (error) {
53
+ if (error.response.data.message) {
54
+ setMessage("Error resetting password: " + error.response.data.message);
55
+ console.error('Error resetting password:', error);
56
+ } else if (error.response.data) {
57
+ setMessage("Error resetting password: " + error.response.data);
58
+ console.error('Error resetting password:', error);
59
+ }
60
+ } finally {
61
+ setLoading(false);
62
+ }
63
+ })();
64
+ }
65
+
66
+ return (
67
+ <div className="login">
68
+ <h2>Password reset</h2>
69
+
70
+ <form className="login-form" onSubmit={handlePasswordResetSubmit}>
71
+ <input type="email" placeholder="Email address" className="email-input-icon" value={email} onChange={(e) => { setEmail(e.target.value); setMessage(''); }}/>
72
+ <input type="password" placeholder="Enter new password" className="password-input-icon" value={password} onChange={(e) => { setPassword(e.target.value); setMessage(''); }}/>
73
+ <input type="password" placeholder="Confirm new password" className="password-input-icon" value={confirmPassword} onChange={(e) => { setConfirmPassword(e.target.value); setMessage(''); }}/>
74
+ <div class="password-requirements">
75
+ <p>To ensure the security of your account, your new password must meet the following criteria:</p>
76
+ <ul>
77
+ <li><strong>Minimum Length</strong>: Your password must be at least <strong>8 characters</strong> long.</li>
78
+ <li><strong>Uppercase Letters</strong>: Include at least <strong>one uppercase letter</strong> (A-Z).</li>
79
+ <li><strong>Lowercase Letters</strong>: Include at least <strong>one lowercase letter</strong> (a-z).</li>
80
+ <li><strong>Numbers</strong>: Include at least <strong>one number</strong> (0-9).</li>
81
+ <li><strong>Special Characters</strong>: Include at least <strong>one special character</strong> from the following: <strong>@, $, !, %, *, ?, &</strong>.</li>
82
+ </ul>
83
+ <p>Please ensure your password meets all of these requirements before submitting.</p>
84
+ </div>
85
+ <button type="submit" disabled={loading}>Update password</button>
86
+ <span id="login-message">{message}</span>
87
+
88
+ {success &&
89
+ <span id="login-message">Password reset successful. <Link to="/login">Click here to login</Link></span>
90
+ }
91
+ </form>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ export default PasswordResetForm;
@@ -0,0 +1,74 @@
1
+ import React, { useState } from "react"
2
+ import gnarEngine from "@gnar-engine/js-client";
3
+
4
+
5
+ const PasswordResetForm = () => {
6
+
7
+ const [email, setEmail] = useState("");
8
+ const [message, setMessage] = useState("");
9
+ const [success, setSuccess] = useState(false);
10
+ const [loading, setLoading] = useState(null);
11
+
12
+ const handleRequestSubmit = async (e) => {
13
+
14
+ e.preventDefault();
15
+
16
+ setLoading('loading');
17
+
18
+ if (email === "") {
19
+ setMessage("Please enter your email address")
20
+
21
+ setLoading('error');
22
+ setTimeout(() => {
23
+ setLoading(null);
24
+ }, 3000);
25
+ return;
26
+ }
27
+
28
+ (async () => {
29
+ try {
30
+ // reset password
31
+ await gnarEngine.sendPasswordReset({email});
32
+
33
+ setSuccess(true);
34
+
35
+ setLoading('success');
36
+ setTimeout(() => {
37
+ setLoading(null);
38
+ }, 3000);
39
+
40
+ } catch (error) {
41
+ if (error.response.data.message) {
42
+ setMessage("Error requesting password reset: " + error.response.data.message);
43
+ console.error('Error requesting password reset:', error);
44
+ } else if (error.response.data) {
45
+ setMessage("Error requesting password reset: " + error.response.data);
46
+ console.error('Error requesting password reset:', error);
47
+ }
48
+
49
+ setLoading('error');
50
+ setTimeout(() => {
51
+ setLoading(null);
52
+ }, 3000);
53
+ }
54
+ })();
55
+ }
56
+
57
+ return (
58
+ <div className="login">
59
+ <h2>Password reset</h2>
60
+
61
+ <form className="login-form" onSubmit={handleRequestSubmit}>
62
+ <input type="email" placeholder="Email address" className="email-input-icon" value={email} onChange={(e) => { setEmail(e.target.value); setMessage(''); }}/>
63
+ <button className={loading} type="submit">Reset password</button>
64
+ <span id="login-message">{message}</span>
65
+
66
+ {success &&
67
+ <span id="login-message">A reset password link will be emailed to this email if it exists as a user.</span>
68
+ }
69
+ </form>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ export default PasswordResetForm;
@@ -0,0 +1,102 @@
1
+ import { useState, useEffect } from 'react';
2
+ import bin from '../../assets/bin.svg';
3
+ import { currencies, productPriceTypes, productIntervals } from '../../data/data';
4
+ import CustomSelect from '../../ui/customSelect/CustomSelect';
5
+
6
+ const PriceFormRow = ({ item, onChange, remove, showLabels }) => {
7
+
8
+ const [selectedCurrency, setSelectedCurrency] = useState(null);
9
+ const [selectedInterval, setSelectedInterval] = useState(null);
10
+ const [selectedPriceType, setSelectedPriceType] = useState(null);
11
+
12
+ useEffect(() => {
13
+ if (item.currency) {
14
+ const currency = currencies.find(currency => {
15
+ return currency.id === item.currency;
16
+ });
17
+ setSelectedCurrency(currency || null);
18
+ }
19
+
20
+ if (item.type) {
21
+ const priceType = productPriceTypes.find(priceType => {
22
+ return priceType.id === item.type;
23
+ });
24
+ setSelectedPriceType(priceType || null);
25
+ }
26
+
27
+ if (item.interval) {
28
+ const interval = productIntervals.find(interval => {
29
+ return interval.id === item.interval;
30
+ }
31
+ );
32
+ setSelectedInterval(interval || null);
33
+ }
34
+
35
+ }, [item]);
36
+
37
+ return (
38
+ <div className='form-row'>
39
+ <div className="input-cont">
40
+ {showLabels && <label>Currency</label>}
41
+ <CustomSelect
42
+ name="currency"
43
+ placeholder="Select currency"
44
+ options={currencies}
45
+ labelKey="currency"
46
+ setSelectedOption={(currency) => onChange("currency", currency.id)}
47
+ selectedOption={selectedCurrency}
48
+ />
49
+ </div>
50
+
51
+ <div className="input-cont">
52
+ {showLabels && <label>Price Type</label>}
53
+ <CustomSelect
54
+ name="type"
55
+ placeholder="Select price type"
56
+ options={productPriceTypes}
57
+ labelKey="type"
58
+ setSelectedOption={(type) => onChange("type", type.id)}
59
+ selectedOption={selectedPriceType}
60
+ />
61
+ </div>
62
+
63
+ {item.type === 'recurring' && (
64
+ <div className="input-cont">
65
+ {showLabels && <label>Interval</label>}
66
+ <CustomSelect
67
+ name="interval"
68
+ placeholder="Select interval"
69
+ options={productIntervals}
70
+ labelKey="interval"
71
+ setSelectedOption={(interval) => onChange("interval", interval.id)}
72
+ selectedOption={selectedInterval}
73
+ />
74
+ </div>
75
+ )}
76
+
77
+ <div className="input-cont">
78
+ {showLabels && <label>Price</label>}
79
+ <input
80
+ type="text"
81
+ name='price'
82
+ value={item.price}
83
+ onChange={(e) => onChange("price", Number(e.target.value))}
84
+ />
85
+ </div>
86
+
87
+ <div className="input-cont">
88
+ {showLabels && <label>Sale price</label>}
89
+ <input
90
+ type="text"
91
+ name='salePrice'
92
+ value={item.salePrice}
93
+ onChange={(e) => onChange("salePrice", Number(e.target.value))}
94
+ />
95
+ </div>
96
+
97
+ <button className='bin-icon' onClick={remove}><img src={bin} alt="" /></button>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ export default PriceFormRow;
@@ -0,0 +1,24 @@
1
+ .form-row {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ margin-bottom: 10px;
5
+ gap: 20px;
6
+ align-items: center;
7
+ flex-wrap: wrap;
8
+
9
+ .input-cont {
10
+ display: flex;
11
+ flex-direction: column;
12
+
13
+ input {
14
+ width: 250px;
15
+ }
16
+ }
17
+ }
18
+
19
+ .form-group.categories {
20
+ width: 100%;
21
+ .custom-select-input {
22
+ min-width: 500px !important;
23
+ }
24
+ }
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import CustomCheckbox from '../../ui/customCheckbox/CustomCheckbox';
3
+
4
+ const RaffleEntriesList = ({setSelectedSingleItemId, setView, selectedEntryIds, setSelectedEntryIds, raffleEntries, message }) => {
5
+
6
+ const allSelected = raffleEntries.length > 0 && selectedEntryIds.size === raffleEntries.length;
7
+
8
+ const toggleEntrySelection = (entryId) => {
9
+ setSelectedEntryIds(prev => {
10
+ const newSet = new Set(prev);
11
+ if (newSet.has(entryId)) {
12
+ newSet.delete(entryId);
13
+ } else {
14
+ newSet.add(entryId);
15
+ }
16
+ return newSet;
17
+ });
18
+ };
19
+
20
+ const toggleSelectAll = () => {
21
+ if (allSelected) {
22
+ setSelectedEntryIds(new Set());
23
+ } else {
24
+ setSelectedEntryIds(new Set(raffleEntries.map(entry => entry.id)));
25
+ }
26
+ };
27
+
28
+ const columns = [
29
+ { columnLabel: 'Name', dataKey: 'user_name' },
30
+ { columnLabel: 'Entry ID', dataKey: 'entry_id' },
31
+ { columnLabel: 'Email', dataKey: 'user_email' },
32
+ { columnLabel: 'Order ID', dataKey: 'order_id' },
33
+ { columnLabel: 'Subscription ID', dataKey: 'subscription_id' },
34
+ { columnLabel: 'Multiplier', dataKey: 'multiplier' }
35
+ ];
36
+
37
+ return (
38
+ <div className="">
39
+ <div className='pagination-labels-cont'>
40
+ <div className='pagination-count'>
41
+ Showing {raffleEntries.length} of {raffleEntries.length} raffle entr{raffleEntries.length !== 1 ? 'ies' : 'y'}
42
+ </div>
43
+ <div className="pagination-count">
44
+ {selectedEntryIds.size} of {raffleEntries.length} selected
45
+ </div>
46
+ </div>
47
+
48
+ <div className='crud-list'>
49
+ <table className='custom-table'>
50
+ <thead>
51
+ <tr>
52
+ <th className="checkbox">
53
+ <CustomCheckbox
54
+ name="selectAll"
55
+ checked={allSelected}
56
+ setChecked={toggleSelectAll}
57
+ />
58
+ </th>
59
+ {columns.map(column => (
60
+ <th key={column.dataKey}>{column.columnLabel}</th>
61
+ ))}
62
+ </tr>
63
+ </thead>
64
+
65
+ <tbody>
66
+ {message ? (
67
+ <tr>
68
+ <td colSpan={columns.length + 1}>{message}</td>
69
+ </tr>
70
+ ) : (
71
+ raffleEntries.length > 0 ? (
72
+ raffleEntries.map(entry => (
73
+ <tr key={entry.id}>
74
+ <td>
75
+ <CustomCheckbox
76
+ name={`checkbox-${entry.id}`}
77
+ checked={selectedEntryIds.has(entry.id)}
78
+ setChecked={() => toggleEntrySelection(entry.id)}
79
+ />
80
+ </td>
81
+ {columns.map(column => (
82
+ <td key={column.dataKey}>{entry[column.dataKey]}</td>
83
+ ))}
84
+ </tr>
85
+ ))
86
+ ) : (
87
+ <tr>
88
+ <td colSpan={columns.length + 1}>{message}</td>
89
+ </tr>
90
+ )
91
+ )}
92
+ </tbody>
93
+ </table>
94
+ </div>
95
+ </div>
96
+ );
97
+ };
98
+
99
+ export default RaffleEntriesList;
@@ -0,0 +1,46 @@
1
+ import bin from '../../assets/bin.svg';
2
+ import { useState } from 'react';
3
+ import { currencies, taxClasses } from '../../data/data';
4
+ import CustomSelect from '../../ui/customSelect/CustomSelect';
5
+
6
+ const RaffleProductFormRow = ({ item, onChange, remove }) => {
7
+ return (
8
+ <div className='form-row'>
9
+ <div>
10
+ <label>Product SKU</label>
11
+ <input
12
+ type="text"
13
+ name='productSku'
14
+ value={item.productSku}
15
+ onChange={(e) => onChange("productSku", e.target.value)}
16
+ />
17
+ </div>
18
+
19
+ <div>
20
+ <label>Type (one-time / recurring)</label>
21
+ <input
22
+ type="text"
23
+ name='type'
24
+ value={item.type}
25
+ onChange={(e) => onChange("type", e.target.value)}
26
+ />
27
+ </div>
28
+
29
+ <div>
30
+ <label>Number of Entries</label>
31
+ <input
32
+ type="number"
33
+ name='numEntries'
34
+ min={1}
35
+ value={item.numEntries}
36
+ onChange={(e) => onChange("numEntries", Number(e.target.value))}
37
+ />
38
+ </div>
39
+
40
+ <button type="button" className='bin-icon' onClick={remove}><img src={bin} alt="" /></button>
41
+ </div>
42
+ )
43
+ };
44
+
45
+
46
+ export default RaffleProductFormRow;
@@ -0,0 +1,64 @@
1
+ import React from "react";
2
+ import { useLocation, Link } from "react-router-dom";
3
+ import { useSelector } from "react-redux";
4
+ import home from '../../assets/home.svg';
5
+ import product from '../../assets/package.svg';
6
+ import activity from '../../assets/activity.svg';
7
+ import users from '../../assets/users.svg';
8
+ import settings from '../../assets/settings.svg';
9
+ import link from '../../assets/link.svg';
10
+ import wallet from '../../assets/wallet.svg';
11
+ import shoppingBag from '../../assets/shopping-bag.svg';
12
+ import contacts from '../../assets/contact.svg';
13
+ import raffle from '../../assets/raffle.svg';
14
+
15
+ const Sidebar = () => {
16
+ const location = useLocation();
17
+ const user = useSelector(state => state.auth.authUser);
18
+
19
+ // Helper function to check if the link is active
20
+ const isActive = (path) => location.pathname === path;
21
+
22
+ return (
23
+ <div className="sidebar">
24
+ <Link to="/portal/" className={`menu-item ${isActive("/portal/") ? "active" : ""}`}>
25
+ <img src={home} alt="Dashboard"/>Dashboard
26
+ </Link>
27
+ <Link to="/portal/products" className={`menu-item ${isActive("/portal/products") ? "active" : ""}`}>
28
+ <img src={product} alt="Products"/>Products
29
+ </Link>
30
+ <Link to="/portal/orders" className={`menu-item ${isActive("/portal/orders") ? "active" : ""}`}>
31
+ <img src={shoppingBag} alt="Order"/>Orders
32
+ </Link>
33
+ <Link to="/portal/subscriptions" className={`menu-item ${isActive("/portal/subscriptions") ? "active" : ""}`}>
34
+ <img src={shoppingBag} alt="Subscriptions"/>Subscriptions
35
+ </Link>
36
+ <Link to="/portal/integrations" className={`menu-item ${isActive("/portal/integrations") ? "active" : ""}`}>
37
+ <img src={link} alt="Integrations"/>Integrations
38
+ </Link>
39
+ <Link to="/portal/raffles" className={`menu-item ${isActive("/portal/raffles") ? "active" : ""}`}>
40
+ <img src={raffle} alt="Raffles"/>Raffles
41
+ </Link>
42
+ <Link to="/portal/raffle-entries" className={`menu-item ${isActive("/portal/raffle-entries") ? "active" : ""}`}>
43
+ <img src={raffle} alt="Raffle Entries"/>Raffle Entries
44
+ </Link>
45
+ <Link to="/portal/contacts" className={`menu-item ${isActive("/portal/contacts") ? "active" : ""}`}>
46
+ <img src={contacts} alt="Contacts"/>Contacts
47
+ </Link>
48
+ <Link to="/portal/payments" className={`menu-item ${isActive("/portal/payments") ? "active" : ""}`}>
49
+ <img src={wallet} alt="Payments"/>Payments
50
+ </Link>
51
+ <Link to="/portal/users" className={`menu-item ${isActive("/portal/users") ? "active" : ""}`}>
52
+ <img src={users} alt="Users"/>Users
53
+ </Link>
54
+ <Link to="/portal/reports" className={`menu-item ${isActive("/portal/reports") ? "active" : ""}`}>
55
+ <img src={activity} alt="Reports"/>Reports
56
+ </Link>
57
+ <Link to="/portal/settings" className={`menu-item ${isActive("/portal/settings") ? "active" : ""}`}>
58
+ <img src={settings} alt="Settings"/>Settings
59
+ </Link>
60
+ </div>
61
+ );
62
+ };
63
+
64
+ export default Sidebar;
@@ -0,0 +1,49 @@
1
+ .sidebar {
2
+ min-width: 240px;
3
+ max-width: 240px;
4
+ padding-top: 30px;
5
+ box-sizing: border-box;
6
+
7
+ .collapsible-cont {
8
+ margin-bottom: 30px;
9
+ }
10
+
11
+ .collapsible-content-inner {
12
+ padding-left: 32px;
13
+ }
14
+
15
+ a {
16
+ text-decoration: none;
17
+ color: @dark-1;
18
+ font-size: 18px;
19
+ font-weight: 400;
20
+ padding-top: 12px;
21
+ padding-bottom: 12px;
22
+ display: inline-block;
23
+ min-width: 100%;
24
+ }
25
+
26
+ .menu-item {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 10px;
30
+ padding-left: 16px;
31
+ padding-right: 16px;
32
+ transition: all 0.3s;
33
+
34
+ &.active {
35
+ color: @green-1;
36
+ padding-left: 30px;
37
+
38
+ img {
39
+ filter: none;
40
+ }
41
+ }
42
+
43
+ img {
44
+ filter: grayscale(60%) brightness(0.5);
45
+ width: 35px;
46
+ height: 35px;
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,109 @@
1
+ import React, { useEffect } from 'react';
2
+ import CustomSelect from '../../ui/customSelect/CustomSelect';
3
+ import Repeater from '../../ui/repeater/Repeater';
4
+ import PriceFormRow from '../priceFormRow/PriceFormRow';
5
+ import InventoryFormRow from '../inventoryFormRow/InventoryFormRow';
6
+ import AttributeFormRow from '../attributeFormRow/AttributeFormRow';
7
+ import { taxClasses } from '../../data/data';
8
+ import binWhite from '../../assets/bin-white.svg';
9
+
10
+
11
+ const Skus = ({ item, onChange, remove, showLabels }) => {
12
+
13
+ // useEffect(() => {
14
+ // console.log(item);
15
+ // }, [item]);
16
+
17
+
18
+ return (
19
+ <>
20
+ <div className='card'>
21
+ <div className='card-header'>
22
+ <h2>SKU: {item.sku || ''}</h2>
23
+ <button className="bin-icon white" onClick={remove}><img src={binWhite} alt="Remove" /></button>
24
+ </div>
25
+ <div className='card-content'>
26
+ <div>
27
+ <div className='form-cont'>
28
+ <h2>SKU</h2>
29
+ <div className='input-cont'>
30
+ <input
31
+ type="text"
32
+ name="skus"
33
+ value={item.sku || ''}
34
+ onChange={(e) => onChange('sku', e.target.value)}
35
+ required
36
+ />
37
+ </div>
38
+ </div>
39
+ <div className='form-cont'>
40
+ <h2>Tax Class</h2>
41
+ <div className="tax-cont">
42
+ <CustomSelect
43
+ name="taxClass"
44
+ placeholder="Tax Class"
45
+ options={taxClasses}
46
+ labelKey="taxClass"
47
+ setSelectedOption={(selected) => onChange("taxClass", selected.id)}
48
+ selectedOption={taxClasses.find(tc => tc.id === item.taxClass) || null}
49
+ />
50
+ </div>
51
+ </div>
52
+ </div>
53
+ {/* <h2>Attributes</h2>
54
+ <Repeater
55
+ items={attributes}
56
+ setItems={setAttributes}
57
+ defaultItem={{ name: '', value: '' }}
58
+ buttonText="Add new attribute"
59
+ renderRow={(item, index, updateItem, remove) => (
60
+ <AttributeFormRow
61
+ key={index}
62
+ item={item}
63
+ onChange={(field, value) => updateItem({ ...item, [field]: value })}
64
+ remove={remove}
65
+ product={product}
66
+ />
67
+ )}
68
+ /> */}
69
+
70
+ <h2>Prices</h2>
71
+ <Repeater
72
+ items={item?.prices ?? [{}]}
73
+ setItems={(prices) => onChange('prices', prices)}
74
+ defaultItem={{ price: 0, currency: "GBP", type: "regular" }}
75
+ buttonText="Add new price list"
76
+ renderRow={(item, index, updateItem, remove) => (
77
+ <PriceFormRow
78
+ key={index}
79
+ item={item}
80
+ onChange={(field, value) => updateItem({ ...item, [field]: value })}
81
+ remove={remove}
82
+ showLabels={index === 0}
83
+ />
84
+ )}
85
+ />
86
+
87
+ {/* <h2>Inventory</h2>
88
+ <Repeater
89
+ items={inventories}
90
+ setItems={setInventories}
91
+ defaultItem={{ stock: 0, warehouse: "Main" }}
92
+ buttonText="Add new allocation"
93
+ renderRow={(item, index, updateItem, remove) => (
94
+ <InventoryFormRow
95
+ key={index}
96
+ item={item}
97
+ onChange={(field, value) => updateItem({ ...item, [field]: value })}
98
+ remove={remove}
99
+ product={product}
100
+ />
101
+ )}
102
+ /> */}
103
+ </div>
104
+ </div>
105
+ </>
106
+ );
107
+ };
108
+
109
+ export default Skus;