Browse Source

first commit

master
P.BARRY 6 years ago
commit
922e25bd94
53 changed files with 14075 additions and 0 deletions
  1. 18
      .eslintrc.js
  2. 88
      .gitignore
  3. 22
      README.md
  4. 7
      assets/README.md
  5. 1
      assets/variables.scss
  6. 106
      components/FormGen/FormGen.vue
  7. 79
      components/Logo.vue
  8. 118
      components/MediaUploader.vue
  9. 7
      components/README.md
  10. 168
      components/SelectApi.vue
  11. 166
      components/TemplateEditor/TemplateEditor.vue
  12. 94
      components/TemplateEditor/modals/TeAddSection.vue
  13. 14
      components/TemplateEditor/widgets/TeImg.vue
  14. 113
      components/TemplateEditor/widgets/TeSection.vue
  15. 12
      components/TemplateEditor/widgets/TeText.vue
  16. 21
      components/VuetifyLogo.vue
  17. 5
      config.js
  18. 7
      layouts/README.md
  19. 100
      layouts/admin.vue
  20. 117
      layouts/default.ori.vue
  21. 74
      layouts/default.vue
  22. 44
      layouts/error.vue
  23. 8
      middleware/README.md
  24. 6
      middleware/renderer.js
  25. 92
      nuxt.config.js
  26. 11467
      package-lock.json
  27. 29
      package.json
  28. 6
      pages/README.md
  29. 40
      pages/admin/index.vue
  30. 33
      pages/admin/landingpage/_id.vue
  31. 94
      pages/admin/landingpage/index.vue
  32. 99
      pages/admin/resources/_rsc/_id.vue
  33. 199
      pages/admin/resources/_rsc/index.vue
  34. 53
      pages/admin/resources/index.vue
  35. 92
      pages/index.ori.vue
  36. 217
      pages/index.vue
  37. 19
      pages/inspire.vue
  38. 7
      plugins/README.md
  39. 90
      plugins/vuetify-confirm.component.vue
  40. 19
      plugins/vuetify-confirm.js
  41. 28
      plugins/vuetify-confirm.plugin.js
  42. 11
      static/README.md
  43. BIN
      static/assets/hero.jpeg
  44. BIN
      static/assets/section.jpg
  45. BIN
      static/assets/vuetify.png
  46. BIN
      static/favicon.ico
  47. BIN
      static/v.png
  48. 10
      store/README.md
  49. 5
      store/admin/landingpage.js
  50. 5
      store/admin/resources.js
  51. 43
      store/admin/ui.js
  52. 13
      store/index.js
  53. 9
      store/ui/index.js

18
.eslintrc.js

@ -0,0 +1,18 @@
module.exports = {
root: true,
env: {
browser: true,
node: true
},
parserOptions: {
parser: 'babel-eslint'
},
extends: [
'@nuxtjs',
'plugin:nuxt/recommended'
],
// add your custom rules here
rules: {
"vue/attribute-hyphenation": "never"
}
}

88
.gitignore

@ -0,0 +1,88 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
.editorconfig
# Service worker
sw.*
# Mac OSX
.DS_Store

22
README.md

@ -0,0 +1,22 @@
# artifex
> An artifex world for noob
## Build Setup
``` bash
# install dependencies
$ npm run install
# serve with hot reload at localhost:3000
$ npm run dev
# build for production and launch server
$ npm run build
$ npm run start
# generate static project
$ npm run generate
```
For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).

7
assets/README.md

@ -0,0 +1,7 @@
# ASSETS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).

1
assets/variables.scss

@ -0,0 +1 @@
$font-size-root: 20px;

106
components/FormGen/FormGen.vue

@ -0,0 +1,106 @@
<template>
<v-form ref="formGen" lazy-loading>
<v-layout>
<v-flex v-for="(row, idx) in form" :key="`row-${idx}`">
<v-layout column mx-3>
<v-flex v-for="input in row.list" :key="input.field">
<v-text-field
v-if="input.type === 'text'"
v-model="doc[input.field]"
:label="input.label"
:disabled="input.disabled || disabled"
:rules="getRules(input)"
@change="triggerUp()"
/>
<v-select
v-if="input.type === 'select'"
v-model="doc[input.field]"
:label="input.label"
:items="input.items"
:disabled="input.disabled || disabled"
:multiple="input.multiple"
item-text="label"
@change="triggerUp()"
/>
<media-uploader
v-if="input.type === 'media'"
v-model="doc[input.field]"
:disabled="input.disabled || disabled"
:doc-coll="coll.type"
:doc-id="coll.id"
:doc-path="input.field"
@change="triggerUp()"
/>
<select-api
v-if="input.type === 'select-api'"
v-model="doc[input.field]"
:label="input.label"
:collection="input.items"
:multiple="input.multiple"
@change="triggerUp()"
/>
<v-textarea
v-if="input.type === 'textarea'"
v-model="doc[input.field]"
:label="input.label"
:disabled="input.disabled || disabled"
@change="triggerUp()"
/>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-form>
</template>
<script>
import MediaUploader from '@/components/MediaUploader.vue'
import SelectApi from '@/components/SelectApi.vue'
export default {
name: 'FormGen',
components: { MediaUploader, SelectApi },
props: {
value: { type: Object, default: () => {} },
more: { type: String, default: null },
form: { type: Array, default: () => {} },
coll: { type: Object, default: null },
disabled: { type: Boolean, default: false }
},
data: () => ({
doc: {}
}),
mounted () {
if (this.more) {
this.value[this.more] = this.value[this.more] || {}
this.doc = this.value[this.more]
} else {
this.doc = this.value
}
},
methods: {
getRules (input) {
if (!input.rules) { return [] }
const rules = []
if (input.rules.required) {
rules.push(v => !!v || input.rules.required)
}
if (input.rules.regex) {
const aRegex = input.rules.regex.split(';')
const nRegex = new RegExp(aRegex[0], aRegex[1])
rules.push(v => nRegex.test(v) || aRegex[2] || 'Field must be valid')
}
return rules
},
triggerUp () {
const valid = this.$refs.formGen.validate()
this.$emit('update')
this.$emit('valid', valid)
}
}
}
</script>

79
components/Logo.vue

@ -0,0 +1,79 @@
<template>
<div class="VueToNuxtLogo">
<div class="Triangle Triangle--two" />
<div class="Triangle Triangle--one" />
<div class="Triangle Triangle--three" />
<div class="Triangle Triangle--four" />
</div>
</template>
<style>
.VueToNuxtLogo {
display: inline-block;
animation: turn 2s linear forwards 1s;
transform: rotateX(180deg);
position: relative;
overflow: hidden;
height: 180px;
width: 245px;
}
.Triangle {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
}
.Triangle--one {
border-left: 105px solid transparent;
border-right: 105px solid transparent;
border-bottom: 180px solid #41b883;
}
.Triangle--two {
top: 30px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 87.5px solid transparent;
border-right: 87.5px solid transparent;
border-bottom: 150px solid #3b8070;
}
.Triangle--three {
top: 60px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 70px solid transparent;
border-right: 70px solid transparent;
border-bottom: 120px solid #35495e;
}
.Triangle--four {
top: 120px;
left: 70px;
animation: godown 0.5s linear forwards 3s;
border-left: 35px solid transparent;
border-right: 35px solid transparent;
border-bottom: 60px solid #fff;
}
@keyframes turn {
100% {
transform: rotateX(0deg);
}
}
@keyframes godown {
100% {
top: 180px;
}
}
@keyframes goright {
100% {
left: 70px;
}
}
</style>

118
components/MediaUploader.vue

@ -0,0 +1,118 @@
<template>
<div>
<v-layout
ref="upload-area"
class="upload-area"
:style="{cursor: disabled ? 'not-allowed' : 'pointer'}"
align-center
justify-center
row
fill-height
@click="selectMedia"
>
<v-flex xs12 style="text-align: center">
<span v-if="disabled" />
<span v-else-if="!uploadOnGoing">Upload one media</span>
<span v-else>Uploading {{ percent }}%</span>
</v-flex>
</v-layout>
<div class="upload-note">
{{ note }}
</div>
<input ref="uploader" type="file" style="display:none" @change="onMediaSelected">
</div>
</template>
<script>
import cfg from '@/config'
export default {
model: { prop: 'mid', event: 'change' },
props: {
mid: { type: String, default: '' },
docColl: { type: String, default: '' },
docId: { type: String, default: '' },
docPath: { type: String, default: '' },
disabled: { type: Boolean, default: false }
},
data: () => ({
uploadOnGoing: false,
percent: 0,
selectedMedia: null,
mediaId: null,
mediaInfo: null
}),
computed: {
note () {
return this.mediaInfo ? this.mediaInfo.originalName : ''
}
},
watch: {
mid (val) {
if (!val) { return }
this.mediaId = val
this.updateBg()
}
},
mounted () {},
methods: {
updateBg () {
if (!this.mediaId) { return }
this.$refs['upload-area'].style.backgroundImage = `url('${cfg.serverUrl}/media/preview/${this.mediaId}?height=120&timestamp=${(new Date()).getTime()}')`
this.$axios
.get(`${cfg.serverUrl}/api/media/${this.mediaId}`)
.then((res) => {
this.mediaInfo = res.data.data
})
},
onMediaSelected (event) {
this.selectedMedia = event.target.files[0]
this.uploadOnGoing = true
this.percent = 0
const formData = new FormData()
formData.append('media', this.selectedMedia, this.selectedMedia.name)
formData.append('docColl', this.docColl || '')
formData.append('docId', this.docId || '')
formData.append('docPath', this.docPath || '')
this.$axios
.post(`${cfg.serverUrl}/media/upload`, formData, {
onUploadProgress: (progressEvent) => {
this.percent = Math.ceil(progressEvent.loaded / progressEvent.total * 100)
}
})
.then((pRes) => {
this.uploadOnGoing = false
this.selectedMedia = null
this.mediaId = pRes.data.mediaId
this.$emit('change', this.mediaId)
this.updateBg()
})
.catch((pRes) => {
this.uploadOnGoing = false
this.selectedMedia = null
})
},
selectMedia () {
if (this.disabled) { return }
if (this.uploadOnGoing) { return }
this.$refs.uploader.click()
}
}
}
</script>
<style scoped>
.upload-area {
height: 120px;
background: #CCC;
box-shadow: 0 0 8px #666 inset;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
.upload-note {
color: #AAA;
}
</style>

7
components/README.md

@ -0,0 +1,7 @@
# COMPONENTS
**This directory is not required, you can delete it if you don't want to use it.**
The components directory contains your Vue.js Components.
_Nuxt.js doesn't supercharge these components._

168
components/SelectApi.vue

@ -0,0 +1,168 @@
<template>
<v-layout column>
<v-flex>
<v-subheader>{{ label }}</v-subheader>
</v-flex>
<v-flex>
<v-list dense>
<v-list-item
v-for="item in doc"
:key="item.key"
>
<v-list-item-content>
<v-list-item-title v-text="item.label" />
</v-list-item-content>
<v-list-item-action style="margin:0">
<v-layout>
<v-flex>
<v-btn small icon @click="viewItem(item)">
<v-icon>mdi-eye</v-icon>
</v-btn>
</v-flex>
<v-flex>
<v-btn small icon @click="deleteItem(item)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</v-flex>
</v-layout>
</v-list-item-action>
</v-list-item>
</v-list>
</v-flex>
<v-flex class="text-center">
<v-btn v-if="mode !== 'adding' && canAdd" @click="add()">
<v-icon>mdi-plus-circle-outline</v-icon>
</v-btn>
<v-autocomplete
v-if="mode === 'adding'"
v-model="selectedItem"
:items="items"
:search-input.sync="search"
:loading="isLoading"
cache-items
item-text="name"
item-value="_id"
hide-no-data
hide-details
label="Find"
@change="addItem"
/>
</v-flex>
</v-layout>
</template>
<script>
export default {
model: { prop: 'value', event: 'change' },
props: {
value: { type: Array, default: () => [] },
label: { type: String, default: 'SelectApi' },
collection: { type: String, default: '', required: true },
multiple: { type: [Number, Boolean], default: false }
},
data: () => ({
mode: '',
items: [],
isLoading: false,
selectedItem: null,
search: '',
doc: [],
byId: {},
fromValueDone: false
}),
computed: {
canAdd () {
if (this.multiple) {
if (typeof this.multiple === 'number') {
return this.doc.length < this.multiple
} else {
return true
}
} else {
return this.doc.length === 0
}
}
},
watch: {
search (val) {
val && val.length >= 3 && val !== this.select && this.querySelections(val)
},
value (val) {
if (!val || this.fromValueDone) { return }
this.doc = val.map(id => ({ id, label: null, key: null }))
this.updateLabelFromId()
this.fromValueDone = true
}
},
methods: {
add () {
this.mode = 'adding'
},
viewItem (item) {
window.open(`/admin/resources/${this.collection}/${item.id}`, '_blank')
},
deleteItem (item) {
this
.$confirm(`Supprimer <b>${item.label}</b> ?`)
.then((res) => {
if (res) {
this.doc = this.doc.filter(val => val.id !== item.id)
this.updateLabelFromId()
this.$emit('change', this.doc.map(item => item.id))
}
})
},
addItem () {
this.doc.push({ id: this.selectedItem, label: null, key: null })
this.selectedItem = null
this.mode = ''
this.search = ''
this.updateLabelFromId()
this.$emit('change', this.doc.map(item => item.id))
},
querySelections (val) {
this.isLoading = true
this.$axios.get(`/cloud/api/${this.collection}?name__regex=${val},i&projection=name:1`)
.then((res) => {
this.items = res.data.data
})
.finally(() => (this.isLoading = false))
},
getTitle (item) {
return this.byId[item] || item
},
updateLabelFromId () {
const idsToGet = []
this.doc.forEach((item) => {
if (!this.byId[item.id]) {
item.key = this.__generateKey(item.id)
item.label = item.id
this.byId[item.id] = item
}
})
for (const key in this.byId) {
if (this.byId[key].label === key) {
idsToGet.push(key)
}
}
if (!idsToGet.length) { return }
this.$axios.get(`/cloud/api/${this.collection}?_id__in=${idsToGet.join(',')}&projection=name:1`)
.then((res) => {
res.data.data.forEach((doc) => {
this.byId[doc._id].label = doc.name
})
})
},
__generateKey (id) {
const ts = (new Date()).getTime()
return `${id}${ts}`
}
}
}
</script>

166
components/TemplateEditor/TemplateEditor.vue

@ -0,0 +1,166 @@
<template>
<v-container fluid>
<draggable :list="template">
<transition-group>
<div v-for="section in template" :key="section.uid" :style="section.sectionStyle" style="position: relative">
<v-layout column class="text-xs-center te-section--menu">
<v-flex class="te-section--btn" elevation-4>
<v-icon dark>
mdi-cursor-move
</v-icon>
</v-flex>
<v-flex class="te-section--btn" elevation-4 @click.stop="editSection(section)">
<v-icon dark>
mdi-pencil
</v-icon>
</v-flex>
<v-flex class="te-section--btn" elevation-4 @click.stop="removeSection(section)">
<v-icon dark>
mdi-delete
</v-icon>
</v-flex>
</v-layout>
<te-section :colls="section.colls" :style="section.containerStyle" />
</div>
</transition-group>
</draggable>
<v-layout
text-xs-center
align-center
justify-center
row
fill-height
@click="openAddSection()"
>
<v-flex class="lp-add-row">
<v-card>
<v-card-text>ADD SECTION</v-card-text>
</v-card>
</v-flex>
</v-layout>
<te-add-section @newsection="addSection" />
<v-navigation-drawer
v-model="rightDrawer"
temporary
right
fixed
:width="500"
>
<h1 />
<v-tabs fixed-tabs>
<v-tab key="setting">
Settings
</v-tab>
<v-tab-item key="settings" />
</v-tabs>
</v-navigation-drawer>
</v-container>
</template>
<script>
import draggable from 'vuedraggable'
import TeSection from './widgets/TeSection.vue'
import TeAddSection from './modals/TeAddSection.vue'
export default {
components: { draggable, TeSection, TeAddSection },
model: { prop: 'src', event: 'change' },
props: {
src: { type: Array, default: () => [] }
},
data: () => ({
template: [],
rightDrawer: false
}),
mounted () {
this.addSection([4, 4, 4])
this.addSection([6, 6])
// console.log(this.template)
// setInterval(() => {
// console.log(JSON.stringify(this.myArray))
// }, 3000)
},
methods: {
openAddSection () {
this.$emit('section:add')
},
editSection (section) {
this.rightDrawer = true
},
removeSection (section) {
this.$confirm('Do you really want to remove this section?', { title: 'Remove Section' })
.then((res) => {
if (res) { this.template = this.template.filter(sec => sec.name !== section.name) }
})
},
addSection (pTemplate) {
const uid = Math.random().toString(36).slice(2, 10)
const section = {
name: `Section ${uid}`,
sectionStyle: {
backgroundColor: 'red',
margin: '1em 0'
},
containerStyle: {
color: 'blue'
},
uid,
colls: []
}
pTemplate.forEach((size, idx) => {
section.colls.push({
size,
style: {},
uid: `${uid}-${idx}`,
list: [
{
type: 'TeImg',
uid: '123456700' + uid + idx,
data: {
src: 'https://picsum.photos/id/370/200/200'
}
},
{
type: 'TeText',
uid: '1234567' + uid + idx,
data: {
text: size === '2' ? 'Cupidatat enim id consequat deserunt elit eiusmod ullamco ipsum irure ullamco proident. Enim consectetur commodo incididunt in ad esse. Voluptate proident et aliquip est tempor est duis reprehenderit consequat. Ad cupidatat nisi sit incididunt tempor amet laborum adipisicing incididunt nostrud minim.' : 'Sunt mollit ut quis adipisicing consectetur pariatur laboris Lorem sunt quis deserunt id.'
}
},
{
type: 'TeText',
uid: '12345678' + uid + idx,
data: {
text: size === '2' ? 'Cupidatat enim id consequat deserunt elit eiusmod ullamco ipsum irure ullamco proident. Enim consectetur commodo incididunt in ad esse. Voluptate proident et aliquip est tempor est duis reprehenderit consequat. Ad cupidatat nisi sit incididunt tempor amet laborum adipisicing incididunt nostrud minim.' : 'Sunt mollit ut quis adipisicing consectetur pariatur laboris Lorem sunt quis deserunt id.'
}
}
]
})
})
this.template.push(section)
}
}
}
</script>
<style scoped>
.lp-add-row {
margin: 10px;
cursor: pointer;
}
.te-section--menu {
position: absolute;
width:30px;
left:-30px;
}
.te-section--btn {
background: #888;
margin: 2px 0;
height: 30px;
cursor: pointer;
}
.te-section--btn:hover {
background: #555;
}
</style>

94
components/TemplateEditor/modals/TeAddSection.vue

@ -0,0 +1,94 @@
<template>
<div class="text-xs-center">
<v-dialog
v-model="dialog"
scrollable
width="500"
>
<v-card>
<v-card-title
class="headline grey lighten-2"
primary-title
>
Select new section format
</v-card-title>
<v-card-text>
<div v-for="(format, fIdx) in formats" :key="format.join()">
<h4 v-if="showTitle(fIdx)">
{{ format.length }} Column(s)
</h4>
<v-layout row ma-2 elevation-3 class="add-section" @click="selectSection(format)">
<v-flex v-for="(size, idx) in format" :key="format.join()+idx" :[`xs${size}`]="true" class="add-section--coll" />
</v-layout>
</div>
</v-card-text>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
text
@click="dialog = false"
>
Fermer
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data: () => ({
dialog: false,
formats: [
[12],
[6, 6],
[4, 8],
[8, 4],
[4, 4, 4],
[3, 6, 3],
[3, 3, 3, 3]
]
}),
created () {
this.$parent.$on('section:add', this.openDialog)
},
methods: {
selectSection (format) {
this.$emit('newsection', format)
this.dialog = false
},
openDialog () {
this.dialog = true
},
showTitle (idx) {
if (idx === 0) {
return true
} else {
const cLength = this.formats[idx].length
const nLength = this.formats[idx - 1].length
return cLength !== nLength
}
}
}
}
</script>
<style scoped>
.add-section--coll {
outline: 1px dashed;
}
.add-section {
cursor: pointer;
min-height: 30px;
margin: 0.5em 0;
}
.add-section:hover {
background: #CCC;
}
</style>

14
components/TemplateEditor/widgets/TeImg.vue

@ -0,0 +1,14 @@
<template>
<div>
<v-img height="100" :src="src" aspect-ratio="2" :contain="true" />
</div>
</template>
<script>
export default {
name: 'TeImg',
props: {
src: { type: String, default: '' }
}
}
</script>

113
components/TemplateEditor/widgets/TeSection.vue

@ -0,0 +1,113 @@
<template>
<v-container style="position: relative">
<v-layout justify-space-around row wrap>
<v-flex
v-for="coll in colls"
:key="coll.uid"
:style="coll.style"
xs12
:[getSize(coll)]="true"
xxxclass="te-coll"
>
<!-- <div class="te-coll--menu">
MOVE
</div> -->
<draggable
class="te-section"
:list="coll.list"
:group="{name: 'te-widget'}"
v-bind="dragOptions"
@start="drag = true"
@end="drag = false"
>
<transition-group
type="transition"
:name="!drag ? 'flip-list' : null"
tag="v-layout"
class="column"
>
<v-flex v-for="widget in coll.list" :key="widget.uid">
<component :is="widget.type" v-bind="widget.data" />
</v-flex>
<v-flex v-if="!coll.list.length" :key="`${coll.uid}--more`">
---
</v-flex>
</transition-group>
</draggable>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
// :grow="coll.size === 'g'" :shrink="coll.size === 's'"
import draggable from 'vuedraggable'
import TeText from './TeText.vue'
import TeImg from './TeImg.vue'
export default {
components: { draggable, TeText, TeImg },
props: {
colls: { type: Array, default: () => [] }
},
data: () => ({
fullText: false,
text: 'Nisi ex cillum ut mollit. Eiusmod velit ea labore do esse magna commodo occaecat. Irure ex velit culpa elit excepteur incididunt dolore fugiat esse veniam amet. Cupidatat duis voluptate quis dolor dolor veniam duis aliqua eu proident ipsum tempor cupidatat. Occaecat ea officia anim exercitation officia enim quis mollit id veniam id eiusmod qui in. Proident eu do cillum eu quis occaecat ex ipsum adipisicing exercitation ad elit sunt.',
drag: false
}),
computed: {
dragOptions () {
return {
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost'
}
}
},
mounted () {
},
methods: {
getSize (coll) {
return `md${coll.size}`
}
}
}
</script>
<style scoped>
.te-section {
min-height: 100px;
outline: 1px dashed;
margin-bottom: 1em;
/* position: relative; */
}
.te-coll {
/* position: relative; */
}
.te-coll--menu {
position: absolute;
height:30px;
top:-30px;
background: blue;
}
.item {
min-width: 100px;
min-height: 30px;
margin: 10px;
text-align: center;
}
.ghost {
opacity: 0.5;
background: #c8ebfb;
}
</style>
<style scoped>
@media (min-width: 1904px) {
.container {
max-width: 1300px!important;
padding: 0;
}
}
</style>

12
components/TemplateEditor/widgets/TeText.vue

@ -0,0 +1,12 @@
<template>
<div>{{ text }}</div>
</template>
<script>
export default {
name: 'TeText',
props: {
text: { type: String, default: '' }
}
}
</script>

21
components/VuetifyLogo.vue

@ -0,0 +1,21 @@
<template>
<img
class="VuetifyLogo"
alt="Vuetify Logo"
src="data:image/png;base64,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"
>
</template>
<style>
.VuetifyLogo {
width: 180px;
transform: rotateY(560deg);
animation: turn 3.5s ease-out forwards 1s;
}
@keyframes turn {
100% {
transform: rotateY(0deg);
}
}
</style>

5
config.js

@ -0,0 +1,5 @@
const CFG = {
serverUrl: 'http://localhost:3009'
}
export default CFG

7
layouts/README.md

@ -0,0 +1,7 @@
# LAYOUTS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Application Layouts.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).

100
layouts/admin.vue

@ -0,0 +1,100 @@
<template>
<v-app dark>
<v-navigation-drawer
v-model="drawer"
:mini-variant="miniVariant"
clipped
fixed
app
>
<v-list>
<v-list-item
v-for="(item, i) in menu"
:key="i"
:to="item.to"
router
exact
>
<v-list-item-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-app-bar
clipped-left
fixed
app
>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
<v-btn
icon
@click.stop="miniVariant = !miniVariant"
>
<v-icon>mdi-{{ `chevron-${miniVariant ? 'right' : 'left'}` }}</v-icon>
</v-btn>
<v-toolbar-title v-text="title" />
<v-spacer />
</v-app-bar>
<v-content>
<v-breadcrumbs :items="breadcrumbs">
<template v-slot:divider>
<v-icon>mdi-chevron-right</v-icon>
</template>
</v-breadcrumbs>
<h2 class="display-1 grey lighten-4 font-weight-thin">
<v-container>
{{ title }}
</v-container>
</h2>
<nuxt />
</v-content>
<v-footer
fixed
app
>
<span>Nubium Artifex &copy; 2019</span>
</v-footer>
<v-snackbar
:value="snackbar.showIt"
:timeout="snackbar.timeout"
:color="snackbar.color"
>
{{ snackbar.msg }}
<v-btn
text
@click="$store.commit('admin/ui/snackClose')"
>
Fermer
</v-btn>
</v-snackbar>
</v-app>
</template>
<script>
export default {
data: () => ({
drawer: true,
miniVariant: false
}),
computed: {
title () {
return this.$store.state.admin.ui.title
},
breadcrumbs () {
return this.$store.state.admin.ui.breadcrumbs
},
menu () {
return this.$store.state.admin.ui.menu
},
snackbar () {
return this.$store.state.admin.ui.snackbar
}
},
methods: {}
}
</script>

117
layouts/default.ori.vue

@ -0,0 +1,117 @@
<template>
<v-app dark>
<v-navigation-drawer
v-model="drawer"
:mini-variant="miniVariant"
:clipped="clipped"
fixed
app
>
<v-list>
<v-list-item
v-for="(item, i) in items"
:key="i"
:to="item.to"
router
exact
>
<v-list-item-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-app-bar
:clipped-left="clipped"
fixed
app
>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
<v-btn
icon
@click.stop="miniVariant = !miniVariant"
>
<v-icon>mdi-{{ `chevron-${miniVariant ? 'right' : 'left'}` }}</v-icon>
</v-btn>
<v-btn
icon
@click.stop="clipped = !clipped"
>
<v-icon>mdi-application</v-icon>
</v-btn>
<v-btn
icon
@click.stop="fixed = !fixed"
>
<v-icon>mdi-minus</v-icon>
</v-btn>
<v-toolbar-title v-text="title" />
<v-spacer />
<v-btn
icon
@click.stop="rightDrawer = !rightDrawer"
>
<v-icon>mdi-menu</v-icon>
</v-btn>
</v-app-bar>
<v-content>
<v-container>
<nuxt />
</v-container>
</v-content>
<v-navigation-drawer
v-model="rightDrawer"
:right="right"
temporary
fixed
>
<v-list>
<v-list-item @click.native="right = !right">
<v-list-item-action>
<v-icon light>
mdi-repeat
</v-icon>
</v-list-item-action>
<v-list-item-title>Switch drawer (click me)</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-footer
:fixed="fixed"
app
>
<span>&copy; 2019</span>
</v-footer>
</v-app>
</template>
<script>
export default {
data () {
return {
clipped: false,
drawer: false,
fixed: false,
items: [
{
icon: 'mdi-apps',
title: 'Welcome',
to: '/'
},
{
icon: 'mdi-chart-bubble',
title: 'Inspire',
to: '/inspire'
}
],
miniVariant: false,
right: true,
rightDrawer: false,
title: 'Vuetify.js'
}
}
}
</script>

74
layouts/default.vue

@ -0,0 +1,74 @@
<template>
<v-app>
<v-app-bar
fixed
app
>
<v-toolbar-title v-text="title" />
<v-spacer />
<v-btn
icon
nuxt
to="/admin"
>
<v-icon>mdi-account</v-icon>
</v-btn>
</v-app-bar>
<v-content>
<v-container>
<nuxt />
</v-container>
</v-content>
<v-navigation-drawer
v-model="rightDrawer"
:right="right"
temporary
fixed
>
<v-list>
<v-list-item @click.native="right = !right">
<v-list-item-action>
<v-icon light>
mdi-repeat
</v-icon>
</v-list-item-action>
<v-list-item-title>Switch drawer (click me)</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-footer
:fixed="fixed"
app
>
<span>Nubium Artifex &copy; 2019</span>
</v-footer>
</v-app>
</template>
<script>
export default {
data () {
return {
clipped: false,
drawer: false,
fixed: false,
items: [
{
icon: 'mdi-apps',
title: 'Welcome',
to: '/'
},
{
icon: 'mdi-chart-bubble',
title: 'Inspire',
to: '/inspire'
}
],
miniVariant: false,
right: true,
rightDrawer: false,
title: 'Vuetify.js'
}
}
}
</script>

44
layouts/error.vue

@ -0,0 +1,44 @@
<template>
<v-app dark>
<h1 v-if="error.statusCode === 404">
{{ pageNotFound }}
</h1>
<h1 v-else>
{{ otherError }}
</h1>
<NuxtLink to="/">
Home page
</NuxtLink>
</v-app>
</template>
<script>
export default {
layout: 'empty',
props: {
error: {
type: Object,
default: null
}
},
head () {
const title =
this.error.statusCode === 404 ? this.pageNotFound : this.otherError
return {
title
}
},
data () {
return {
pageNotFound: '404 Not Found',
otherError: 'An error occurred'
}
}
}
</script>
<style scoped>
h1 {
font-size: 20px;
}
</style>

8
middleware/README.md

@ -0,0 +1,8 @@
# MIDDLEWARE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your application middleware.
Middleware let you define custom functions that can be run before rendering either a page or a group of pages.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).

6
middleware/renderer.js

@ -0,0 +1,6 @@
export default function ({ store }) {
if (store.state.ssr === process.server) {
return
}
store.commit('setRenderer', process.server)
}

92
nuxt.config.js

@ -0,0 +1,92 @@
import colors from 'vuetify/es5/util/colors'
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
titleTemplate: '%s - ' + process.env.npm_package_name,
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#f0f' },
/*
** Global CSS
*/
css: [
],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '~/plugins/vuetify-confirm', mode: 'client' }
],
/*
** Nuxt.js dev-modules
*/
devModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module',
'@nuxtjs/vuetify'
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
proxy: true // Can be also an object with default options,
// debug: true
},
proxy: {
'/cloud': { target: 'http://localhost:3009', pathRewrite: { '/cloud': '' } }
},
/*
** vuetify module configuration
** https://github.com/nuxt-community/vuetify-module
*/
vuetify: {
customVariables: ['~/assets/variables.scss'],
theme: {
dark: false,
themes: {
dark: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
}
}
}

11467
package-lock.json
File diff suppressed because it is too large
View File

29
package.json

@ -0,0 +1,29 @@
{
"name": "artifex",
"version": "1.0.0",
"description": "An artifex world for noob",
"author": "P.BARRY",
"private": true,
"scripts": {
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
},
"dependencies": {
"@nuxtjs/axios": "^5.3.6",
"nuxt": "^2.0.0",
"vuedraggable": "^2.23.0",
"vuetify-confirm": "^1.0.0-alpha.0"
},
"devDependencies": {
"@nuxtjs/vuetify": "^1.0.0",
"@nuxtjs/eslint-config": "^1.0.1",
"@nuxtjs/eslint-module": "^0.2.1",
"babel-eslint": "^10.0.1",
"eslint": "^5.15.1",
"eslint-plugin-nuxt": ">=0.4.2",
"nodemon": "^1.18.9"
}
}

6
pages/README.md

@ -0,0 +1,6 @@
# PAGES
This directory contains your Application Views and Routes.
The framework reads all the `*.vue` files inside this directory and creates the router of your application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).

40
pages/admin/index.vue

@ -0,0 +1,40 @@
<template>
<v-layout column justify-center align-center>
<v-flex xs12 sm8 md6>
<div class="text-center">
<img src="/assets/vuetify.png" alt="Vuetify.js" class="mb-5">
</div>
<v-card>
<v-card-title class="headline">
Welcome to the Vuetify + Nuxt.js template
</v-card-title>
<v-card-text>
<p>Vuetify is a progressive Material Design component framework for Vue.js. It was designed to empower developers to create amazing applications.</p>
<p>For more information on Vuetify, check out the <a href="https://vuetifyjs.com" target="_blank">documentation</a>.</p>
<p>If you have questions, please join the official <a href="https://chat.vuetifyjs.com/" target="_blank" title="chat">discord</a>.</p>
<p>Find a bug? Report it on the github <a href="https://github.com/vuetifyjs/vuetify/issues" target="_blank" title="contribute">issue board</a>.</p>
<p>Thank you for developing with Vuetify and I look forward to bringing more exciting features in the future.</p>
<div class="text-xs-right">
<em><small>&mdash; John Leider</small></em>
</div>
<hr class="my-3">
<a href="https://nuxtjs.org/" target="_blank">Nuxt Documentation</a>
<br>
<a href="https://github.com/nuxt/nuxt.js" target="_blank">Nuxt GitHub</a>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="primary" text nuxt to="/inspire">
Continue
</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
export default {
layout: 'admin'
}
</script>

33
pages/admin/landingpage/_id.vue

@ -0,0 +1,33 @@
<template>
<template-editor v-model="lp" />
</template>
<script>
import TemplateEditor from '@/components/TemplateEditor/TemplateEditor.vue'
export default {
layout: 'admin',
components: { TemplateEditor },
data: () => ({
doc: {},
lp: []
}),
mounted () {
this.$store.commit('admin/ui/setBreadcrumbs', [
{ text: 'Admin', to: '/admin' },
{ text: 'Landing Page', to: '/admin/landingpage' },
{ text: 'Edit', to: `/admin/landingpage/${this.$route.params.id}` }
])
this.getDoc()
},
methods: {
getDoc () {
this.$axios.get(`/cloud/api/landingpages/${this.$route.params.id}`)
.then((res) => {
this.doc = res.data.data
this.$store.commit('admin/ui/setTitle', this.doc.name)
})
}
}
}
</script>

94
pages/admin/landingpage/index.vue

@ -0,0 +1,94 @@
<template>
<v-container>
<v-layout>
<v-flex xs6>
<p class="caption font-weight-light font-italic">
Choisir la landing page à afficher sur la page d'accueil.
</p>
<v-select
v-model="currentLP"
:items="docs"
item-text="name"
item-value="_id"
label="Landing page"
solo
@change="updateCurrentLP()"
/>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs6>
<p class="caption font-weight-light font-italic">
Editer une landing page.
Pour ajouter une nouvelle landing page, passer par <b>Resources > Landing Page</b>
</p>
</v-flex>
</v-layout>
<v-list dense two-line class="elevation-2">
<template v-for="item in docs">
<v-list-item
:key="`item-${item.coll}`"
link
nuxt
:to="`/admin/landingpage/${item._id}`"
>
<v-list-item-content>
<v-list-item-title v-text="item.name" />
<v-list-item-subtitle v-text="item.description" />
</v-list-item-content>
<v-list-item-action />
</v-list-item>
<v-divider :key="`divider-${item.name}`" />
</template>
</v-list>
</v-container>
</template>
<script>
export default {
layout: 'admin',
data: () => ({
currentLP: '',
docs: [],
switch1: false
}),
mounted () {
this.$store.commit('admin/ui/setTitle', 'Landing Page')
this.$store.commit('admin/ui/setBreadcrumbs', [
{ text: 'Admin', to: '/admin' },
{ text: 'Landing Page', to: '/admin/landingpage' }
])
this.getDocs()
this.getCurrentLP()
},
methods: {
getDocs () {
this.$axios
.get('/cloud/api/landingpages?projection=name:1,description:1')
.then((res) => {
this.docs = res.data.data
})
},
getCurrentLP () {
this.$axios
.get('/cloud/api/config/home:landing-page')
.then((res) => {
try { this.currentLP = res.data.data.value } catch (e) {}
})
},
updateCurrentLP () {
this.$axios
.put('/cloud/api/config/home:landing-page', { value: this.currentLP })
.then((res) => {
this.$store.commit('admin/ui/snack', { msg: 'Enregistré avec succès', color: 'success' })
})
.then((res) => {
this.$store.commit('admin/ui/snack', { msg: 'Echec', color: 'error' })
})
}
}
}
</script>

99
pages/admin/resources/_rsc/_id.vue

@ -0,0 +1,99 @@
<template>
<v-container>
<v-tabs v-model="currentTab" centered grow>
<v-tab v-for="tab in jsonForm" :key="tab.label" :href="`#tab-${tab.label}`">
{{ tab.label }}
</v-tab>
</v-tabs>
<v-tabs-items v-model="currentTab" class="my-4">
<v-tab-item v-for="tab in jsonForm" :key="tab.label" :value="`tab-${tab.label}`">
<form-gen
v-if="doc"
:value="doc"
:more="tab.field"
:form="tab.form"
:coll="{type: $route.params.rsc, id: doc._id}"
:disabled="disabled"
@update="hasUpdate()"
/>
</v-tab-item>
</v-tabs-items>
<v-fab-transition>
<v-btn
v-show="canSave"
class="mx-2"
fab
dark
large
color="success ma-3"
absolute
bottom
right
@click="save()"
>
<v-icon dark>
mdi-content-save
</v-icon>
</v-btn>
</v-fab-transition>
</v-container>
</template>
<script>
import FormGen from '@/components/FormGen/FormGen'
export default {
layout: 'admin',
components: { FormGen },
data: () => ({
jsonForm: [],
currentTab: '',
doc: null,
canSave: false,
disabled: true
}),
mounted () {
this.$store.commit('admin/ui/setBreadcrumbs', [
{ text: 'Resources', href: '/admin/resources' },
{ text: this.$route.params.rsc, href: `/admin/resources/${this.$route.params.rsc}` },
{ text: 'Edit', href: `/admin/resources/${this.$route.params.rsc}/${this.$route.params.id}` }
])
this.getForm()
this.getDoc()
},
methods: {
hasUpdate () {
this.canSave = true
},
getForm () {
this.$axios.get(`/cloud/api/${this.$route.params.rsc}/private/json-form`)
.then((res) => {
this.jsonForm = res.data
this.jsonForm.forEach((tab) => {
if (!this.currentTab) { this.currentTab = `#tab-${tab.label}` }
})
this.disabled = false
})
},
getDoc () {
this.$axios.get(`/cloud/api/${this.$route.params.rsc}/${this.$route.params.id}`)
.then((res) => {
this.doc = res.data.data
this.$store.commit('admin/ui/setTitle', this.doc.name)
})
},
save () {
this.$axios.put(`/cloud/api/${this.$route.params.rsc}/${this.$route.params.id}`, this.doc)
.then((res) => {
this.canSave = false
this.$store.commit('admin/ui/snack', { msg: 'Enregistré avec succès', color: 'success' })
})
.catch((res) => {
this.$store.commit('admin/ui/snack', { msg: 'Impossible de sauvegarder', color: 'error' })
})
}
}
}
</script>

199
pages/admin/resources/_rsc/index.vue

@ -0,0 +1,199 @@
<template>
<v-container>
<v-toolbar flat color="white">
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Recherche"
single-line
hide-details
@change="searchDoc"
/>
<v-spacer />
<v-btn color="primary" @click="createDoc()">
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-toolbar>
<v-data-table
:headers="headers"
:items="docs"
:loading="loading"
:options.sync="options"
:server-items-length="nbDocs"
class="elevation-2"
>
<template v-slot:items="props">
<td>{{ props.item.name }}</td>
<td>
<v-icon small class="mr-2" @click="editDoc(props.item)">
edit
</v-icon>
<v-icon small @click="deleteDoc(props.item)">
delete
</v-icon>
</td>
</template>
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
@click="editDoc(item)"
>
mdi-pencil
</v-icon>
<v-icon
small
@click="deleteDoc(item)"
>
mdi-delete
</v-icon>
</template>
</v-data-table>
<v-dialog v-model="openCreateDialog" persistent max-width="600px">
<v-card>
<v-card-title>
<span class="headline">Create new item</span>
</v-card-title>
<v-card-text>
<v-container v-if="newItem.form" grid-list-md>
<form-gen
:value="newItem.doc"
:form="newItem.form"
:coll="{type: $route.params.rsc, id: null}"
@valid="newItemIsValid"
/>
<v-alert :value="newItem.hasError" type="error">
{{ newItem.hasError }}
</v-alert>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="blue darken-1" text @click="openCreateDialog = false">
Cancel
</v-btn>
<v-btn color="primary" text :disabled="!newItem.isValid" @click="newItemCreate">
Create
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
import FormGen from '@/components/FormGen/FormGen'
export default {
layout: 'admin',
components: { FormGen },
data: () => ({
docs: [],
jsonForm: null,
openCreateDialog: false,
loading: false,
nbDocs: 0,
options: {},
search: '',
newItem: {
doc: {},
form: null,
hasError: null,
isValid: false
},
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Actions', value: 'action', align: 'right', sortable: false }
]
}),
watch: {
options: {
handler () {
this.getDataFromApi()
},
deep: true
}
},
mounted () {
this.$store.commit('admin/ui/setTitle', 'Resources')
this.$store.commit('admin/ui/setBreadcrumbs', [
{ text: 'Admin', to: '/admin' },
{ text: 'Resources', to: '/admin/resources' },
{ text: this.$route.params.rsc, to: `/admin/resources/${this.$route.params.rsc}` }
])
this.getListingHeader()
},
methods: {
editDoc (item) {
this.$router.push({ path: `/admin/resources/${this.$route.params.rsc}/${item._id}` })
},
deleteDoc (item) {},
createDoc () {
this.getCreateForm()
},
searchDoc () {
this.getDataFromApi()
},
getCreateForm () {
this.$axios.get(`/cloud/api/${this.$route.params.rsc}/private/json-form-create`)
.then((res) => {
this.newItem.doc = {}
this.newItem.form = res.data
this.openCreateDialog = true
})
},
getListingHeader () {
this.$axios.get(`/cloud/api/${this.$route.params.rsc}/private/listing-header`)
.then((res) => {
this.headers = res.data
this.headers.push({ text: 'Actions', value: 'action', align: 'right', sortable: false })
})
},
newItemIsValid (pVal) {
this.newItem.isValid = pVal
},
newItemCreate () {
this.newItem.hasError = null
if (!this.newItem.isValid) { return }
this.$axios.post(`/cloud/api/${this.$route.params.rsc}`, this.newItem.doc)
.then((res) => {
this.$router.push({ path: `/admin/resources/${this.$route.params.rsc}/${res.data.doc._id}` })
})
.catch((res) => {
this.newItem.hasError = 'Impossible de créer le nouvel item'
})
},
newItemCancel () {
this.newItem.doc = {}
this.newItem.form = null
this.newItem.isValid = false
this.openCreateDialog = true
},
getDataFromApi () {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
this.loading = true
const queries = []
const sort = sortBy.length ? sortBy[0] : 'name'
const desc = sortDesc.length ? sortDesc[0] : true
const search = this.search
queries.push(`limit=${itemsPerPage}`)
queries.push(`skip=${itemsPerPage * (page - 1)}`)
queries.push(`sort=${desc ? '' : '-'}${sort}`)
if (search) {
queries.push(`name__regex=${search},i`)
}
this.$axios.get(`/cloud/api/${this.$route.params.rsc}?${queries.join('&')}`)
.then((res) => {
this.$store.commit('admin/ui/setTitle', res.data.label)
this.nbDocs = res.data.count
this.docs = res.data.data
this.loading = false
})
}
}
}
</script>

53
pages/admin/resources/index.vue

@ -0,0 +1,53 @@
<template>
<v-container>
<v-list two-line dense class="elevation-2">
<template v-for="item in collections">
<v-list-item
:key="`item-${item.coll}`"
link
nuxt
:to="`/admin/resources/${item.coll}`"
>
<v-list-item-avatar>
<v-icon
v-text="item.icon"
/>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.label" />
<v-list-item-subtitle v-text="item.description" />
</v-list-item-content>
<v-list-item-action />
</v-list-item>
<v-divider :key="`divider-${item.coll}`" />
</template>
</v-list>
</v-container>
</template>
<script>
export default {
layout: 'admin',
data: () => ({
collections: []
}),
mounted () {
this.$store.commit('admin/ui/setTitle', 'Resources')
this.$store.commit('admin/ui/setBreadcrumbs', [
{ text: 'Admin', to: '/admin' },
{ text: 'Resources', to: '/admin/resources' }
])
this.getCollections()
},
methods: {
getCollections () {
this.$axios.get('/cloud/api/__intern/collections')
.then((res) => {
this.collections = res.data
})
}
}
}
</script>

92
pages/index.ori.vue

@ -0,0 +1,92 @@
<template>
<v-layout
column
justify-center
align-center
>
<v-flex
xs12
sm8
md6
>
<div class="text-center">
<logo />
<vuetify-logo />
</div>
<v-card>
<v-card-title class="headline">
Welcome to the Vuetify + Nuxt.js template
</v-card-title>
<v-card-text>
<p>Vuetify is a progressive Material Design component framework for Vue.js. It was designed to empower developers to create amazing applications.</p>
<p>
For more information on Vuetify, check out the <a
href="https://vuetifyjs.com"
target="_blank"
>
documentation
</a>.
</p>
<p>
If you have questions, please join the official <a
href="https://chat.vuetifyjs.com/"
target="_blank"
title="chat"
>
discord
</a>.
</p>
<p>
Find a bug? Report it on the github <a
href="https://github.com/vuetifyjs/vuetify/issues"
target="_blank"
title="contribute"
>
issue board
</a>.
</p>
<p>Thank you for developing with Vuetify and I look forward to bringing more exciting features in the future.</p>
<div class="text-xs-right">
<em><small>&mdash; John Leider</small></em>
</div>
<hr class="my-3">
<a
href="https://nuxtjs.org/"
target="_blank"
>
Nuxt Documentation
</a>
<br>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
>
Nuxt GitHub
</a>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
nuxt
to="/inspire"
>
Continue
</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</template>
<script>
import Logo from '~/components/Logo.vue'
import VuetifyLogo from '~/components/VuetifyLogo.vue'
export default {
components: {
Logo,
VuetifyLogo
}
}
</script>

217
pages/index.vue

@ -0,0 +1,217 @@
<template>
<v-content>
<section>
<v-parallax src="assets/hero.jpeg" height="600">
<v-layout
column
align-center
justify-center
class="white--text"
>
<img src="assets/vuetify.png" alt="Vuetify.js" height="200">
<h1 class="white--text mb-2 display-1 text-center">
Parallax Template
</h1>
<div class="subheading mb-4 text-center">
Powered by Vuetify
</div>
<v-btn
class="mt-12"
color="blue lighten-2"
dark
large
href="/pre-made-themes"
>
Get Started
</v-btn>
</v-layout>
</v-parallax>
</section>
<section>
<v-layout
column
wrap
class="my-12"
align-center
>
<v-flex xs12 sm4 class="my-4">
<div class="text-center">
<h2 class="headline">
The best way to start developing
</h2>
<span class="subheading">
Cras facilisis mi vitae nunc
</span>
</div>
</v-flex>
<v-flex xs12>
<v-container grid-list-xl>
<v-layout row wrap align-center>
<v-flex xs12 md4>
<v-card flat class="transparent">
<v-card-text class="text-center">
<v-icon x-large class="blue--text text--lighten-2">
mdi-palette
</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline text-center">
Material Design
</div>
</v-card-title>
<v-card-text>
Cras facilisis mi vitae nunc lobortis pharetra. Nulla volutpat tincidunt ornare.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Nullam in aliquet odio. Aliquam eu est vitae tellus bibendum tincidunt. Suspendisse potenti.
</v-card-text>
</v-card>
</v-flex>
<v-flex xs12 md4>
<v-card flat class="transparent">
<v-card-text class="text-center">
<v-icon x-large class="blue--text text--lighten-2">
mdi-flash
</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline">
Fast development
</div>
</v-card-title>
<v-card-text>
Cras facilisis mi vitae nunc lobortis pharetra. Nulla volutpat tincidunt ornare.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Nullam in aliquet odio. Aliquam eu est vitae tellus bibendum tincidunt. Suspendisse potenti.
</v-card-text>
</v-card>
</v-flex>
<v-flex xs12 md4>
<v-card flat class="transparent">
<v-card-text class="text-center">
<v-icon x-large class="blue--text text--lighten-2">
mdi-wrench
</v-icon>
</v-card-text>
<v-card-title primary-title class="layout justify-center">
<div class="headline text-center">
Completely Open Sourced
</div>
</v-card-title>
<v-card-text>
Cras facilisis mi vitae nunc lobortis pharetra. Nulla volutpat tincidunt ornare.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Nullam in aliquet odio. Aliquam eu est vitae tellus bibendum tincidunt. Suspendisse potenti.
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</v-layout>
</section>
<section>
<v-parallax src="assets/section.jpg" height="380">
<v-layout column align-center justify-center>
<div class="headline white--text mb-4 text-center">
Web development has never been easier
</div>
<em>Kick-start your application today</em>
<v-btn
class="mt-12"
color="blue lighten-2"
dark
large
href="/pre-made-themes"
>
Get Started
</v-btn>
</v-layout>
</v-parallax>
</section>
<section>
<v-container grid-list-xl>
<v-layout row wrap justify-center class="my-12">
<v-flex xs12 sm4>
<v-card flat class="transparent">
<v-card-title primary-title class="layout justify-center">
<div class="headline">
Company info
</div>
</v-card-title>
<v-card-text>
Cras facilisis mi vitae nunc lobortis pharetra. Nulla volutpat tincidunt ornare.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
Nullam in aliquet odio. Aliquam eu est vitae tellus bibendum tincidunt. Suspendisse potenti.
</v-card-text>
</v-card>
</v-flex>
<v-flex xs12 sm4 offset-sm1>
<v-card flat class="transparent">
<v-card-title primary-title class="layout justify-center">
<div class="headline">
Contact us
</div>
</v-card-title>
<v-card-text>
Cras facilisis mi vitae nunc lobortis pharetra. Nulla volutpat tincidunt ornare.
</v-card-text>
<v-list class="transparent">
<v-list-item>
<v-list-item-action>
<v-icon class="blue--text text--lighten-2">
mdi-phone
</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>777-867-5309</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item>
<v-list-item-action>
<v-icon class="blue--text text--lighten-2">
mdi-map-marker
</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Chicago, US</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item>
<v-list-item-action>
<v-icon class="blue--text text--lighten-2">
mdi-email
</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>john@vuetifyjs.com</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card>
</v-flex>
</v-layout>
</v-container>
</section>
</v-content>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: mapState({
sidebar: state => state.ui.sidebar
}),
mounted () {
console.log(this.$store)
},
methods: {
toggle () {
this.$store.commit('ui/toggleSidebar')
}
}
}
</script>

19
pages/inspire.vue

@ -0,0 +1,19 @@
<template>
<v-layout>
<v-flex class="text-center">
<img
src="/v.png"
alt="Vuetify.js"
class="mb-5"
>
<blockquote class="blockquote">
&#8220;First, solve the problem. Then, write the code.&#8221;
<footer>
<small>
<em>&mdash;John Johnson</em>
</small>
</footer>
</blockquote>
</v-flex>
</v-layout>
</template>

7
plugins/README.md

@ -0,0 +1,7 @@
# PLUGINS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).

90
plugins/vuetify-confirm.component.vue

@ -0,0 +1,90 @@
<template>
<v-dialog v-model="value" :max-width="width" :persistent="persistent" @input="change" @keydown.esc="choose(false)">
<v-card>
<v-card-title class="headline">
Use Google's location service?
</v-card-title>
<v-card-text>Let Google help apps determine location. This means sending anonymous location data to Google, even when no apps are running.</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn color="green darken-1" text @click="choose(false)">
Disagree
</v-btn>
<v-btn color="green darken-1" text @click="choose(false)">
Agree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
// components: {
// VCard,
// VCardActions,
// VCardText,
// VDialog,
// VIcon,
// VToolbar,
// VToolbarTitle,
// VSpacer,
// VBtn
// },
props: {
buttonTrueText: {
type: String,
default: 'Yes'
},
buttonFalseText: {
type: String,
default: 'No'
},
buttonTrueColor: {
type: String,
default: 'primary'
},
buttonFalseColor: {
type: String,
default: 'grey'
},
color: {
type: String,
default: 'warning'
},
icon: {
type: String,
default: 'mdi-information'
},
message: {
type: String,
required: true
},
persistent: Boolean,
title: {
type: String,
default: ''
},
width: {
type: Number,
default: 350
}
},
data () {
return {
value: true
}
},
methods: {
choose (value) {
this.$emit('result', value)
this.value = value
this.$destroy()
},
change (res) {
this.$destroy()
}
}
}
</script>

19
plugins/vuetify-confirm.js

@ -0,0 +1,19 @@
// import Vue from 'vue'
// import VuetifyConfirm from './vuetify-confirm.plugin'
// Vue.prototype.$confirm =
// Vue.use(VuetifyConfirm)
import Vue from 'vue'
import VuetifyConfirm from 'vuetify-confirm'
export default ({ app }) => {
Vue.use(VuetifyConfirm, {
vuetify: app.vuetify,
buttonTrueText: 'Confirmer',
buttonFalseText: 'Annuler',
color: 'warning',
icon: 'mdi-information',
title: 'Attention'
})
}

28
plugins/vuetify-confirm.plugin.js

@ -0,0 +1,28 @@
import Confirm from './vuetify-confirm.component'
function install (Vue, options) {
const property = (options && options.property) || '$confirm'
function createDialogCmp (options) {
return new Promise((resolve) => {
const cmp = new Vue(Object.assign({}, Confirm, {
propsData: Object.assign({}, Vue.prototype.$confirm.options, options),
destroyed: (c) => {
document.body.removeChild(cmp.$el)
resolve(cmp.value)
}
}))
console.log(cmp.$mount())
// document.body.appendChild(cmp.$mount().$el)
})
}
function show (message, options = {}) {
options.message = message
return createDialogCmp(options)
}
Vue.prototype[property] = show
Vue.prototype[property].options = options || {}
}
export default { install }

11
static/README.md

@ -0,0 +1,11 @@
# STATIC
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your static files.
Each file inside this directory is mapped to `/`.
Thus you'd want to delete this README.md before deploying to production.
Example: `/static/robots.txt` is mapped as `/robots.txt`.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).

BIN
static/assets/hero.jpeg

Before After
Width: 2200  |  Height: 1457  |  Size: 650 KiB

BIN
static/assets/section.jpg

Before After
Width: 1920  |  Height: 1276  |  Size: 718 KiB

BIN
static/assets/vuetify.png

Before After
Width: 197  |  Height: 225  |  Size: 2.4 KiB

BIN
static/favicon.ico

Before After

BIN
static/v.png

Before After
Width: 120  |  Height: 120  |  Size: 5.5 KiB

10
store/README.md

@ -0,0 +1,10 @@
# STORE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Vuex Store files.
Vuex Store option is implemented in the Nuxt.js framework.
Creating a file in this directory automatically activates the option in the framework.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).

5
store/admin/landingpage.js

@ -0,0 +1,5 @@
export const menu = { icon: 'mdi-home', title: 'Landing Page', to: '/admin/landingpage' }
export const state = () => ({
list: ['users']
})

5
store/admin/resources.js

@ -0,0 +1,5 @@
export const menu = { icon: 'mdi-database', title: 'Resources', to: '/admin/resources' }
export const state = () => ({
list: ['users']
})

43
store/admin/ui.js

@ -0,0 +1,43 @@
export const state = () => ({
breadcrumbs: [],
title: '---',
menu: [
{ icon: 'mdi-apps', title: 'Welcome', to: '/admin' },
require('./landingpage').menu,
require('./resources').menu
],
snackbar: {
timeout: 6000,
color: 'error',
showIt: false,
msg: ''
}
})
export const mutations = {
addItem (state, item) {
state.items.push(item)
},
setTitle (state, data) {
state.title = data
},
setBreadcrumbs (state, data) {
data.forEach((item) => {
item.replace = true
item.nuxt = true
})
data[data.length - 1].disabled = true
state.breadcrumbs = data
},
snack (state, data) {
state.snackbar.showIt = true
state.snackbar.msg = data.msg
state.snackbar.color = data.color || ''
},
snackClose (state, data) {
state.snackbar.showIt = false
}
}
export const actions = {}

13
store/index.js

@ -0,0 +1,13 @@
export const state = () => ({
ssr: null,
sidebar: false
})
export const mutations = {
setRenderer (state, value) {
state.ssr = value
},
toggleSidebar (state) {
state.sidebar = !state.sidebar
}
}

9
store/ui/index.js

@ -0,0 +1,9 @@
export const state = () => ({
sidebar: false
})
export const mutations = {
toggleSidebar (state) {
state.sidebar = !state.sidebar
}
}
Loading…
Cancel
Save