mirror of
https://github.com/HackPlan/RootPanel.git
synced 2026-01-12 22:27:09 +08:00
Use react in admin dashboard
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
_ = require 'lodash'
|
||||
Q = require 'q'
|
||||
|
||||
{Component} = root
|
||||
{Account, Component} = root
|
||||
|
||||
###
|
||||
Class: Billing Plan, Managed by {BillingManager}.
|
||||
@@ -63,6 +63,9 @@ class BillingPlan
|
||||
@state(account).removePlan().then =>
|
||||
@manager.destroyOverflowedComponents account
|
||||
|
||||
membersInPlan: ->
|
||||
Account.find _.zipObject ["plans.#{@name}"], [$exists: true]
|
||||
|
||||
setupDefaultComponents: (account) ->
|
||||
Q.all _.values(@components).map ({type, defaults}) ->
|
||||
if defaults
|
||||
|
||||
@@ -11,12 +11,6 @@ $ ->
|
||||
, ->
|
||||
location.reload()
|
||||
|
||||
$('.action-details').click ->
|
||||
request "/admin/account_details?account_id=#{$(@).parents('tr').data 'id'}", {}, {method: 'get'}, (account) ->
|
||||
$('.account-details-modal .label-account-id').text account._id
|
||||
$('.account-details-modal .label-details').html JSON.stringify account, null, ' '
|
||||
$('.account-details-modal').modal 'show'
|
||||
|
||||
$('.confirm-payment-modal .action-confirm-payment').click ->
|
||||
request '/admin/confirm_payment',
|
||||
account_id: $('.input-account-id').text()
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tab-pane .row > header {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ fs = require 'q-io/fs'
|
||||
_ = require 'lodash'
|
||||
Q = require 'q'
|
||||
|
||||
require('node-jsx').install harmony: true
|
||||
|
||||
###
|
||||
Class: Root object for control RootPanel, An instance is always available as the `root` global.
|
||||
###
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{Router} = require 'express'
|
||||
React = require 'react'
|
||||
_ = require 'lodash'
|
||||
Q = require 'q'
|
||||
|
||||
@@ -25,11 +26,42 @@ router.get '/dashboard', (req, res, next) ->
|
||||
limit: 10
|
||||
closed:
|
||||
limit: 10
|
||||
]).done ([accounts, components, tickets]) ->
|
||||
res.render 'admin',
|
||||
Q.all root.billing.all().map (plan) ->
|
||||
plan.membersInPlan().then (accounts) ->
|
||||
return {
|
||||
name: plan.name
|
||||
count: accounts.length
|
||||
}
|
||||
.then (result) ->
|
||||
return _.mapValues _.indexBy(result, 'name'), 'count'
|
||||
]).done ([accounts, components, tickets, accountsInPlan]) ->
|
||||
props =
|
||||
accounts: accounts
|
||||
components: components
|
||||
tickets: tickets
|
||||
package: root.package
|
||||
plans: root.billing.all().map (plan) ->
|
||||
plan = _.extend {}, plan,
|
||||
users: accountsInPlan[plan.name]
|
||||
return _.pick plan, 'components', 'join_freely', 'billing', 'name', 'users'
|
||||
plugins: root.plugins.all().map (plugin) ->
|
||||
return _.extend {}, _.pick(plugin, 'dependencies', 'name'),
|
||||
registered: do ->
|
||||
{routers, hooks, views, widgets, components, couponTypes, paymentProviders} = root.plugins.getRegisteredExtends plugin
|
||||
|
||||
return {
|
||||
routers: _.pluck routers, 'path'
|
||||
hooks: _.pluck hooks, 'path'
|
||||
views: _.pluck views, 'view'
|
||||
widgets: _.pluck widgets, 'view'
|
||||
components: _.pluck components, 'name'
|
||||
couponTypes: _.pluck couponTypes, 'name'
|
||||
paymentProviders: _.pluck paymentProviders, 'name'
|
||||
}
|
||||
|
||||
res.render 'admin/layout',
|
||||
mainBlock: React.renderToString React.createElement require('../view/admin/dashboard.jsx'), props
|
||||
initializeProps: props
|
||||
, next
|
||||
|
||||
###
|
||||
@@ -46,7 +78,7 @@ router.use '/users', do (router = new Router) ->
|
||||
router.param 'id', (req, res, next, user_id) ->
|
||||
Account.findById(user_id).then (user) ->
|
||||
if req.user = user
|
||||
next
|
||||
next()
|
||||
else
|
||||
next new Error 'user not found'
|
||||
.catch next
|
||||
@@ -66,19 +98,29 @@ router.use '/users', do (router = new Router) ->
|
||||
res.json req.user.pick 'admin'
|
||||
|
||||
###
|
||||
Router: GET /admin/users/:id/plans/join
|
||||
Router: POST /admin/users/:id/plans/join
|
||||
|
||||
Request {Object}
|
||||
|
||||
* `plan` {String}
|
||||
|
||||
###
|
||||
router.post '/:id/plans/join', (req, res, next) ->
|
||||
req.plan.addMember(req.account).done ->
|
||||
res.sendStatus 204
|
||||
root.billing.byName(req.body.plan).addMember(req.user).done ->
|
||||
res.json req.user
|
||||
, next
|
||||
|
||||
###
|
||||
Router: GET /admin/users/:id/plans/leave
|
||||
Router: POST /admin/users/:id/plans/leave
|
||||
|
||||
Request {Object}
|
||||
|
||||
* `plan` {String}
|
||||
|
||||
###
|
||||
router.post '/:id/plans/leave', (req, res, next) ->
|
||||
req.plan.removeMember(req.account).done ->
|
||||
res.sendStatus 204
|
||||
root.billing.byName(req.body.plan).removeMember(req.user).done ->
|
||||
res.json req.user
|
||||
, next
|
||||
|
||||
###
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
table.table.table-hover
|
||||
thead
|
||||
tr
|
||||
th= t('account.username')
|
||||
th= t('account.email')
|
||||
th= t('plan.')
|
||||
th= t('common.amount')
|
||||
th= t('common.actions')
|
||||
tbody
|
||||
for account in accounts
|
||||
tr(data-id='#{account._id}')
|
||||
td= account.username
|
||||
td= account.email
|
||||
td= _.keys(account.plans).join(', ')
|
||||
td= account.balance.toFixed(2)
|
||||
td
|
||||
button.btn.btn-info.btn-sm.action-details(type='button')= t('common.details')
|
||||
.btn-group
|
||||
button(type='button', data-toggle='dropdown').btn.btn-warning.btn-sm.dropdown-toggle
|
||||
| 计划
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(href='#') 加入套餐 A
|
||||
li
|
||||
a(href='#') 离开套餐 B
|
||||
.btn-group
|
||||
button(type='button', data-toggle='dropdown').btn.btn-primary.btn-sm.dropdown-toggle
|
||||
| #{t('common.actions')}
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a.action-confirm-payment(href='#')= t('view.admin.confirm_payment')
|
||||
if account.balance <= 0 && !_.isEmpty(account.plans)
|
||||
li
|
||||
a.action-delete-account(href='#')= t('view.admin.delete_account')
|
||||
119
core/view/admin/accounts.jsx
Normal file
119
core/view/admin/accounts.jsx
Normal file
@@ -0,0 +1,119 @@
|
||||
var React = require('react');
|
||||
var {Table, Button, DropdownButton, MenuItem, Modal} = require('react-bootstrap');
|
||||
var _ = require('lodash');
|
||||
var agent = require('../scripts/agent.coffee');
|
||||
var Cookies = require('js-cookie');
|
||||
var $ = require('jquery');
|
||||
|
||||
module.exports = AdminAccounts = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
accounts: this.props.accounts
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
$.ajaxSetup({
|
||||
headers: {'X-Token': Cookies.get('token')}
|
||||
});
|
||||
},
|
||||
|
||||
showAccountDetails: function(account_id) {
|
||||
this.setState({
|
||||
detailsModal: account_id
|
||||
});
|
||||
},
|
||||
|
||||
closeAccountDetails: function() {
|
||||
this.setState({
|
||||
detailsModal: null
|
||||
});
|
||||
},
|
||||
|
||||
joinPlan: function(account_id, plan_name) {
|
||||
agent.post(`/admin/users/${account_id}/plans/join`, {
|
||||
plan: plan_name
|
||||
}).then( account => {
|
||||
this.updateAccount(account);
|
||||
});
|
||||
},
|
||||
|
||||
leavePlan: function(account_id, plan_name) {
|
||||
agent.post(`/admin/users/${account_id}/plans/leave`, {
|
||||
plan: plan_name
|
||||
}).then( account => {
|
||||
this.updateAccount(account);
|
||||
});
|
||||
},
|
||||
|
||||
updateAccount: function(account) {
|
||||
this.setState({
|
||||
accounts: this.state.accounts.map(function(originalAccount) {
|
||||
if (originalAccount._id == account._id) {
|
||||
return account;
|
||||
} else {
|
||||
return originalAccount;
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<Table hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户名</th>
|
||||
<th>邮箱</th>
|
||||
<th>付费计划</th>
|
||||
<th>余额</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.accounts.map( account => {
|
||||
return (
|
||||
<tr key={account._id}>
|
||||
<td>{account.username}</td>
|
||||
<td>{account.email}</td>
|
||||
<td>{_.keys(account.plans).join()}</td>
|
||||
<td>{account.balance.toFixed(2)}</td>
|
||||
<td>
|
||||
<Button bsStyle='info' bsSize='small' onClick={this.showAccountDetails.bind(this, account._id)}>
|
||||
详情
|
||||
</Button>
|
||||
{this.state.detailsModal == account._id && (
|
||||
<Modal show={true} onHide={this.closeAccountDetails} bsSize='large'>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{account._id}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<pre>{JSON.stringify(account, null, ' ')}</pre>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={this.closeAccountDetails}>关闭</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)}
|
||||
<DropdownButton title='付费计划' bsStyle='warning' bsSize='small'>
|
||||
{this.props.plans.map( plan => {
|
||||
if (account.plans[plan.name]) {
|
||||
return <MenuItem key={plan.name} className='bg-danger' eventKey={plan.name} onSelect={this.leavePlan.bind(this, account._id)}>离开计划 {plan.name}</MenuItem>;
|
||||
} else {
|
||||
return <MenuItem key={plan.name} className='bg-success' eventKey={plan.name} onSelect={this.joinPlan.bind(this, account._id)}>加入计划 {plan.name}</MenuItem>;
|
||||
}
|
||||
})}
|
||||
</DropdownButton>
|
||||
<DropdownButton title='操作' bsStyle='primary' bsSize='small'>
|
||||
<MenuItem>确认充值</MenuItem>
|
||||
<MenuItem>删除账号</MenuItem>
|
||||
</DropdownButton>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
});
|
||||
17
core/view/admin/admin.coffee
Normal file
17
core/view/admin/admin.coffee
Normal file
@@ -0,0 +1,17 @@
|
||||
Backbone = require 'backbone'
|
||||
React = require 'react'
|
||||
|
||||
AdminDashboard = require './dashboard.jsx'
|
||||
|
||||
getInitializeProps = ->
|
||||
return JSON.parse $('#initialize-props').html()
|
||||
|
||||
AdminRouter = Backbone.Router.extend
|
||||
routes:
|
||||
'admin/dashboard': 'dashboard'
|
||||
|
||||
dashboard: ->
|
||||
React.render React.createElement(AdminDashboard, getInitializeProps()), document.querySelector('#main-block')
|
||||
|
||||
new AdminRouter()
|
||||
Backbone.history.loadUrl location.pathname
|
||||
0
core/view/admin/admin.less
Normal file
0
core/view/admin/admin.less
Normal file
@@ -1,4 +0,0 @@
|
||||
.page-header
|
||||
h1
|
||||
| RootPanel
|
||||
small= root.package.version
|
||||
26
core/view/admin/dashboard.jsx
Normal file
26
core/view/admin/dashboard.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
var React = require('react');
|
||||
var {TabbedArea, TabPane} = require('react-bootstrap');
|
||||
var AdminExtensions = require('./extensions.jsx');
|
||||
var AdminAccounts = require('./accounts.jsx');
|
||||
|
||||
module.exports = AdminDashboard = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<TabbedArea defaultActiveKey='dashboard'>
|
||||
<TabPane eventKey='dashboard' tab='仪表盘'>
|
||||
<h1>RootPanel <small>{this.props.package.version}</small></h1>
|
||||
</TabPane>
|
||||
<TabPane eventKey='extensions' tab='插件和拓展'>
|
||||
<AdminExtensions {...this.props} />
|
||||
</TabPane>
|
||||
<TabPane eventKey='accounts' tab='用户'>
|
||||
<AdminAccounts {...this.props} />
|
||||
</TabPane>
|
||||
<TabPane eventKey='tickets' tab='工单'></TabPane>
|
||||
<TabPane eventKey='coupons' tab='优惠和兑换'></TabPane>
|
||||
<TabPane eventKey='compontents' tab='元件'></TabPane>
|
||||
<TabPane eventKey='logs' tab='系统日志'></TabPane>
|
||||
</TabbedArea>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
.row
|
||||
header 付费方案
|
||||
|
||||
for plan in root.billing.all()
|
||||
.col-md-3
|
||||
.panel.panel-success
|
||||
.panel-heading
|
||||
strong= plan.name
|
||||
.panel-body
|
||||
p join_freely: #{plan.join_freely}
|
||||
p components: #{_.keys(plan.components).join()}
|
||||
p billing: #{_.keys(plan.billing).join()}
|
||||
p 用户数量:0
|
||||
|
||||
.row
|
||||
header 已加载的插件
|
||||
|
||||
for plugin in root.plugins.all()
|
||||
- registered = root.plugins.getRegisteredExtends(plugin)
|
||||
|
||||
.col-md-6
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
strong= plugin.name
|
||||
.panel-body
|
||||
p dependencies: #{_.keys(plugin.dependencies).join()}
|
||||
p routes: #{_.pluck(registered.routers, 'path').join()}
|
||||
p hooks: #{_.pluck(registered.hooks, 'path').join()}
|
||||
p views: #{_.pluck(registered.views, 'view').join()}
|
||||
p widgets: #{_.pluck(registered.widgets, 'view').join()}
|
||||
p components: #{_.pluck(registered.components, 'name').join()}
|
||||
p couponTypes: #{_.pluck(registered.couponTypes, 'name').join()}
|
||||
p paymentProviders: #{_.pluck(registered.paymentProviders, 'name').join()}
|
||||
48
core/view/admin/extensions.jsx
Normal file
48
core/view/admin/extensions.jsx
Normal file
@@ -0,0 +1,48 @@
|
||||
var {Row, Col, Panel} = require('react-bootstrap');
|
||||
var React = require('react');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = AdminExtensions = React.createClass({
|
||||
render: function() {
|
||||
var {plugins, plans} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<header>付费方案</header>
|
||||
{plans.map(function(plan) {
|
||||
return (
|
||||
<Col md={3} key={plan.name}>
|
||||
<Panel header={plan.name}>
|
||||
<p>join_freely: {plan.join_freely.toString()}</p>
|
||||
<p>components: {_.keys(plan.components).join()}</p>
|
||||
<p>billing: {_.keys(plan.billing).join()}</p>
|
||||
<p>users: {plan.users}</p>
|
||||
</Panel>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
<Row>
|
||||
<header>插件</header>
|
||||
{plugins.map(function(plugin) {
|
||||
return (
|
||||
<Col md={6} key={plugin.name}>
|
||||
<Panel header={plugin.name}>
|
||||
<p>dependencies: {_.keys(plugin.dependencies).join()}</p>
|
||||
<p>routes: {_.pluck(plugin.registered.routers, 'path').join()}</p>
|
||||
<p>hooks: {_.pluck(plugin.registered.hooks, 'path').join()}</p>
|
||||
<p>views: {_.pluck(plugin.registered.views, 'view').join()}</p>
|
||||
<p>widgets: {_.pluck(plugin.registered.widgets, 'view').join()}</p>
|
||||
<p>components: {_.pluck(plugin.registered.components, 'name').join()}</p>
|
||||
<p>couponTypes: {_.pluck(plugin.registered.couponTypes, 'name').join()}</p>
|
||||
<p>paymentProviders: {_.pluck(plugin.registered.paymentProviders, 'name').join()}</p>
|
||||
</Panel>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
15
core/view/admin/layout.jade
Normal file
15
core/view/admin/layout.jade
Normal file
@@ -0,0 +1,15 @@
|
||||
extends ../layout
|
||||
|
||||
prepend header
|
||||
title= helpers.title('admin.admin_panel')
|
||||
|
||||
block main
|
||||
!= mainBlock
|
||||
|
||||
prepend sidebar
|
||||
.row
|
||||
a.btn.btn-lg.btn-success(href='/admin/ticket/')= t('ticket.ticket_list')
|
||||
|
||||
append footer
|
||||
script(id='initialize-props', type='application/json')!= JSON.stringify(initializeProps)
|
||||
script(src='/public/admin.js')
|
||||
@@ -3,7 +3,7 @@ html
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
block header
|
||||
link(rel='stylesheet', href='/public/vendor/vendor.css')
|
||||
link(rel='stylesheet', href='/public/bootstrap.css')
|
||||
link(rel='stylesheet', href='/public/core.css')
|
||||
|
||||
body(data-username="#{account ? account.username : ''}")
|
||||
@@ -50,8 +50,9 @@ html
|
||||
block content
|
||||
#content.container
|
||||
.row
|
||||
.col-md-9
|
||||
#main-block.col-md-9
|
||||
block main
|
||||
!= mainBlock
|
||||
|
||||
#sidebar.col-md-3
|
||||
block sidebar
|
||||
|
||||
25
core/view/scripts/agent.coffee
Normal file
25
core/view/scripts/agent.coffee
Normal file
@@ -0,0 +1,25 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'lodash'
|
||||
|
||||
methods = ['get', 'post', 'delete', 'put', 'patch', 'head', 'options']
|
||||
|
||||
agent = {}
|
||||
|
||||
methods.forEach (method) ->
|
||||
agent[method] = (url, data = {}, options = {}) ->
|
||||
unless method == 'get'
|
||||
data = JSON.stringify data
|
||||
options.contentType = 'application/json; charset=UTF-8'
|
||||
|
||||
_.extend options,
|
||||
url: url
|
||||
data: data
|
||||
type: method.toUpperCase()
|
||||
|
||||
$.ajax(options).fail (jqXHR) ->
|
||||
if jqXHR.responseJSON?.error
|
||||
alert root.t jqXHR.responseJSON.error
|
||||
else
|
||||
alert jqXHR.statusText
|
||||
|
||||
module.exports = agent
|
||||
2
core/view/styles/bootstrap.less
vendored
Normal file
2
core/view/styles/bootstrap.less
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "less/bootstrap.less";
|
||||
@icon-font-path: "";
|
||||
@@ -12,17 +12,34 @@ uglify = require 'gulp-uglify'
|
||||
minifyCss = require 'gulp-minify-css'
|
||||
bowerFiles = require 'main-bower-files'
|
||||
runSequence = require 'run-sequence'
|
||||
browserify = require 'browserify'
|
||||
reactify = require 'reactify'
|
||||
source = require 'vinyl-source-stream'
|
||||
coffeeify = require 'coffeeify'
|
||||
|
||||
gulp.task 'clean', ->
|
||||
del 'public/*'
|
||||
|
||||
gulp.task 'vendor:styles', ->
|
||||
gulp.src bowerFiles()
|
||||
.pipe filter '*.less'
|
||||
.pipe less()
|
||||
.pipe concat 'vendor.css'
|
||||
gulp.task 'vendor:bootstrap:styles', ->
|
||||
gulp.src 'core/view/styles/bootstrap.less'
|
||||
.pipe less
|
||||
paths: ['bower_components/bootstrap']
|
||||
.pipe minifyCss()
|
||||
.pipe gulp.dest 'public/vendor'
|
||||
.pipe gulp.dest 'public'
|
||||
|
||||
gulp.task 'scripts:admin', ->
|
||||
browserify 'core/view/admin/admin.coffee'
|
||||
.transform coffeeify
|
||||
.transform reactify, es6: true
|
||||
.bundle()
|
||||
.pipe source 'admin.js'
|
||||
.pipe gulp.dest 'public'
|
||||
|
||||
gulp.task 'styles:admin', ->
|
||||
gulp.src 'core/view/admin/admin.less'
|
||||
.pipe less()
|
||||
.pipe minifyCss()
|
||||
.pipe gulp.dest 'public'
|
||||
|
||||
gulp.task 'vendor:scripts', ->
|
||||
gulp.src bowerFiles()
|
||||
@@ -35,16 +52,9 @@ gulp.task 'vendor:scripts', ->
|
||||
gulp.task 'vendor:fonts', ->
|
||||
gulp.src bowerFiles()
|
||||
.pipe filter ['*.eot', '*.svg', '*.ttf', '*.woff', '*.woff2']
|
||||
.pipe gulp.dest 'public/fonts'
|
||||
.pipe gulp.dest 'public'
|
||||
|
||||
gulp.task 'build:vendor', ->
|
||||
runSequence [
|
||||
'clean'
|
||||
], [
|
||||
'vendor:styles'
|
||||
'vendor:scripts'
|
||||
'vendor:fonts'
|
||||
]
|
||||
gulp.task 'build:vendor', ['vendor:bootstrap:styles', 'vendor:scripts', 'vendor:fonts']
|
||||
|
||||
gulp.task 'build:styles', ->
|
||||
gulp.src 'core/public/style/*.less'
|
||||
@@ -64,8 +74,9 @@ gulp.task 'build:scripts', ->
|
||||
gulp.task 'watch', ->
|
||||
gulp.watch 'core/public/style/*.less', ['build:styles']
|
||||
gulp.watch 'core/public/script/*.coffee', ['build:scripts']
|
||||
gulp.watch ['core/view/admin/*.jsx', 'core/view/admin/*.coffee'], ['scripts:admin']
|
||||
|
||||
gulp.task 'build', ['build:vendor', 'build:styles', 'build:scripts']
|
||||
gulp.task 'build', ['build:vendor', 'build:styles', 'build:scripts', 'scripts:admin', 'styles:admin']
|
||||
|
||||
gulp.task 'build:docs', shell.task 'node_modules/.bin/endokken --extension html --theme bullet --dest ./docs-public'
|
||||
|
||||
|
||||
14
package.json
14
package.json
@@ -59,7 +59,13 @@
|
||||
"ssh2": "0.3.6",
|
||||
"underscore": "1.7.0",
|
||||
"validator": "3.37.0",
|
||||
"ioredis": "1.3.6"
|
||||
"ioredis": "1.3.6",
|
||||
"react": "0.13.3",
|
||||
"node-jsx": "0.13.3",
|
||||
"react-bootstrap": "0.24.5",
|
||||
"backbone": "1.1.2",
|
||||
"jquery": "2.1.4",
|
||||
"js-cookie": "2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "1.10.0",
|
||||
@@ -80,6 +86,10 @@
|
||||
"mocha": "2.0.1",
|
||||
"mocha-reporter-cov-summary": "0.1.0",
|
||||
"run-sequence": "1.1.0",
|
||||
"supertest": "0.15.0"
|
||||
"supertest": "0.15.0",
|
||||
"browserify": "11.0.1",
|
||||
"reactify": "1.1.1",
|
||||
"vinyl-source-stream": "1.1.0",
|
||||
"coffeeify": "1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user