mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
WIP.
This commit is contained in:
@@ -16,3 +16,8 @@ modoboa_test
|
||||
.coverage
|
||||
build/
|
||||
dist/
|
||||
|
||||
# JS stuff
|
||||
node_modules
|
||||
/static/dist
|
||||
/frontend/webpack-stats.json
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
||||
}
|
||||
}],
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": ["transform-vue-jsx", "transform-runtime"],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": ["env", "stage-2"],
|
||||
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,5 @@
|
||||
/build/
|
||||
/config/
|
||||
/dist/
|
||||
/*.js
|
||||
/test/unit/coverage/
|
||||
@@ -0,0 +1,30 @@
|
||||
// https://eslint.org/docs/user-guide/configuring
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
extends: [
|
||||
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
|
||||
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
|
||||
'plugin:vue/essential',
|
||||
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
|
||||
'standard'
|
||||
],
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'vue'
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
// allow async-await
|
||||
'generator-star-spacing': 'off',
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'indent': ['error', 4]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
/dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
/test/unit/coverage/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
@@ -0,0 +1,10 @@
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
"postcss-import": {},
|
||||
"postcss-url": {},
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# frontend
|
||||
|
||||
> Vue.js frontend for Modoboa
|
||||
|
||||
## Build Setup
|
||||
|
||||
``` bash
|
||||
# install dependencies
|
||||
npm install
|
||||
|
||||
# serve with hot reload at localhost:8080
|
||||
npm run dev
|
||||
|
||||
# build for production with minification
|
||||
npm run build
|
||||
|
||||
# build for production and view the bundle analyzer report
|
||||
npm run build --report
|
||||
|
||||
# run unit tests
|
||||
npm run unit
|
||||
|
||||
# run all tests
|
||||
npm test
|
||||
```
|
||||
|
||||
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
|
||||
@@ -0,0 +1,7 @@
|
||||
'use strict'
|
||||
const merge = require('webpack-merge')
|
||||
const prodEnv = require('./prod.env')
|
||||
|
||||
module.exports = merge(prodEnv, {
|
||||
NODE_ENV: '"development"'
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
'use strict'
|
||||
// Template version: 1.3.1
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
dev: {
|
||||
|
||||
// Paths
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: 'http://localhost:8080/',
|
||||
proxyTable: {},
|
||||
|
||||
// Various Dev Server settings
|
||||
host: 'localhost', // can be overwritten by process.env.HOST
|
||||
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||
autoOpenBrowser: false,
|
||||
errorOverlay: true,
|
||||
notifyOnErrors: true,
|
||||
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
||||
|
||||
// Use Eslint Loader?
|
||||
// If true, your code will be linted during bundling and
|
||||
// linting errors and warnings will be shown in the console.
|
||||
useEslint: true,
|
||||
// If true, eslint errors and warnings will also be shown in the error overlay
|
||||
// in the browser.
|
||||
showEslintErrorsInOverlay: false,
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
|
||||
// If you have problems debugging vue-files in devtools,
|
||||
// set this to false - it *may* help
|
||||
// https://vue-loader.vuejs.org/en/options.html#cachebusting
|
||||
cacheBusting: true,
|
||||
|
||||
cssSourceMap: true
|
||||
},
|
||||
|
||||
build: {
|
||||
// Template for index.html
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
|
||||
// Paths
|
||||
assetsRoot: path.resolve(__dirname, '../../modoboa/static'),
|
||||
assetsSubDirectory: 'admin',
|
||||
assetsPublicPath: '/sitestatic/',
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
|
||||
productionSourceMap: true,
|
||||
// https://webpack.js.org/configuration/devtool/#production
|
||||
devtool: '#source-map',
|
||||
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
'use strict'
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
'use strict'
|
||||
const merge = require('webpack-merge')
|
||||
const devEnv = require('./dev.env')
|
||||
|
||||
module.exports = merge(devEnv, {
|
||||
NODE_ENV: '"testing"'
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>frontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "Vue.js frontend for Modoboa",
|
||||
"author": "Antoine Nguyen <tonio@ngyn.org>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
|
||||
"start": "npm run dev",
|
||||
"unit": "jest --config test/unit/jest.conf.js --coverage",
|
||||
"test": "npm run unit",
|
||||
"lint": "eslint --ext .js,.vue src test/unit",
|
||||
"build": "node build/build.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"buefy": "^0.6.5",
|
||||
"vue": "^2.5.2",
|
||||
"vue-resource": "^1.5.0",
|
||||
"vue-router": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.2",
|
||||
"babel-core": "^6.22.1",
|
||||
"babel-eslint": "^8.2.1",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
||||
"babel-jest": "^21.0.2",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^1.2.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||
"babel-plugin-transform-runtime": "^6.22.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.5.0",
|
||||
"babel-preset-env": "^1.3.2",
|
||||
"babel-preset-stage-2": "^6.22.0",
|
||||
"chalk": "^2.0.1",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"css-loader": "^0.28.0",
|
||||
"eslint": "^4.15.0",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-friendly-formatter": "^3.0.0",
|
||||
"eslint-loader": "^1.7.1",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-node": "^5.2.0",
|
||||
"eslint-plugin-promise": "^3.4.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"eslint-plugin-vue": "^4.0.0",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^1.1.4",
|
||||
"friendly-errors-webpack-plugin": "^1.6.1",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"jest": "^22.0.4",
|
||||
"jest-serializer-vue": "^0.3.0",
|
||||
"node-notifier": "^5.1.2",
|
||||
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
||||
"ora": "^1.2.0",
|
||||
"portfinder": "^1.0.13",
|
||||
"postcss-import": "^11.0.0",
|
||||
"postcss-loader": "^2.0.8",
|
||||
"postcss-url": "^7.2.1",
|
||||
"rimraf": "^2.6.0",
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"uglifyjs-webpack-plugin": "^1.1.1",
|
||||
"url-loader": "^0.5.8",
|
||||
"vue-jest": "^1.0.2",
|
||||
"vue-loader": "^13.3.0",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.5.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
"webpack-bundle-tracker": "^0.3.0",
|
||||
"webpack-dev-server": "^2.9.1",
|
||||
"webpack-merge": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div class="container is-fluid">
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">
|
||||
Admin
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<router-link tag="li" :to="{ name: 'DomainList' }">
|
||||
<a>Domains</a>
|
||||
</router-link>
|
||||
<router-link tag="li" :to="{ name: 'DomainList' }">
|
||||
<a>Identities</a>
|
||||
</router-link>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
<div class="column is-9">
|
||||
<router-view/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,14 @@
|
||||
import Vue from 'vue'
|
||||
import VueResource from 'vue-resource'
|
||||
|
||||
Vue.use(VueResource)
|
||||
|
||||
var domainResource = Vue.resource('/api/v1/domains{/pk}/')
|
||||
|
||||
export const getDomains = () => {
|
||||
return domainResource.get()
|
||||
}
|
||||
|
||||
export const createDomain = (data) => {
|
||||
return domainResource.save(data)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="title">Add domain</h1>
|
||||
<form>
|
||||
<b-field label="Name">
|
||||
<b-input v-model="domain.name"></b-input>
|
||||
</b-field>
|
||||
<b-field label="Quota">
|
||||
<b-input v-model="domain.quota"></b-input>
|
||||
</b-field>
|
||||
<b-field>
|
||||
<button class="button is-primary" @click="saveDomain">Save</button>
|
||||
</b-field>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as api from '@/api'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
domain: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveDomain () {
|
||||
api.createDomain(this.domain).then(response => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h1 class="title">Domains</h1>
|
||||
</div>
|
||||
<div class="column">
|
||||
<router-link :to="{ name: 'DomainAdd' }">Add</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<b-table :data="domains" checkable>
|
||||
<template slot-scope="props">
|
||||
<b-table-column field="name" label="Name">
|
||||
{{ props.row.name }}
|
||||
</b-table-column>
|
||||
<b-table-column field="quota" label="Quota">
|
||||
<progress class="progress is-primary">30%</progress>
|
||||
</b-table-column>
|
||||
</template>
|
||||
</b-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as api from '@/api'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
domains: [],
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
label: 'Name'
|
||||
},
|
||||
{
|
||||
field: 'quota',
|
||||
label: 'Quota'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api.getDomains().then(response => {
|
||||
this.domains = response.data
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
import Vue from 'vue'
|
||||
import Buefy from 'buefy'
|
||||
import 'buefy/lib/buefy.css'
|
||||
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
|
||||
Vue.use(Buefy)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
components: { App },
|
||||
template: '<App/>'
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import DomainList from '@/components/DomainList'
|
||||
import DomainForm from '@/components/DomainForm'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/domains/',
|
||||
name: 'DomainList',
|
||||
component: DomainList
|
||||
},
|
||||
{
|
||||
path: '/domains/add/',
|
||||
name: 'DomainAdd',
|
||||
component: DomainForm
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"globals": {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
rootDir: path.resolve(__dirname, '../../'),
|
||||
moduleFileExtensions: [
|
||||
'js',
|
||||
'json',
|
||||
'vue'
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1'
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
|
||||
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
|
||||
},
|
||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
||||
setupFiles: ['<rootDir>/test/unit/setup'],
|
||||
mapCoverage: true,
|
||||
coverageDirectory: '<rootDir>/test/unit/coverage',
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.{js,vue}',
|
||||
'!src/main.js',
|
||||
'!src/router/index.js',
|
||||
'!**/node_modules/**'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
@@ -1,29 +1,24 @@
|
||||
{% extends "fluid.html" %}
|
||||
|
||||
{% load i18n lib_tags static %}
|
||||
{% load render_bundle from webpack_loader %}
|
||||
|
||||
{% block pagetitle %}{% trans "Admin" %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% if not debug %}
|
||||
{% render_bundle 'app' 'css' 'ADMIN' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% if not debug %}
|
||||
{% render_bundle 'manifest' 'js' 'ADMIN' %}
|
||||
{% render_bundle 'vendor' 'js' 'ADMIN' %}
|
||||
{% render_bundle 'app' 'js' 'ADMIN' %}
|
||||
{% else %}
|
||||
{% render_bundle 'app' 'js' 'ADMIN' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block container_content %}
|
||||
|
||||
<div id="app">
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Modoboa</title>
|
||||
{% if not debug %}
|
||||
{% render_bundle 'app' 'css' 'ADMIN' %}
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
{% if not debug %}
|
||||
{% render_bundle 'manifest' 'js' 'ADMIN' %}
|
||||
{% render_bundle 'vendor' 'js' 'ADMIN' %}
|
||||
{% render_bundle 'app' 'js' 'ADMIN' %}
|
||||
{% else %}
|
||||
{% render_bundle 'app' 'js' 'ADMIN' %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,11 +9,13 @@ from django.conf.urls import url
|
||||
from . import views
|
||||
from .views import (
|
||||
alias as alias_views, domain as domain_views, export as export_views,
|
||||
identity as identity_views, import_ as import_views
|
||||
identity as identity_views, import_ as import_views,
|
||||
base as base_views
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', domain_views.index, name="index"),
|
||||
# url(r'^$', domain_views.index, name="index"),
|
||||
url(r'^$', base_views.AdminIndexView.as_view(), name="index"),
|
||||
url(r'^domains/$', domain_views.domains, name="domain_list"),
|
||||
url(r'^domains/(?P<pk>\d+)/$', views.DomainDetailView.as_view(),
|
||||
name="domain_detail"),
|
||||
|
||||
@@ -8,6 +8,7 @@ from .alias import AliasDetailView
|
||||
from .dns import DNSBLDomainDetailView, MXDomainDetailView
|
||||
from .domain import DomainDetailView
|
||||
from .identity import AccountDetailView
|
||||
from .base import AdminIndexView
|
||||
|
||||
__all__ = [
|
||||
"AccountDetailView",
|
||||
@@ -15,4 +16,5 @@ __all__ = [
|
||||
"DNSBLDomainDetailView",
|
||||
"DomainDetailView",
|
||||
"MXDomainDetailView",
|
||||
"AdminIndexView",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Admin views."""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.views import generic
|
||||
|
||||
|
||||
class AdminIndexView(generic.TemplateView):
|
||||
"""Admin index view."""
|
||||
|
||||
template_name = "admin/index.html"
|
||||
Reference in New Issue
Block a user