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,455 @@
1
+ <template>
2
+ <div v-infinite-scroll="loadMore">
3
+ <slot :data="data" :loading="loading" :loadingPage="loadingPage" :totalResults="totalResults" :save="save" :saving="saving" />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import Mango from './mango'
9
+ import Swal from 'sweetalert2'
10
+ import 'sweetalert2/dist/sweetalert2.min.css';
11
+ // import { siteName } from '../../../mango/config/settings.json'
12
+ import { siteName } from '../../../config/config/settings.json'
13
+ import rws from './reconnecting-websocket'
14
+
15
+ import { ref, watch, computed, nextTick, onUnmounted } from 'vue'
16
+ // import { useRouter, useRoute, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
17
+ import { useRoute, onBeforeRouteLeave } from 'vue-router'
18
+
19
+ const props = defineProps({
20
+ collection: {type: String},
21
+ algoliaSearch: {type: String},
22
+ algoliaFilters: {type: String},
23
+ id: {type: String},
24
+ query: {type: Object},
25
+ graphql: {type: String},
26
+ debounce: {type: Number, default: 500},
27
+ infinite: {type: Boolean, default: false},
28
+ suspend: {type: Boolean, default: false},
29
+ disabled: {type: Boolean, default: false},
30
+ main: {type: Boolean, default: false},
31
+ globalSearch: {type: Boolean, default: false},
32
+ subscribe: {type: Boolean, default: false},
33
+ })
34
+
35
+ /*
36
+ If I comment this out, some things are fixed and others are broken...
37
+ */
38
+ onBeforeRouteLeave((to, from) => {
39
+ // console.log('onBeforeRouteLeave')
40
+ active.value = false
41
+ })
42
+
43
+ const emit = defineEmits(['update:data'])
44
+
45
+ let webSocket
46
+
47
+ // const router = useRouter()
48
+ const route = useRoute()
49
+
50
+ let active = ref(true)
51
+ let autoPage = ref(null)
52
+ let loadingPage = ref(false)
53
+ let noneRemain = ref(false)
54
+ let saving = ref(false)
55
+ let inferedCollection = ref(props.collection || props.query?.collection || route.path.split('/')?.[1] || null)
56
+
57
+ let loading = ref(false)
58
+ let data = ref(null)
59
+ let error = ref(null)
60
+ let totalResults = ref(null)
61
+ let oldQuery = JSON.stringify(props.query || {})
62
+
63
+ let inferedId = computed(() => props.id !== undefined ? props.id : props.query?.id || route.params?.id || null )
64
+ let searchingAlgolia = computed(() => !!props.algoliaSearch || !!props.algoliaFilters )
65
+
66
+ let debounceInit = computed(() => {
67
+ let timer;
68
+ return (...args) => {
69
+ if (active.value === false || props.disabled) return
70
+ loading.value = true
71
+ clearTimeout(timer);
72
+ timer = setTimeout(() => { init.apply(this, args); }, props.debounce);
73
+ };
74
+ })
75
+
76
+ watch(() => props.query, (n, o) => {
77
+ let newQuery = JSON.stringify(n)
78
+ if (oldQuery != newQuery) defer(newQuery, oldQuery, 'query')
79
+ }, { deep: true })
80
+
81
+ watch(inferedId, (n, o) => defer(null, null, 'id'))
82
+ watch(() => props.collection, (n, o) => defer(null, null, 'collection'))
83
+ watch(() => props.algoliaSearch, (n, o) => searchingAlgolia.value ? debounceInit.value() : init())
84
+ watch(() => props.algoliaFilters, (n, o) => searchingAlgolia.value ? debounceInit.value() : init())
85
+ watch(loading, (n, o) => emit('update:loading', loading.value))
86
+ watch(data, () => {
87
+ if (data?.value?.length && props.query?.limit && data?.value?.length < props.query?.limit) noneRemain.value = true
88
+ if (data?.value) emit('update:data', data.value)
89
+ })
90
+
91
+
92
+ function defer(n,o,origin) {
93
+ console.log('n,o,origin', n,o,origin)
94
+ if (n == o && origin == 'query') return console.log(`they're the same...`)
95
+ if (origin == 'query') oldQuery = n
96
+ nextTick(() => init())
97
+ }
98
+
99
+ async function loadMore() {
100
+
101
+ // console.log('loadMore')
102
+
103
+ if (!props.infinite || !data?.value?.length || loadingPage.value || noneRemain.value || props.disabled) return
104
+ loadingPage.value = true
105
+ let query = props.query ? JSON.parse(JSON.stringify(props.query)) : {}
106
+ if (query.search && !query.search?.wordSearch) delete query.search.wordSearch
107
+ autoPage.value ++
108
+ query.page = autoPage.value
109
+ if (searchingAlgolia.value) {
110
+ var nextPage = (await Mango[inferedCollection.value].search(props.algoliaSearch, query, props.algoliaFilters))?.hits
111
+ } else if (inferedCollection.value?.includes('/')) {
112
+ var nextPage = await Mango.relationRequest({...query, path: inferedCollection.value})
113
+ } else {
114
+ var nextPage = await Mango[inferedCollection.value](query)
115
+ }
116
+ if (nextPage.length) data.value = data.value.concat(nextPage)
117
+ if (!nextPage.length || (query.limit && nextPage.length < query.limit)) noneRemain.value = true
118
+ loadingPage.value = false
119
+
120
+ }
121
+
122
+ async function init() {
123
+
124
+ console.log('init')
125
+
126
+ // If the main entry is provided in ssr
127
+ if (props.main && window.mainEntry?.id == inferedId.value) {
128
+ data.value = window.mainEntry
129
+ window.mainEntry = null
130
+ emit('update:data', data.value)
131
+ return
132
+ }
133
+
134
+ if (active.value === false || props.disabled) return
135
+
136
+ console.log('active', active.value)
137
+
138
+ loading.value = true
139
+ data.value = null
140
+ noneRemain.value = false
141
+ autoPage.value = props.query?.page || 0
142
+
143
+ // Using the computed here won't work because it hasn't been computed yet
144
+ let searchingAlgolia = !!props.algoliaSearch || !!props.algoliaFilters
145
+
146
+ if (props.graphql){
147
+ data.value = await Mango.graphql(props.graphql)
148
+ emit('update:data', data.value)
149
+ loading.value = false
150
+ return
151
+ }
152
+
153
+ let collection = inferedCollection.value
154
+ let validatedCollection = Mango.collections.find(c => c.name == collection)
155
+ let id = props.id || props.query?.id || route.params?.id || null
156
+ if (props.id !== undefined) id = props.id // So you can pass null to id and get a list
157
+
158
+ let query = props.query ? JSON.parse(JSON.stringify(props.query)) : {}
159
+ if (query.search && !query.search?.wordSearch) delete query.search.wordSearch // So empty search still returns something
160
+
161
+ if (inferedCollection.value?.includes('/')) {
162
+ data.value = await Mango.relationRequest({...query, path: inferedCollection.value})
163
+ emit('update:data', data.value)
164
+ loading.value = false
165
+ return
166
+ }
167
+
168
+ // Global Algolia Search
169
+ if (props.globalSearch) {
170
+ data.value = await Mango.search(props.algoliaSearch, query, props.algoliaFilters)
171
+ emit('update:data', data.value)
172
+ loading.value = false
173
+ return
174
+ }
175
+
176
+ if (!validatedCollection) return console.error(`🥭 ${collection} is not a valid collection.`)
177
+ if (id && !searchingAlgolia) collection = validatedCollection.singular
178
+ // if (id) query = id
179
+
180
+ console.log('collection, id:', collection, id)
181
+ // console.log('collection', collection)
182
+
183
+ if (searchingAlgolia) {
184
+ console.log('collection', collection)
185
+ let algoliaResponse = await Mango[collection].search(props.algoliaSearch, query, props.algoliaFilters)
186
+ data.value = algoliaResponse.hits
187
+ totalResults.value = algoliaResponse.nbHits
188
+ emit('update:data', data.value)
189
+ loading.value = false
190
+ } else {
191
+ if (id) data.value = await Mango[collection](id, query)
192
+ else data.value = await Mango[collection](query)
193
+
194
+ emit('update:data', data.value)
195
+ loading.value = false
196
+
197
+ if (id && props.main) document.title = data.value?.title || siteName
198
+ else document.title = siteName
199
+
200
+ if (props.subscribe) {
201
+
202
+ let sub = function() {
203
+ let subscribeCollection = `${validatedCollection.singular}Change`
204
+ console.log('subscribeCollection', subscribeCollection)
205
+ subscribe(subscribeCollection, id)
206
+ }
207
+
208
+ sub()
209
+
210
+ let hidden;
211
+ let visibilityChange;
212
+ if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
213
+ hidden = "hidden";
214
+ visibilityChange = "visibilitychange";
215
+ } else if (typeof document.msHidden !== "undefined") {
216
+ hidden = "msHidden";
217
+ visibilityChange = "msvisibilitychange";
218
+ } else if (typeof document.webkitHidden !== "undefined") {
219
+ hidden = "webkitHidden";
220
+ visibilityChange = "webkitvisibilitychange";
221
+ }
222
+
223
+ document.addEventListener(visibilityChange, () => {
224
+ if (!document[hidden]) {
225
+ // Ensure connectivity
226
+ console.log('ensure connectivity')
227
+ init()
228
+ }
229
+
230
+ // else {
231
+ // if (webSocket?.send) {
232
+ // webSocket.send(JSON.stringify({
233
+ // type: GQL.CONNECTION_TERMINATE
234
+ // }))
235
+ // }
236
+ // webSocket = null
237
+ // }
238
+ }, false)
239
+
240
+ }
241
+
242
+
243
+ return
244
+ }
245
+
246
+ }
247
+
248
+ const GQL = {
249
+ CONNECTION_INIT: 'connection_init',
250
+ CONNECTION_ACK: 'connection_ack',
251
+ CONNECTION_ERROR: 'connection_error',
252
+ CONNECTION_KEEP_ALIVE: 'ka',
253
+ START: 'start',
254
+ STOP: 'stop',
255
+ CONNECTION_TERMINATE: 'connection_terminate',
256
+ DATA: 'data',
257
+ ERROR: 'error',
258
+ COMPLETE: 'complete'
259
+ }
260
+ function subscribe(collection, id) {
261
+
262
+ webSocket = new rws(Mango.ws, 'graphql-ws')
263
+
264
+ webSocket.onopen = event => {
265
+
266
+ webSocket.send(JSON.stringify({
267
+ type: GQL.CONNECTION_INIT,
268
+ payload: {}
269
+ }))
270
+
271
+ webSocket.send(JSON.stringify({
272
+ type: GQL.START,
273
+ id: Date.now(),
274
+ payload: {
275
+ query: `
276
+ subscription {
277
+ ${collection}(id: "${id}") {
278
+ id
279
+ settings {
280
+ clueTimeLimit
281
+ guessTimeLimit
282
+ }
283
+ guesses {
284
+ word
285
+ playerId
286
+ time
287
+ }
288
+ clue {
289
+ text
290
+ number
291
+ time
292
+ team
293
+ words
294
+ guesses
295
+ }
296
+ words {
297
+ value
298
+ color
299
+ guessed
300
+ }
301
+ }
302
+ }`,
303
+ variables: {},
304
+ operationName: null
305
+ }
306
+ }))
307
+ }
308
+
309
+ webSocket.onmessage = event => {
310
+ const response = JSON.parse(event.data)
311
+ switch (response.type) {
312
+ case GQL.CONNECTION_ACK: {
313
+ console.log('success')
314
+ break
315
+ }
316
+ case GQL.CONNECTION_ERROR: {
317
+ console.error(response.payload)
318
+ break
319
+ }
320
+ case GQL.CONNECTION_KEEP_ALIVE: {
321
+ break
322
+ }
323
+ case GQL.DATA: {
324
+ console.log(response.id, response.payload.errors, response.payload.data)
325
+ if (response.payload.data) {
326
+ data.value = response.payload.data[collection]
327
+ emit('update:data', data.value)
328
+ }
329
+ break
330
+ }
331
+ case GQL.COMPLETE: {
332
+ console.log('completed', response.id)
333
+ break
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ onUnmounted(() => {
340
+ if (webSocket?.send) {
341
+ webSocket.send(JSON.stringify({
342
+ type: GQL.CONNECTION_TERMINATE
343
+ }))
344
+ }
345
+ })
346
+
347
+ // init()
348
+ // await init()
349
+
350
+ if (props.suspend) {
351
+ await init()
352
+ } else {
353
+ init()
354
+ }
355
+ </script>
356
+
357
+ <script>
358
+ export default {
359
+ directives: {
360
+ infiniteScroll: {
361
+ mounted(element, binding) {
362
+ document.addEventListener("scroll", () => binding.dir.methods.handleScroll(element, binding.value))
363
+ },
364
+ unmounted(element, binding) {
365
+ document.removeEventListener("scroll", () => binding.dir.methods.handleScroll(element, binding.value))
366
+ },
367
+ methods: {
368
+ handleScroll(element, method) {
369
+ // console.log('triggered')
370
+ if (element.getBoundingClientRect().bottom - 100 < window.innerHeight) method()
371
+ }
372
+ }
373
+ }
374
+ },
375
+ watch: {
376
+ // id: 'defer',
377
+ // inferedId: 'oldDefer',
378
+ // collection: 'oldDefer',
379
+ // query: {
380
+ // handler(n, o) {
381
+ // let oldQuery = JSON.stringify(o)
382
+ // let newQuery = JSON.stringify(n)
383
+ // if (oldQuery != newQuery) console.log(`but they're not the same!`)
384
+ // this.defer(newQuery, oldQuery, 'query')
385
+ // },
386
+ // deep: true,
387
+ // },
388
+ // algoliaSearch() {
389
+ // if (this.searchingAlgolia) this.debounceInit()
390
+ // else this.init()
391
+ // },
392
+ // data() {
393
+ // if (this.data?.length && this.query?.limit && this.data?.length < this.query?.limit) {
394
+ // this.noneRemain = true
395
+ // }
396
+ // if (this.data) this.$emit('update:data', this.data)
397
+ // }
398
+ },
399
+ computed: {
400
+ // searchingAlgolia() {return !!this.algoliaSearch},
401
+ // inferedId() { return this.id !== undefined ? this.id : this.query?.id || this.$route.params?.id || null },
402
+ // debounceInit() {
403
+ // let timer;
404
+ // return (...args) => {
405
+ // this.loading = true
406
+ // clearTimeout(timer);
407
+ // timer = setTimeout(() => { this.init.apply(this, args); }, this.debounce);
408
+ // };
409
+ // }
410
+ },
411
+ methods: {
412
+ // async loadMore() {
413
+
414
+ // console.log('loadMore')
415
+
416
+ // if (!this.infinite || !this.data?.length || this.loadingPage || this.noneRemain) return
417
+ // this.loadingPage = true
418
+ // let query = this.query ? JSON.parse(JSON.stringify(this.query)) : {}
419
+ // if (query.search && !query.search?.wordSearch) delete query.search.wordSearch
420
+ // this.autoPage ++
421
+ // query.page = this.autoPage
422
+ // if (this.searchingAlgolia) {
423
+ // var nextPage = await Mango[this.inferedCollection].search(this.algoliaSearch, query)
424
+ // } else {
425
+ // var nextPage = await Mango[this.inferedCollection](query)
426
+ // }
427
+ // if (nextPage.length) this.data = this.data.concat(nextPage)
428
+ // if (!nextPage.length || (query.limit && nextPage.length < query.limit)) this.noneRemain = true
429
+ // this.loadingPage = false
430
+
431
+ // },
432
+ // oldDefer(n,o,origin) {
433
+ // console.log('n,o,origin', n,o,origin)
434
+ // if (n == o) { console.log(`they're the same...`); return }
435
+ // this.$nextTick(() => this.init())
436
+ // },
437
+ async save(data) {
438
+ data = data || this.data
439
+ if (Array.isArray(data)) {
440
+ console.log('You can only call save after querying for an individual document.')
441
+ return
442
+ } else {
443
+ if (this.saving) return
444
+ this.saving = true
445
+ let response = await Mango[this.inferedCollection].save(data)
446
+ this.saving = false
447
+ // if (response?.id) return Swal.fire({ title: 'Success!', icon: 'success', confirmButtonText: 'Awesome!' })
448
+ // return Swal.fire({ title: 'Error 😬', icon: 'error' })
449
+ }
450
+ }
451
+ },
452
+ activated() { this.active = true },
453
+ deactivated() { this.active = false },
454
+ }
455
+ </script>
@@ -0,0 +1,34 @@
1
+ import { reactive } from 'vue'
2
+
3
+ const screens = {
4
+ '2xs': 425,
5
+ xs: 475,
6
+ sm: 640,
7
+ md: 768,
8
+ lg: 1024,
9
+ xl: 1280
10
+ }
11
+
12
+ const breakpoints = reactive({ xs: false, sm: false, md: false, lg: false, xl: false })
13
+
14
+ const setBreakpoint = () => {
15
+ breakpoints['2xs'] = window.innerWidth >= screens['2xs']
16
+ breakpoints.xs = window.innerWidth >= screens.xs
17
+ breakpoints.sm = window.innerWidth >= screens.sm
18
+ breakpoints.md = window.innerWidth >= screens.md
19
+ breakpoints.lg = window.innerWidth >= screens.lg
20
+ breakpoints.xl = window.innerWidth >= screens.xl
21
+ }
22
+
23
+ const useBreakpoint = () => {
24
+
25
+ setBreakpoint()
26
+ window.addEventListener('resize', setBreakpoint)
27
+
28
+ return {
29
+ breakpoints
30
+ }
31
+ }
32
+
33
+ export default useBreakpoint
34
+ export { screens }
@@ -0,0 +1,38 @@
1
+ import { reactive } from 'vue'
2
+
3
+ const toggle = () => {
4
+ console.log('toggle')
5
+ if (darkMode.enabled) {
6
+ window.localStorage.setItem('darkMode', 'false')
7
+ } else {
8
+ window.localStorage.setItem('darkMode', 'true')
9
+ }
10
+ checkDarkMode()
11
+
12
+ }
13
+
14
+ const darkMode = reactive({ enabled: false, toggle })
15
+
16
+ const systemChange = () => {
17
+ window.localStorage.removeItem('darkMode')
18
+ darkMode.enabled = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
19
+ }
20
+
21
+ const checkDarkMode = () => {
22
+ let storedValue = window.localStorage.getItem('darkMode')
23
+ let enabledLocally = storedValue != null
24
+ let localBoolean = storedValue == 'true' ? true : false
25
+ let systemPreference = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
26
+ darkMode.enabled = enabledLocally ? localBoolean : systemPreference
27
+ }
28
+
29
+ const useDarkMode = () => {
30
+
31
+ checkDarkMode()
32
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', systemChange)
33
+
34
+ return { darkMode }
35
+
36
+ }
37
+
38
+ export default useDarkMode
@@ -0,0 +1,32 @@
1
+ import Swal from "sweetalert2"
2
+ import axios from "axios"
3
+
4
+ function validateEmail(email) {
5
+ var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
6
+ return re.test(String(email.trim()).toLowerCase());
7
+ }
8
+
9
+ function subscribeToMailChimp({ email, list, source, alert }) {
10
+
11
+ let lists = {
12
+ master: '816047355d',
13
+ devotional: '5fbac827f8'
14
+ }
15
+
16
+ let listId = lists[list]
17
+
18
+ if (!listId) return console.error(`"${list}" is not a valid email list`)
19
+ if (!source) return console.error(`You must provide a subscription source`)
20
+
21
+ if (!validateEmail(email)) {
22
+ new Swal('Invalid Email', `"${email}" is not a valid email address`, 'warning')
23
+ return console.error(`"${email}" is not a valid email address`)
24
+ }
25
+
26
+ axios.get(`https://ncfic.us2.list-manage.com/subscribe/post-json?u=12eae89c5f8969e490844c75a&id=${listId}&c=jQuery190019886783748288184_1530501492048&MMERGE3=${source}&EMAIL=${email}&b_12eae89c5f8969e490844c75a_816047355d=&subscribe=Subscribe&_=1530501492049`)
27
+ .then(response => { if (alert) new Swal('Subscribed!', 'Thank you for subscribing.', 'success') })
28
+ // MC apparently doesn't give a proper error message we can read at the time of writing this...
29
+ .catch(response => { if (alert) new Swal('Subscribed!', 'Thank you for subscribing.', 'success') })
30
+ }
31
+
32
+ export { subscribeToMailChimp, validateEmail }
@@ -0,0 +1,18 @@
1
+ export default (phone) => {
2
+ if (!phone) return ''
3
+ let formattedPhoneNumber = ''
4
+ let number = phone.replace(/\D/g, '').split('')
5
+ if (number.length == 11) {
6
+ formattedPhoneNumber += '+' + number[0] + ' '
7
+ number.splice(0, 1)
8
+ }
9
+ for (let [index, digit] in number) {
10
+ if (number.length >= 3) {
11
+ if (index == 0) formattedPhoneNumber += '('
12
+ if (index == 6) formattedPhoneNumber += '-'
13
+ if (number[index]) formattedPhoneNumber += number[index]
14
+ if (index == 2) formattedPhoneNumber += ') '
15
+ }
16
+ }
17
+ return formattedPhoneNumber
18
+ }