mango-cms 0.0.13

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 (59) hide show
  1. package/README.md +17 -0
  2. package/bin/mango +4 -0
  3. package/frontend-starter/README.md +8 -0
  4. package/frontend-starter/dist/_redirects +1 -0
  5. package/frontend-starter/dist/assets/index.00922bd5.js +99 -0
  6. package/frontend-starter/dist/assets/index.1781f175.css +1 -0
  7. package/frontend-starter/dist/favicon.png +0 -0
  8. package/frontend-starter/dist/index.html +53 -0
  9. package/frontend-starter/dist/index.js +66 -0
  10. package/frontend-starter/index.html +25 -0
  11. package/frontend-starter/index.js +197 -0
  12. package/frontend-starter/package-lock.json +5454 -0
  13. package/frontend-starter/package.json +40 -0
  14. package/frontend-starter/postcss.config.js +6 -0
  15. package/frontend-starter/public/_redirects +1 -0
  16. package/frontend-starter/public/favicon.png +0 -0
  17. package/frontend-starter/public/index.js +66 -0
  18. package/frontend-starter/src/App.vue +27 -0
  19. package/frontend-starter/src/components/layout/login.vue +212 -0
  20. package/frontend-starter/src/components/layout/modal.vue +113 -0
  21. package/frontend-starter/src/components/layout/spinner.vue +17 -0
  22. package/frontend-starter/src/components/pages/404.vue +28 -0
  23. package/frontend-starter/src/components/pages/home.vue +74 -0
  24. package/frontend-starter/src/components/partials/button.vue +31 -0
  25. package/frontend-starter/src/helpers/Mango.vue +455 -0
  26. package/frontend-starter/src/helpers/breakpoints.js +34 -0
  27. package/frontend-starter/src/helpers/darkMode.js +38 -0
  28. package/frontend-starter/src/helpers/email.js +32 -0
  29. package/frontend-starter/src/helpers/formatPhone.js +18 -0
  30. package/frontend-starter/src/helpers/localDB.js +315 -0
  31. package/frontend-starter/src/helpers/mango.js +338 -0
  32. package/frontend-starter/src/helpers/model.js +9 -0
  33. package/frontend-starter/src/helpers/multiSelect.vue +252 -0
  34. package/frontend-starter/src/helpers/pills.vue +75 -0
  35. package/frontend-starter/src/helpers/reconnecting-websocket.js +357 -0
  36. package/frontend-starter/src/helpers/uploadFile.vue +157 -0
  37. package/frontend-starter/src/helpers/uploadFiles.vue +100 -0
  38. package/frontend-starter/src/helpers/uploadImages.vue +89 -0
  39. package/frontend-starter/src/helpers/user.js +40 -0
  40. package/frontend-starter/src/index.css +281 -0
  41. package/frontend-starter/src/main.js +145 -0
  42. package/frontend-starter/tailwind.config.js +46 -0
  43. package/frontend-starter/vite.config.js +10 -0
  44. package/frontend-starter/yarn.lock +3380 -0
  45. package/mango-cms-0.0.13.tgz +0 -0
  46. package/package.json +24 -0
  47. package/src/cli.js +93 -0
  48. package/src/default-config/automation/index.js +37 -0
  49. package/src/default-config/collections/examples.js +60 -0
  50. package/src/default-config/config/.collections.json +1 -0
  51. package/src/default-config/config/globalFields.js +15 -0
  52. package/src/default-config/config/settings.json +23 -0
  53. package/src/default-config/config/statuses.js +0 -0
  54. package/src/default-config/config/users.js +35 -0
  55. package/src/default-config/endpoints/index.js +19 -0
  56. package/src/default-config/fields/vimeo.js +36 -0
  57. package/src/default-config/hooks/test.js +5 -0
  58. package/src/default-config/plugins/mango-stand/index.js +206 -0
  59. package/src/main.js +278 -0
@@ -0,0 +1,206 @@
1
+ // import CMS from '../../../mango/src/cms' (for use like {address: CMS.fields.Address()})
2
+ import fields from '@cms/1. build/fields'
3
+ import { readEntries, readEntry } from '../../../mango/src/cms/1. build/libraries/mongo'
4
+
5
+ const billing = {
6
+ fields: {
7
+ paymentId: String,
8
+ firstName: { type: String, search: { enabled: true, weight: 5 } },
9
+ lastName: { type: String, search: { enabled: true, weight: 5 } },
10
+ phoneNumber: String,
11
+ address: fields.Address(),
12
+ payment: {
13
+ fields: {
14
+ creditCard: {
15
+ fields: {
16
+ cardNumber: String,
17
+ cardType: String,
18
+ expirationDate: String,
19
+ }
20
+ },
21
+ bankAccount: {
22
+ fields: {
23
+ accountNumber: String,
24
+ accountType: String,
25
+ echeckType: String,
26
+ nameOnAccount: String,
27
+ routingNumber: String
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ const Items = {
36
+ name: 'items',
37
+ singular: 'item',
38
+ fields: {
39
+ price: { type: 'Int' },
40
+ stock: { type: 'Int' },
41
+ description: fields.PlainText(),
42
+ },
43
+ hooks: {
44
+ sold({ item }) {
45
+ console.log('sold', item.title)
46
+ },
47
+ addedToCart({ item }) {
48
+ console.log('addedToCart', item.title)
49
+ },
50
+ removedFromCart({ item }) {
51
+ console.log('removedFromCart', item.title)
52
+ }
53
+ }
54
+ }
55
+
56
+ const Discounts = {
57
+ name: 'discounts',
58
+ singular: 'discount',
59
+ permissions: {
60
+ public: ['create', 'read']
61
+ },
62
+ fields: {
63
+ type: fields.Select({ options: ['coupon', 'sale'], required: true }),
64
+ code: { required: true, translateInput: i => i.toLowerCase() },
65
+ limitations: {
66
+ fields: {
67
+ items: fields.Relationship({ collection: 'item' }),
68
+ perCustomer: 'Int',
69
+ totalUses: 'Int',
70
+ perOrder: 'Int',
71
+ quantity: 'Int',
72
+ // remainingUses: {
73
+ // computed: doc => doc.limitations.totalUses ? /* Get times used and subtract from total uses */ : null
74
+ // }
75
+ }
76
+ },
77
+ freeShipping: Boolean,
78
+ discount: {
79
+ inputExample: '$12.50 or 50% etc.',
80
+ inputType: String,
81
+ validate: input => ({ valid: (!!input.match(/\$[0-9]+/) || input.match(/[0-9]\%+/)) ? true : false, response: 'Should be formatted like `100%` or `$12.50`' }),
82
+ translateInput: input => ({
83
+ type: !!input.match(/\$[0-9]+/) ? 'fixed' : 'percentage',
84
+ value: !!input.match(/\$[0-9]+/) ? input.match(/\$\d+(?:\.\d+)?/)[0].replace('$', '') : input.match(/(\d+|\d+[.,]\d{1,2})(?=\s*%)/)[0].replace('%', '')
85
+ }),
86
+ fields: {
87
+ type: String,
88
+ value: 'Float'
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+
95
+ const Carts = {
96
+ name: 'carts',
97
+ singular: 'cart',
98
+ permissions: {
99
+ public: ['create', 'read', 'update']
100
+ },
101
+ fields: {
102
+
103
+ title: { required: false },
104
+
105
+ transactionId: String,
106
+ finalized: Boolean,
107
+ completed: Boolean,
108
+ completedDate: fields.Timestamp(),
109
+ cartItems: [{
110
+ fields: {
111
+ quantity: 'Int',
112
+ discount: 'Float',
113
+ item: fields.Relationship({ collection: 'item', single: true }),
114
+ }
115
+ }],
116
+ coupons: fields.Relationship({ collection: 'discount' }),
117
+
118
+ customerId: String,
119
+ billing,
120
+ shipping: {
121
+ fields: {
122
+ method: String,
123
+ price: String,
124
+ addressId: String,
125
+ firstName: String,
126
+ lastName: String,
127
+ phoneNumber: String,
128
+ address: fields.Address(),
129
+ free: { type: Boolean, computed: doc => doc?.coupons?.find(c => c.freeShipping) }
130
+ }
131
+ },
132
+
133
+ price: {
134
+ fields: {
135
+ subtotal: 'Float',
136
+ discount: 'Float',
137
+ total: 'Float',
138
+ },
139
+ computed: async doc => {
140
+
141
+ let subtotal = Number(doc.cartItems?.reduce((a, i) => a + ((i.item?.price || 0) * i.quantity), 0)) || 0
142
+ let shipping = doc?.shipping?.price || 0
143
+
144
+ let discount = 0
145
+
146
+ if (doc?.coupons?.length) {
147
+ let coupon = doc?.coupons?.[0]
148
+ if (coupon?.limitations?.items) {
149
+ for (let item of doc.cartItems) {
150
+ if (coupon?.limitations?.items?.find(i => i.id == item.item?.id)) {
151
+ if (coupon.discount.type == 'fixed') {
152
+ discount += (item.quantity * coupon.discount.value)
153
+ } else {
154
+ discount += ((item.quantity * item.item.price) * (Number(coupon.discount.value) / 100))
155
+ console.log('discount', discount)
156
+ }
157
+ }
158
+ }
159
+ } else {
160
+ if (coupon.discount.type == 'fixed') {
161
+ discount = Number(coupon.discount.value > subtotal ? subtotal : coupon.discount.value)
162
+ } else {
163
+ discount = Number(subtotal * (Number(coupon.discount.value) / 100))
164
+ }
165
+ }
166
+ }
167
+
168
+ let activeSales = await readEntries({ collection: 'discounts', search: { type: 'sale', startDate: { $lt: new Date() }, endDate: { $gt: new Date() } } })
169
+
170
+ if (activeSales?.length) {
171
+ for (let item of doc.cartItems) {
172
+ for (let sale of activeSales.sort((a, b) => b.discount.value < a.discount.value ? 1 : -1)) {
173
+ let saleApplies = sale?.limitations?.items?.some(i => i == item.item?.id) || !sale?.limitations?.items?.length
174
+ if (saleApplies) {
175
+ if (sale.discount.type == 'fixed') {
176
+ item.discount = (item.quantity * sale.discount.value)
177
+ } else {
178
+ item.discount = Number((item.quantity * item.item.price) * (Number(sale.discount.value) / 100))
179
+ }
180
+ }
181
+ }
182
+ }
183
+ discount = Number(doc.cartItems?.reduce((a, i) => a + i.discount, 0))
184
+ }
185
+
186
+ let total = (subtotal + shipping - discount) || 0
187
+
188
+ return {
189
+ subtotal,
190
+ discount,
191
+ total,
192
+ }
193
+ }
194
+ }
195
+
196
+ },
197
+ }
198
+
199
+ export default {
200
+ name: 'mangoStand',
201
+ collections: [
202
+ Items,
203
+ Carts,
204
+ Discounts,
205
+ ],
206
+ }
package/src/main.js ADDED
@@ -0,0 +1,278 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import ncp from 'ncp';
4
+ import spawn from 'await-spawn'
5
+ import { promisify } from 'util';
6
+ import path from 'path';
7
+
8
+
9
+ const child_process = require('child_process')
10
+
11
+ const copy = promisify(ncp);
12
+
13
+ async function copyConfigFiles(options) {
14
+ const excludeDir = 'dist';
15
+
16
+ // Filter function to exclude the dist directory
17
+ const filterFunc = (src, dest) => {
18
+ // Use relative path for accurate exclusion when running in nested directories
19
+ const relativePath = src.substring(options.projectConfigDirectory.length);
20
+
21
+ // Exclude the 'dist' directory and its contents
22
+ if (relativePath.startsWith(`/${excludeDir}`) || relativePath === excludeDir) {
23
+ return false;
24
+ }
25
+
26
+ return true;
27
+ };
28
+
29
+ // Copy files
30
+ await fs.copy(options.projectConfigDirectory, options.mangoConfigDirectory, {
31
+ clobber: true,
32
+ filter: filterFunc
33
+ });
34
+
35
+ // Path to settings.json
36
+ const settingsFilePath = path.join(options.mangoConfigDirectory, 'config', 'settings.json');
37
+
38
+ // Read, modify, and write settings.json
39
+ try {
40
+ // Read settings.json
41
+ const settings = await fs.readJson(settingsFilePath);
42
+
43
+ // Modify settings.json
44
+ settings.projectConfigDirectory = options.projectConfigDirectory;
45
+
46
+ // Write updated settings.json
47
+ await fs.writeJson(settingsFilePath, settings, { spaces: 4 });
48
+ } catch (error) {
49
+ console.error('Error updating settings.json:', error);
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ async function installMango() {
55
+
56
+ const currentFileUrl = import.meta.url;
57
+ const currentFilePath = currentFileUrl.replace('file://', '').replace('/main.js', '')
58
+ const mangoDirectory = `${currentFilePath}/../mango`
59
+
60
+ // Make sure mango exists
61
+ if (!fs.existsSync(mangoDirectory)) {
62
+ console.log('installing mango')
63
+ await spawn('git', ['clone', 'git@github.com:colton-neifert/mango-cms.git'], { cwd: `${currentFilePath}/../` })
64
+ const sourceFolderPath = path.join(`${currentFilePath}/../`, 'mango-cms');
65
+ const targetFolderPath = path.join(`${currentFilePath}/../`, 'mango');
66
+ fs.renameSync(sourceFolderPath, targetFolderPath);
67
+ await spawn('yarn', { cwd: `${currentFilePath}/../mango` })
68
+ } else {
69
+ // Get latest mango
70
+ console.log('checking version...')
71
+ let pullResponse = await spawn('git', ['pull'], { cwd: `${currentFilePath}/../mango` })
72
+ let upToDate = pullResponse.toString().includes('Already')
73
+
74
+ if (!upToDate) {
75
+ console.log('updating dependencies')
76
+ await spawn('yarn', { cwd: `${currentFilePath}/../mango` })
77
+ }
78
+ }
79
+ }
80
+
81
+ export async function serveMango(options) {
82
+
83
+ options = {
84
+ ...options,
85
+ targetDirectory: options.targetDirectory || process.cwd(),
86
+ };
87
+
88
+ const currentFileUrl = import.meta.url;
89
+ const currentFilePath = currentFileUrl.replace('file://', '').replace('/main.js', '')
90
+ const mangoDirectory = `${currentFilePath}/../mango`
91
+ const mangoConfigDirectory = `${currentFilePath}/../config`
92
+
93
+ let projectConfigDirectory = `${options.targetDirectory}/mango`
94
+
95
+ // Check if mango config exists
96
+ if (!fs.existsSync(projectConfigDirectory)) {
97
+ const collectionsDirectory = `${options.targetDirectory}/collections`;
98
+
99
+ // Check if collections directory exists inside targetDirectory
100
+ if (fs.existsSync(collectionsDirectory)) {
101
+ // Set projectConfigDirectory to targetDirectory
102
+ projectConfigDirectory = options.targetDirectory;
103
+ } else {
104
+ // If neither mango config nor collections directory exists
105
+ console.error('%s Mango config not found. Please add mango config to ./mango', chalk.red.bold('ERROR'));
106
+ process.exit(1);
107
+ }
108
+ }
109
+
110
+ options.projectConfigDirectory = projectConfigDirectory
111
+ options.mangoConfigDirectory = mangoConfigDirectory
112
+
113
+ await installMango()
114
+
115
+ let settings = fs.readFileSync(`${projectConfigDirectory}/config/settings.json`)
116
+ settings = JSON.parse(settings)
117
+
118
+ console.log('copying config files');
119
+ await copyConfigFiles(options);
120
+
121
+ console.log('spawning mango');
122
+ child_process.spawn('yarn', ['watch'], { cwd: `${mangoDirectory}` })
123
+ setTimeout(() => {
124
+ console.log(`%s Mango Running @ http://localhost:${settings.port}/graphql`, chalk.green.bold('DONE'));
125
+ }, 5000);
126
+ return true;
127
+ }
128
+
129
+ export async function buildMango(options) {
130
+
131
+ options = {
132
+ ...options,
133
+ targetDirectory: options.targetDirectory || process.cwd(),
134
+ };
135
+
136
+ const currentFileUrl = import.meta.url;
137
+ const currentFilePath = currentFileUrl.replace('file://', '').replace('/main.js', '')
138
+ const mangoDirectory = `${currentFilePath}/../mango`
139
+ const mangoConfigDirectory = `${currentFilePath}/../config`
140
+
141
+ let projectConfigDirectory = `${options.targetDirectory}/mango`
142
+
143
+ // Check if mango config exists
144
+ if (!fs.existsSync(projectConfigDirectory)) {
145
+ const collectionsDirectory = `${options.targetDirectory}/collections`;
146
+
147
+ // Check if collections directory exists inside targetDirectory
148
+ if (fs.existsSync(collectionsDirectory)) {
149
+ // Set projectConfigDirectory to targetDirectory
150
+ projectConfigDirectory = options.targetDirectory;
151
+ } else {
152
+ // If neither mango config nor collections directory exists
153
+ console.error('%s Mango config not found. Please add mango config to ./mango', chalk.red.bold('ERROR'));
154
+ process.exit(1);
155
+ }
156
+ }
157
+
158
+ options.projectConfigDirectory = projectConfigDirectory
159
+ options.mangoConfigDirectory = mangoConfigDirectory
160
+
161
+ await installMango()
162
+
163
+ let settings = fs.readFileSync(`${projectConfigDirectory}/config/settings.json`)
164
+ settings = JSON.parse(settings)
165
+
166
+ console.log('copying config files');
167
+ await copyConfigFiles(options);
168
+
169
+ console.log('building mango');
170
+ await spawn('yarn', ['build'], { cwd: `${mangoDirectory}` })
171
+ // console.log('copying mango build', `${projectConfigDirectory}/dist/mango.js`);
172
+ // child_process.spawn('yarn', ['build'], { cwd: `${mangoDirectory}` })
173
+ if (!fs.existsSync(`${projectConfigDirectory}/dist`)) fs.mkdirSync(`${projectConfigDirectory}/dist`)
174
+ await copy(`${mangoDirectory}/node_modules`, `${projectConfigDirectory}/dist/node_modules`)
175
+ await copy(`${mangoDirectory}/build/index.js`, `${projectConfigDirectory}/dist/mango.js`)
176
+
177
+ console.log(`%s Mango build completed successfully.`, chalk.green.bold('DONE'));
178
+
179
+ return true;
180
+ }
181
+
182
+ export async function initMango(options) {
183
+
184
+ options = {
185
+ ...options,
186
+ targetDirectory: options.targetDirectory || process.cwd(),
187
+ };
188
+
189
+ const currentFileUrl = import.meta.url;
190
+ const currentFilePath = currentFileUrl.replace('file://', '').replace('/main.js', '')
191
+ const defaultConfig = `${currentFilePath}/default-config`
192
+
193
+ const projectConfigDirectory = `${options.targetDirectory}/mango`
194
+
195
+ // Make sure mango config exists
196
+ if (fs.existsSync(projectConfigDirectory)) {
197
+ console.error('%s Mango already exists at ./mango', chalk.red.bold('ERROR'));
198
+ process.exit(1);
199
+ } else {
200
+ fs.mkdirSync(projectConfigDirectory);
201
+ }
202
+
203
+ // const mangoUiDirectory = `${currentFilePath}/../mango-ui`
204
+
205
+ await copy(defaultConfig, projectConfigDirectory)
206
+
207
+ /*
208
+ We're not actually moving mango to the project directory anymore
209
+ */
210
+ await installMango()
211
+ const mangoDirectory = `${currentFilePath}/../mango`
212
+ await copy(mangoDirectory, projectConfigDirectory + '/.mango')
213
+
214
+ // await copy(mangoUiDirectory, projectConfigDirectory)
215
+
216
+ console.log(`%s Mango Config Created @ ./mango`, chalk.green.bold('SUCCESS'));
217
+
218
+ }
219
+
220
+
221
+ export async function createMango(options) {
222
+
223
+ options = {
224
+ ...options,
225
+ targetDirectory: options.targetDirectory || process.cwd(),
226
+ };
227
+
228
+ // Env
229
+ const currentFileUrl = import.meta.url;
230
+ const currentFilePath = currentFileUrl.replace('file://', '').replace('/main.js', '')
231
+
232
+ // The copy of mango and config we'll transfer
233
+ const defaultConfig = `${currentFilePath}/default-config`
234
+ const mango = `${currentFilePath}/../mango`
235
+ const frontendStarter = `${currentFilePath}/../frontend-starter`
236
+
237
+ // This is the new project and its mango and config folders
238
+ const projectDirectory = `${options.targetDirectory}/${options.projectName}`
239
+ const projectConfigDirectory = `${projectDirectory}/config`
240
+ const projectMangoDirectory = `${projectDirectory}/mango`
241
+ const projectFrontDirectory = `${projectDirectory}/front`
242
+
243
+ // Make sure mango exists
244
+ await installMango()
245
+
246
+ // Make sure the new project folders exist
247
+ if (!fs.existsSync(projectDirectory)) fs.mkdirSync(projectDirectory)
248
+ if (!fs.existsSync(projectMangoDirectory)) fs.mkdirSync(projectMangoDirectory)
249
+ if (!fs.existsSync(projectConfigDirectory)) fs.mkdirSync(projectConfigDirectory)
250
+ if (!fs.existsSync(projectFrontDirectory)) fs.mkdirSync(projectFrontDirectory)
251
+
252
+ console.log(`installing mango in ${options.projectName}...`)
253
+
254
+ // Copy mango and the default config
255
+ await copy(mango, projectMangoDirectory)
256
+ await copy(defaultConfig, projectConfigDirectory)
257
+ await copy(frontendStarter, projectFrontDirectory)
258
+
259
+ console.log('')
260
+ console.log(`%s ${options.projectName} Created`, chalk.green.bold('SUCCESS'));
261
+
262
+ console.log('')
263
+ console.log('# Start new mango backend')
264
+ console.log(`cd ${options.projectName}`)
265
+ console.log('mango-cms dev')
266
+
267
+ console.log('')
268
+ console.log(`# You might want to change the db name in ${options.projectName}/config/config/settings.json`)
269
+
270
+ console.log('')
271
+ console.log('# Serve frontend')
272
+ console.log(`cd ${options.projectName}/front`)
273
+ console.log('yarn')
274
+ console.log('yarn dev')
275
+
276
+ console.log('')
277
+
278
+ }