fix: build init error + update deps + markdown editor user settings (wip)

pull/7004/head
NGPixel 1 year ago
parent dec9272fbf
commit f8bc9e8c24
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -89,6 +89,18 @@ export default {
return WIKI.config.userDefaults return WIKI.config.userDefaults
}, },
async userEditorSettings (obj, args, context) {
if (!context.req.user || context.req.user.id === WIKI.auth.guest.id) {
throw new WIKI.Error.AuthRequired()
}
const config = await WIKI.db.knex('userEditorSettings').first('config').where({
id: context.req.user.id,
editor: args.editor
})
return config ?? {}
},
async lastLogins (obj, args, context, info) { async lastLogins (obj, args, context, info) {
if (!WIKI.auth.checkAccess(context.req.user, ['read:dashboard', 'read:users', 'write:users', 'manage:users'])) { if (!WIKI.auth.checkAccess(context.req.user, ['read:dashboard', 'read:users', 'write:users', 'manage:users'])) {
throw new Error('ERR_FORBIDDEN') throw new Error('ERR_FORBIDDEN')

@ -18,6 +18,10 @@ extend type Query {
userDefaults: UserDefaults userDefaults: UserDefaults
userEditorSettings(
editor: String!
): JSON
lastLogins: [UserLastLogin] lastLogins: [UserLastLogin]
userPermissions: [String] userPermissions: [String]

@ -1624,6 +1624,11 @@
"editor.select.customView": "or create a custom view?", "editor.select.customView": "or create a custom view?",
"editor.select.title": "Which editor do you want to use for this page?", "editor.select.title": "Which editor do you want to use for this page?",
"editor.settings": "Editor Settings", "editor.settings": "Editor Settings",
"editor.settings.markdown": "Markdown Editor Settings",
"editor.settings.markdownFontSize": "Editor Font Size",
"editor.settings.markdownFontSizeHint": "The font size to use in the editor.",
"editor.settings.markdownPreviewShown": "Display Render Preview",
"editor.settings.markdownPreviewShownHint": "Whether to display a preview of the rendered content.",
"editor.tableEditor.title": "Table Editor", "editor.tableEditor.title": "Table Editor",
"editor.togglePreviewPane": "Toggle Preview Pane", "editor.togglePreviewPane": "Toggle Preview Pane",
"editor.toggleScrollSync": "Toggle Scroll Sync", "editor.toggleScrollSync": "Toggle Scroll Sync",

@ -36,22 +36,22 @@
"node": ">=18.0" "node": ">=18.0"
}, },
"dependencies": { "dependencies": {
"@apollo/server": "4.9.4", "@apollo/server": "4.9.5",
"@azure/storage-blob": "12.16.0", "@azure/storage-blob": "12.17.0",
"@exlinc/keycloak-passport": "1.0.2", "@exlinc/keycloak-passport": "1.0.2",
"@graphql-tools/schema": "10.0.0", "@graphql-tools/schema": "10.0.0",
"@graphql-tools/utils": "10.0.7", "@graphql-tools/utils": "10.0.8",
"@hexagon/base64": "1.1.28", "@hexagon/base64": "1.1.28",
"@joplin/turndown-plugin-gfm": "1.0.50", "@joplin/turndown-plugin-gfm": "1.0.53",
"@node-saml/passport-saml": "4.0.4", "@node-saml/passport-saml": "4.0.4",
"@root/csr": "0.8.1", "@root/csr": "0.8.1",
"@root/keypairs": "0.10.3", "@root/keypairs": "0.10.3",
"@root/pem": "1.0.4", "@root/pem": "1.0.4",
"@simplewebauthn/server": "8.3.2", "@simplewebauthn/server": "8.3.5",
"@vue-email/compiler": "0.8.0-beta.4", "@vue-email/compiler": "0.8.0-beta.5",
"acme": "3.0.3", "acme": "3.0.3",
"akismet-api": "6.0.0", "akismet-api": "6.0.0",
"aws-sdk": "2.1478.0", "aws-sdk": "2.1499.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"chalk": "5.3.0", "chalk": "5.3.0",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
@ -60,7 +60,7 @@
"clean-css": "5.3.2", "clean-css": "5.3.2",
"command-exists": "1.2.9", "command-exists": "1.2.9",
"compression": "1.7.4", "compression": "1.7.4",
"connect-session-knex": "3.0.1", "connect-session-knex": "4.0.0",
"cookie-parser": "1.4.6", "cookie-parser": "1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"cron-parser": "4.9.0", "cron-parser": "4.9.0",
@ -76,7 +76,7 @@
"express": "4.18.2", "express": "4.18.2",
"express-brute": "1.0.1", "express-brute": "1.0.1",
"express-session": "1.17.3", "express-session": "1.17.3",
"file-type": "18.5.0", "file-type": "18.7.0",
"filesize": "10.1.0", "filesize": "10.1.0",
"fs-extra": "11.1.1", "fs-extra": "11.1.1",
"getos": "3.2.1", "getos": "3.2.1",
@ -98,7 +98,7 @@
"knex": "3.0.1", "knex": "3.0.1",
"lodash": "4.17.21", "lodash": "4.17.21",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"luxon": "3.4.3", "luxon": "3.4.4",
"markdown-it": "13.0.2", "markdown-it": "13.0.2",
"markdown-it-abbr": "1.0.4", "markdown-it-abbr": "1.0.4",
"markdown-it-attrs": "4.1.6", "markdown-it-attrs": "4.1.6",
@ -117,14 +117,14 @@
"mime-types": "2.1.35", "mime-types": "2.1.35",
"ms": "2.1.3", "ms": "2.1.3",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"nanoid": "5.0.2", "nanoid": "5.0.3",
"node-2fa": "2.0.3", "node-2fa": "2.0.3",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "6.9.7", "nodemailer": "6.9.7",
"objection": "3.1.2", "objection": "3.1.2",
"octokit": "3.1.1", "octokit": "3.1.2",
"passport": "0.6.0", "passport": "0.6.0",
"passport-auth0": "1.4.3", "passport-auth0": "1.4.4",
"passport-azure-ad": "4.3.5", "passport-azure-ad": "4.3.5",
"passport-cas": "0.1.1", "passport-cas": "0.1.1",
"passport-discord": "0.1.4", "passport-discord": "0.1.4",
@ -148,10 +148,10 @@
"pg-pubsub": "0.8.1", "pg-pubsub": "0.8.1",
"pg-query-stream": "4.5.3", "pg-query-stream": "4.5.3",
"pg-tsquery": "8.4.1", "pg-tsquery": "8.4.1",
"poolifier": "2.7.5", "poolifier": "3.0.5",
"prom-client": "15.0.0", "prom-client": "15.0.0",
"punycode": "2.3.0", "punycode": "2.3.1",
"puppeteer-core": "21.4.0", "puppeteer-core": "21.5.2",
"qr-image": "3.2.0", "qr-image": "3.2.0",
"remove-markdown": "0.5.0", "remove-markdown": "0.5.0",
"safe-regex": "2.1.1", "safe-regex": "2.1.1",
@ -166,19 +166,19 @@
"tar-fs": "3.0.4", "tar-fs": "3.0.4",
"turndown": "7.1.2", "turndown": "7.1.2",
"twemoji": "14.0.2", "twemoji": "14.0.2",
"ufo": "1.3.1", "ufo": "1.3.2",
"uslug": "1.0.4", "uslug": "1.0.4",
"uuid": "9.0.1", "uuid": "9.0.1",
"validate.js": "0.13.1", "validate.js": "0.13.1",
"vue": "3.3.7", "vue": "3.3.8",
"xss": "1.0.14", "xss": "1.0.14",
"yargs": "17.7.2" "yargs": "17.7.2"
}, },
"devDependencies": { "devDependencies": {
"eslint": "8.52.0", "eslint": "8.54.0",
"eslint-config-requarks": "1.0.7", "eslint-config-requarks": "1.0.7",
"eslint-config-standard": "17.1.0", "eslint-config-standard": "17.1.0",
"eslint-plugin-import": "2.28.1", "eslint-plugin-import": "2.29.0",
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"nodemon": "3.0.1" "nodemon": "3.0.1"

File diff suppressed because it is too large Load Diff

@ -7,17 +7,14 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "quasar dev", "dev": "quasar dev",
"build": "quasar build", "build": "NODE_OPTIONS=--max-old-space-size=8192 quasar build"
"lint": "eslint --ext .js,.vue ./",
"ncu": "ncu -i -x codemirror,codemirror-asciidoc",
"ncu-u": "ncu -u -x codemirror,codemirror-asciidoc"
}, },
"dependencies": { "dependencies": {
"@apollo/client": "3.8.6", "@apollo/client": "3.8.7",
"@lezer/common": "1.1.0", "@lezer/common": "1.1.1",
"@mdi/font": "7.3.67", "@mdi/font": "7.3.67",
"@quasar/extras": "1.16.7", "@quasar/extras": "1.16.8",
"@simplewebauthn/browser": "8.3.1", "@simplewebauthn/browser": "8.3.4",
"@tiptap/core": "2.1.12", "@tiptap/core": "2.1.12",
"@tiptap/extension-code-block": "2.1.12", "@tiptap/extension-code-block": "2.1.12",
"@tiptap/extension-code-block-lowlight": "2.1.12", "@tiptap/extension-code-block-lowlight": "2.1.12",
@ -43,8 +40,8 @@
"@tiptap/pm": "2.1.12", "@tiptap/pm": "2.1.12",
"@tiptap/starter-kit": "2.1.12", "@tiptap/starter-kit": "2.1.12",
"@tiptap/vue-3": "2.1.12", "@tiptap/vue-3": "2.1.12",
"@vue/repl": "2.6.1", "@vue/repl": "2.8.0",
"apollo-upload-client": "17.0.0", "apollo-upload-client": "18.0.1",
"browser-fs-access": "0.35.0", "browser-fs-access": "0.35.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"codemirror": "5.65.11", "codemirror": "5.65.11",
@ -52,16 +49,16 @@
"dependency-graph": "0.11.0", "dependency-graph": "0.11.0",
"filesize": "10.1.0", "filesize": "10.1.0",
"filesize-parser": "1.5.0", "filesize-parser": "1.5.0",
"fuse.js": "6.6.2", "fuse.js": "7.0.0",
"graphql": "16.6.0", "graphql": "16.6.0",
"graphql-tag": "2.12.6", "graphql-tag": "2.12.6",
"highlight.js": "11.9.0", "highlight.js": "11.9.0",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"jwt-decode": "3.1.2", "jwt-decode": "4.0.0",
"katex": "0.16.9", "katex": "0.16.9",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"lowlight": "3.1.0", "lowlight": "3.1.0",
"luxon": "3.4.3", "luxon": "3.4.4",
"markdown-it": "13.0.2", "markdown-it": "13.0.2",
"markdown-it-abbr": "1.0.4", "markdown-it-abbr": "1.0.4",
"markdown-it-attrs": "4.1.6", "markdown-it-attrs": "4.1.6",
@ -87,20 +84,21 @@
"prosemirror-schema-list": "1.3.0", "prosemirror-schema-list": "1.3.0",
"prosemirror-state": "1.4.3", "prosemirror-state": "1.4.3",
"prosemirror-transform": "1.8.0", "prosemirror-transform": "1.8.0",
"prosemirror-view": "1.32.1", "prosemirror-view": "1.32.4",
"pug": "3.0.2", "pug": "3.0.2",
"quasar": "2.13.0", "quasar": "2.14.0",
"slugify": "1.6.6", "slugify": "1.6.6",
"socket.io-client": "4.7.2", "socket.io-client": "4.7.2",
"sortablejs-vue3": "1.2.9", "sortablejs": "1.15.0",
"sortablejs-vue3": "1.2.11",
"tabulator-tables": "5.5.2", "tabulator-tables": "5.5.2",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"twemoji": "14.0.2", "twemoji": "14.0.2",
"typescript": "5.2.2", "typescript": "5.3.2",
"uuid": "9.0.1", "uuid": "9.0.1",
"v-network-graph": "0.9.10", "v-network-graph": "0.9.13",
"vue": "3.3.6", "vue": "3.3.8",
"vue-i18n": "9.5.0", "vue-i18n": "9.7.1",
"vue-router": "4.2.5", "vue-router": "4.2.5",
"vue3-otp-input": "0.4.1", "vue3-otp-input": "0.4.1",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
@ -108,18 +106,18 @@
"zxcvbn": "4.4.2" "zxcvbn": "4.4.2"
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "1.4.0", "@intlify/unplugin-vue-i18n": "1.5.0",
"@quasar/app-vite": "1.6.2", "@quasar/app-vite": "1.7.0",
"@types/lodash": "4.14.200", "@types/lodash": "4.14.202",
"@vue/language-plugin-pug": "1.8.19", "@vue/language-plugin-pug": "1.8.22",
"autoprefixer": "10.4.16", "autoprefixer": "10.4.16",
"browserlist": "latest", "browserlist": "latest",
"eslint": "8.52.0", "eslint": "8.54.0",
"eslint-config-standard": "17.1.0", "eslint-config-standard": "17.1.0",
"eslint-plugin-import": "2.28.1", "eslint-plugin-import": "2.29.0",
"eslint-plugin-n": "16.2.0", "eslint-plugin-n": "16.3.1",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-vue": "9.17.0" "eslint-plugin-vue": "9.18.1"
}, },
"engines": { "engines": {
"node": ">= 18.0", "node": ">= 18.0",

File diff suppressed because it is too large Load Diff

@ -98,8 +98,8 @@ module.exports = configure(function (ctx) {
manualChunks (id) { manualChunks (id) {
if (id.includes('lodash')) { if (id.includes('lodash')) {
return 'lodash' return 'lodash'
} else if (id.includes('quasar')) { // } else if (id.includes('quasar')) {
return 'quasar' // return 'quasar'
} else if (id.includes('pages/Admin')) { } else if (id.includes('pages/Admin')) {
return 'admin' return 'admin'
} else if (id.includes('pages/Profile')) { } else if (id.includes('pages/Profile')) {
@ -132,6 +132,7 @@ module.exports = configure(function (ctx) {
include: path.resolve(__dirname, './src/i18n/locales/**') include: path.resolve(__dirname, './src/i18n/locales/**')
}] }]
] ]
// sourcemap: true
}, },
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer

@ -2,7 +2,7 @@ import { boot } from 'quasar/wrappers'
import { ApolloClient, HttpLink, InMemoryCache, from, split } from '@apollo/client/core' import { ApolloClient, HttpLink, InMemoryCache, from, split } from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context' import { setContext } from '@apollo/client/link/context'
import { BatchHttpLink } from '@apollo/client/link/batch-http' import { BatchHttpLink } from '@apollo/client/link/batch-http'
import { createUploadLink } from 'apollo-upload-client' import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
import { useUserStore } from 'src/stores/user' import { useUserStore } from 'src/stores/user'

@ -9,6 +9,7 @@
icon='mdi-link-variant-plus' icon='mdi-link-variant-plus'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertLink') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertLink') }}
q-btn( q-btn(
@ -37,6 +38,8 @@
q-item-label From Clipboard... q-item-label From Clipboard...
q-item( q-item(
clickable clickable
@click='notImplemented'
v-close-popup
) )
q-item-section(side) q-item-section(side)
q-icon(name='las la-cloud-download-alt', color='blue') q-icon(name='las la-cloud-download-alt', color='blue')
@ -47,6 +50,7 @@
icon='mdi-code-json' icon='mdi-code-json'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertCodeBlock') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertCodeBlock') }}
q-btn( q-btn(
@ -60,29 +64,34 @@
icon='mdi-tab-plus' icon='mdi-tab-plus'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertTabset') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertTabset') }}
q-btn( q-btn(
icon='mdi-toy-brick-plus' icon='mdi-toy-brick-plus'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertBlock') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertBlock') }}
q-btn( q-btn(
icon='mdi-chart-multiline' icon='mdi-chart-multiline'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertDiagram') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertDiagram') }}
q-btn( q-btn(
icon='mdi-book-plus' icon='mdi-book-plus'
padding='sm sm' padding='sm sm'
flat flat
@click='notImplemented'
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertFootnote') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertFootnote') }}
q-btn( q-btn(
icon='mdi-cookie-plus' icon='mdi-cookie-plus'
padding='sm sm' padding='sm sm'
@click='notImplemented'
flat flat
) )
q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertEmoji') }} q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertEmoji') }}
@ -741,6 +750,13 @@ onBeforeUnmount(() => {
editor.dispose() editor.dispose()
} }
}) })
function notImplemented () {
$q.notify({
type: 'negative',
message: 'Not implemented'
})
}
</script> </script>
<style lang="scss"> <style lang="scss">

@ -50,22 +50,22 @@ q-layout(view='hHh lpR fFf', container)
q-item(tag='label') q-item(tag='label')
blueprint-icon(icon='enter-key') blueprint-icon(icon='enter-key')
q-item-section q-item-section
q-item-label {{t(`editor.settings.previewShown`)}} q-item-label {{t(`editor.settings.markdownPreviewShown`)}}
q-item-label(caption) {{t(`editor.settings.previewShownHint`)}} q-item-label(caption) {{t(`editor.settings.markdownPreviewShownHint`)}}
q-item-section(avatar) q-item-section(avatar)
q-toggle( q-toggle(
v-model='state.config.previewShown' v-model='state.config.previewShown'
color='primary' color='primary'
checked-icon='las la-check' checked-icon='las la-check'
unchecked-icon='las la-times' unchecked-icon='las la-times'
:aria-label='t(`editor.settings.previewShown`)' :aria-label='t(`editor.settings.markdownPreviewShown`)'
) )
q-separator.q-my-sm(inset) q-separator.q-my-sm(inset)
q-item q-item
blueprint-icon(icon='width') blueprint-icon(icon='width')
q-item-section q-item-section
q-item-label {{t(`editor.settings.fontSize`)}} q-item-label {{t(`editor.settings.markdownFontSize`)}}
q-item-label(caption) {{t(`editor.settings.fontSizeHint`)}} q-item-label(caption) {{t(`editor.settings.markdownFontSizeHint`)}}
q-item-section(side) q-item-section(side)
q-input( q-input(
type='number' type='number'
@ -75,7 +75,7 @@ q-layout(view='hHh lpR fFf', container)
outlined outlined
v-model='state.config.fontSize' v-model='state.config.fontSize'
dense dense
:aria-label='t(`editor.settings.fontSize`)' :aria-label='t(`editor.settings.markdownFontSize`)'
) )
q-inner-loading(:showing='state.loading > 0') q-inner-loading(:showing='state.loading > 0')
@ -129,14 +129,15 @@ async function load () {
try { try {
const resp = await APOLLO_CLIENT.query({ const resp = await APOLLO_CLIENT.query({
query: gql` query: gql`
query loadEditorUserSettings ( query loadEditorUserSettings {
$editor: String! userEditorSettings (editor: "markdown")
) { }
editorUserSettings (editor: "markdown") `,
}`,
fetchPolicy: 'network-only' fetchPolicy: 'network-only'
}) })
state.config = cloneDeep(resp?.data?.editorUserSettings) const respConf = cloneDeep(resp?.data?.userEditorSettings)
state.config.previewShown = respConf.previewShown ?? true
state.config.fontSize = respConf.fontSize ?? 16
} catch (err) { } catch (err) {
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
@ -155,7 +156,7 @@ async function save () {
mutation saveEditorUserSettings ( mutation saveEditorUserSettings (
$config: JSON! $config: JSON!
) { ) {
saveEditorUserSettings ( saveUserEditorSettings (
editor: "markdown" editor: "markdown"
config: $config config: $config
) { ) {
@ -171,14 +172,14 @@ async function save () {
config: state.config config: state.config
} }
}) })
if (respRaw?.data?.saveEditorUserSettings?.operation?.succeeded) { if (respRaw?.data?.saveUserEditorSettings?.operation?.succeeded) {
$q.notify({ $q.notify({
type: 'positive', type: 'positive',
message: t('admin.editors.markdown.saveSuccess') message: t('admin.editors.markdown.saveSuccess')
}) })
close() close()
} else { } else {
throw new Error(respRaw?.data?.saveEditorUserSettings?.operation?.message || 'An unexpected error occured.') throw new Error(respRaw?.data?.saveUserEditorSettings?.operation?.message || 'An unexpected error occured.')
} }
} catch (err) { } catch (err) {
$q.notify({ $q.notify({

@ -327,7 +327,7 @@ import { DateTime } from 'luxon'
import { cloneDeep, dropRight, find, findKey, initial, last, nth } from 'lodash-es' import { cloneDeep, dropRight, find, findKey, initial, last, nth } from 'lodash-es'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import Fuse from 'fuse.js/dist/fuse.basic.esm' import Fuse from 'fuse.js/basic'
import NewMenu from './PageNewMenu.vue' import NewMenu from './PageNewMenu.vue'
import Tree from './TreeNav.vue' import Tree from './TreeNav.vue'

@ -8,7 +8,7 @@ q-menu.translucent-menu(
q-item( q-item(
clickable clickable
@click='create(`wysiwyg`)' @click='create(`wysiwyg`)'
v-if='siteStore.editors.wysiwyg' v-if='siteStore.editors.wysiwyg && flagsStore.experimental'
) )
blueprint-icon(icon='google-presentation') blueprint-icon(icon='google-presentation')
q-item-section.q-pr-sm New Page q-item-section.q-pr-sm New Page

@ -1,5 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import jwtDecode from 'jwt-decode' import { jwtDecode } from 'jwt-decode'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'

Loading…
Cancel
Save