mirror of
https://github.com/gwenhael-le-moine/credger.git
synced 2024-12-26 09:58:36 +01:00
initial real commit
This commit is contained in:
parent
71c20889aa
commit
4cff98de69
16 changed files with 1772 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/lib
|
||||
/public/vendor/node_modules
|
||||
/credger
|
||||
/public/js
|
||||
/.shards
|
73
credger.cr
Normal file
73
credger.cr
Normal file
|
@ -0,0 +1,73 @@
|
|||
require "kemal"
|
||||
|
||||
require "./ledger"
|
||||
|
||||
CURRENCY = "€"
|
||||
SEPARATOR = ","
|
||||
|
||||
ledger = Ledger.new
|
||||
|
||||
# Matches GET "http://host:port/"
|
||||
get "/" do |env|
|
||||
env.response.content_type = "text/html"
|
||||
send_file env, "./public/index.html"
|
||||
end
|
||||
|
||||
get "/api/ledger/accounts" do |env|
|
||||
env.response.content_type = "application/json"
|
||||
ledger.accounts.to_json
|
||||
end
|
||||
|
||||
# get "/api/ledger/accounts/depth/:depth/?" do |env|
|
||||
# env.response.content_type = "application/json"
|
||||
# ledger.accounts( ).to_json
|
||||
# end
|
||||
|
||||
# get "/api/ledger/dates_salaries/?" do |env|
|
||||
# env.response.content_type = "application/json"
|
||||
# ledger.dates_salaries( "salaire" ).to_json
|
||||
# end
|
||||
|
||||
# get "/api/ledger/register/?" do |env|
|
||||
# env.response.content_type = "application/json"
|
||||
# { key: params[ :categories ],
|
||||
# values: ledger.register( params[ :period ], params[ :categories ] ) }
|
||||
# .to_json
|
||||
# end
|
||||
|
||||
get "/api/ledger/balance" do |env|
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
cleared = env.params.query.has_key?( "cleared" ) ? env.params.query[ "cleared" ] == "true" : false
|
||||
|
||||
ledger.balance( cleared,
|
||||
env.params.query[ "depth" ].to_i,
|
||||
env.params.query[ "period" ],
|
||||
env.params.query[ "categories" ] )
|
||||
.to_json
|
||||
end
|
||||
|
||||
# get "/api/ledger/cleared/?" do |env|
|
||||
# env.response.content_type = "application/json"
|
||||
# ledger.cleared().to_json
|
||||
# end
|
||||
|
||||
# get "/api/ledger/budget/?" do |env|
|
||||
# env.response.content_type = "application/json"
|
||||
# ledger.budget( params[ :period ],
|
||||
# params[ :categories ] ).to_json
|
||||
# end
|
||||
|
||||
get "/api/ledger/graph_values" do |env|
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
ledger.graph_values( env.params.query["period"], env.params.query["categories"].split(" ") ).to_json
|
||||
#ledger.graph_values.to_json
|
||||
end
|
||||
|
||||
get "/api/ledger/version" do |env|
|
||||
env.response.content_type = "text"
|
||||
ledger.version
|
||||
end
|
||||
|
||||
Kemal.run 9292
|
73
ledger.cr
Normal file
73
ledger.cr
Normal file
|
@ -0,0 +1,73 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require "csv"
|
||||
|
||||
# Crystal wrapper module for calling ledger
|
||||
class Ledger
|
||||
def initialize( binary : String = "ledger", ledger_file : String = ENV[ "LEDGER_FILE" ] )
|
||||
@binary = binary
|
||||
@file = ledger_file
|
||||
end
|
||||
|
||||
def run( options : String, command : String = "", command_parameters : String = "" ) : String
|
||||
STDERR.puts "#{@binary} -f #{@file} #{options} #{command} #{command_parameters}"
|
||||
`#{@binary} -f #{@file} #{options} #{command} #{command_parameters}`
|
||||
end
|
||||
|
||||
def version : String
|
||||
run "--version"
|
||||
end
|
||||
|
||||
def accounts( depth : Number = 9999 ) : Array( Array( String ) )
|
||||
accounts = run( "", "accounts" )
|
||||
.split( "\n" )
|
||||
.map do |a|
|
||||
a.split( ":" )
|
||||
.each_slice( depth )
|
||||
.to_a.first
|
||||
end.uniq
|
||||
|
||||
accounts.map{|a| a.size}.max.times do |i|
|
||||
accounts += accounts.map { |acc| acc.first( i ) }
|
||||
end
|
||||
|
||||
accounts
|
||||
.uniq
|
||||
.sort
|
||||
.reject { |a| a.empty? || a.first.empty? }
|
||||
.sort_by { |a| a.size }
|
||||
end
|
||||
|
||||
def balance( cleared : Bool = false, depth : Int32 = 9999, period : String = nil, categories : String = "" ) : Array( NamedTuple( account: String, amount: Float64 ) )
|
||||
period = period.nil? ? "" : "-p '#{period}'"
|
||||
depth = depth.nil? ? "" : "--depth #{depth}"
|
||||
operation = cleared ? "cleared" : "balance"
|
||||
|
||||
run( "--flat --no-total --exchange '#{CURRENCY}' #{period} #{depth}", operation, categories )
|
||||
.split( "\n" )
|
||||
.reject {|line| line.empty?}
|
||||
.map do |line|
|
||||
line_array = line.split( "#{CURRENCY}" )
|
||||
|
||||
{ account: line_array[ 1 ].strip,
|
||||
amount: line_array[ 0 ].tr( SEPARATOR, "." ).to_f }
|
||||
end
|
||||
end
|
||||
|
||||
def graph_values( period : String = "", categories : Array(String) = ["Expenses"] ) : Hash( String, Array( NamedTuple( date: String, amount: String, currency: String ) ) )
|
||||
period = period == "" ? "" : "-p '#{period}'"
|
||||
|
||||
result = {} of String => Array(NamedTuple(date: String, amount: String, currency: String))
|
||||
categories.map do |category|
|
||||
result[category] = CSV
|
||||
.parse( run( "-MAn --exchange '#{CURRENCY}' #{period}", "csv --no-revalued", category ) )
|
||||
.map do |row|
|
||||
{ date: row[ 0 ],
|
||||
amount: row[ 5 ],
|
||||
currency: row[ 4 ] }
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
1
public/css/app.css
Normal file
1
public/css/app.css
Normal file
|
@ -0,0 +1 @@
|
|||
/* credger css stylesheet */
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
42
public/index.html
Normal file
42
public/index.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html lang="en" ng-app="app" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html lang="en" ng-app="app" class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html lang="en" ng-app="app" class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html lang="en" ng-app="app" class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>ledger le-moine.org</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link type="image/x-icon" rel="icon" href="/favicon.ico" />
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="/vendor/node_modules/nvd3/build/nv.d3.min.css">
|
||||
<link type="text/css" rel="stylesheet" href="/vendor/node_modules/angular-loading-bar/build/loading-bar.min.css">
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="/css/app.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="main" role="main" tabindex="-1" layout="column" ui-view></div>
|
||||
|
||||
<script src="/vendor/node_modules/underscore/underscore-min.js"></script>
|
||||
<script src="/vendor/node_modules/moment/min/moment-with-locales.min.js"></script>
|
||||
<script src="/vendor/node_modules/hammerjs/hammer.min.js"></script>
|
||||
<script src="/vendor/node_modules/d3/d3.min.js"></script>
|
||||
<script src="/vendor/node_modules/nvd3/build/nv.d3.min.js"></script>
|
||||
|
||||
<script src="/vendor/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-i18n/angular-locale_en-us.js"></script>
|
||||
<script src="/vendor/node_modules/@uirouter/angularjs/release/angular-ui-router.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-aria/angular-aria.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-animate/angular-animate.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-moment/angular-moment.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-nvd3/dist/angular-nvd3.min.js"></script>
|
||||
<script src="/vendor/node_modules/angular-loading-bar/build/loading-bar.min.js"></script>
|
||||
|
||||
<!-- APP -->
|
||||
<script src="/js/app.min.js"></script>
|
||||
</body>
|
||||
</html>
|
15
public/ts/app.ts
Normal file
15
public/ts/app.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
var app = angular.module('app',
|
||||
['ui.router',
|
||||
'nvd3',
|
||||
'angularMoment',
|
||||
'chieffancypants.loadingBar',
|
||||
])
|
||||
.config(['$stateProvider', '$urlRouterProvider',
|
||||
function($stateProvider, $urlRouterProvider) {
|
||||
$stateProvider
|
||||
.state('app', {
|
||||
url: '',
|
||||
component: 'dashboard'
|
||||
});
|
||||
}
|
||||
]);
|
98
public/ts/components/bucket.ts
Normal file
98
public/ts/components/bucket.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
app.component('bucket',
|
||||
{
|
||||
bindings: {
|
||||
categories: '<',
|
||||
period: '<'
|
||||
},
|
||||
controller: ['$filter', 'API',
|
||||
function($filter, API) {
|
||||
let ctrl = this;
|
||||
ctrl.depth = 99;
|
||||
|
||||
ctrl.graph_options = {
|
||||
chart: {
|
||||
type: 'multiBarHorizontalChart',
|
||||
height: 600,
|
||||
margin: {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
left: 200
|
||||
},
|
||||
x: (d) => { return d.account; },
|
||||
y: (d) => { return d.amount; },
|
||||
valueFormat: (d) => { return `${d} €`; },
|
||||
showYAxis: false,
|
||||
showValues: true,
|
||||
showLegend: true,
|
||||
showControls: false,
|
||||
showTooltipPercent: true,
|
||||
duration: 500,
|
||||
labelThreshold: 0.01,
|
||||
labelSunbeamLayout: true,
|
||||
labelsOutside: true
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$onChanges = (changes) => {
|
||||
if (changes.period && changes.period.currentValue != undefined) {
|
||||
API.balance({
|
||||
period: ctrl.period,
|
||||
categories: ctrl.categories,
|
||||
depth: ctrl.depth
|
||||
})
|
||||
.then((response) => {
|
||||
ctrl.raw_data = _(response.data)
|
||||
.sortBy((account) => { return account.amount; })
|
||||
.reverse();
|
||||
ctrl.raw_total = _(response.data).reduce((memo, account) => { return memo + account.amount; }, 0);
|
||||
|
||||
ctrl.total_detailed = _.chain(ctrl.raw_data)
|
||||
.groupBy((account) => {
|
||||
return account.account.split(':')[0];
|
||||
})
|
||||
.each((category) => {
|
||||
category.total = _(category).reduce((memo, account) => {
|
||||
return memo + account.amount;
|
||||
}, 0);
|
||||
})
|
||||
.value();
|
||||
ctrl.total_detailed = _.chain(ctrl.total_detailed)
|
||||
.keys()
|
||||
.map((key) => {
|
||||
return {
|
||||
account: key,
|
||||
amount: ctrl.total_detailed[key].total
|
||||
};
|
||||
})
|
||||
.value();
|
||||
|
||||
ctrl.graph_options.chart.height = 60 + (25 * ctrl.raw_data.length);
|
||||
|
||||
ctrl.data = ctrl.categories.split(' ').map((category) => {
|
||||
return {
|
||||
key: category,
|
||||
values: _(ctrl.raw_data).select((line) => { return line.account.match(`^${category}:.*`); })
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
],
|
||||
|
||||
template: `
|
||||
<div class="bucket">
|
||||
<div class="tollbar">
|
||||
<span ng:repeat="account in $ctrl.total_detailed">{{account.account}} = {{account.amount | number:2}} €</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="graph">
|
||||
<nvd3 data="$ctrl.data"
|
||||
options="$ctrl.graph_options">
|
||||
</nvd3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
143
public/ts/components/dashboard.ts
Normal file
143
public/ts/components/dashboard.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
app.component('dashboard',
|
||||
{
|
||||
controller: ['$filter', 'API',
|
||||
function($filter, API) {
|
||||
let ctrl = this;
|
||||
ctrl.graphed_accounts = ['Expenses', 'Income'];
|
||||
|
||||
let retrieve_graph_values = (params) => {
|
||||
API.graph_values(params)
|
||||
.then((response) => {
|
||||
ctrl.periods = [];
|
||||
|
||||
let largest_cat = _(response.data).reduce((memo, cat) => { return cat.length > memo.length ? cat : memo; }, []);
|
||||
|
||||
_.chain(largest_cat)
|
||||
.pluck('date')
|
||||
.each((date) => {
|
||||
_(response.data).each((cat) => {
|
||||
let value = _(cat).find({ date: date });
|
||||
if (_(value).isUndefined()) {
|
||||
cat.push({
|
||||
date: date,
|
||||
amount: 0,
|
||||
currency: _(cat).first().currency
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
_(response.data).each((cat) => {
|
||||
cat = _(cat).sortBy((month) => {
|
||||
return month.date;
|
||||
});
|
||||
});
|
||||
|
||||
ctrl.graphiques = {
|
||||
monthly_values: {
|
||||
options: {
|
||||
chart: {
|
||||
type: 'multiBarChart',
|
||||
height: 300,
|
||||
showControls: false,
|
||||
showLegend: true,
|
||||
showLabels: true,
|
||||
showValues: true,
|
||||
showYAxis: false,
|
||||
|
||||
x: (d) => { return d.x; },
|
||||
y: (d) => { return d.y; },
|
||||
valueFormat: (d) => { return `${d} €`; },
|
||||
|
||||
xAxis: {
|
||||
tickFormat: (d) => {
|
||||
return `${d}${d == ctrl.period ? '*' : ''}`;
|
||||
}
|
||||
},
|
||||
stacked: false,
|
||||
duration: 500,
|
||||
reduceXTicks: false,
|
||||
rotateLabels: -67,
|
||||
labelSunbeamLayout: true,
|
||||
useInteractiveGuideline: true,
|
||||
multibar: {
|
||||
dispatch: {
|
||||
elementClick: (event) => {
|
||||
console.log('change period')
|
||||
console.log(ctrl.period)
|
||||
|
||||
ctrl.period = event.data.x;
|
||||
|
||||
console.log(ctrl.period)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: _.chain(response.data)
|
||||
.keys()
|
||||
.reverse()
|
||||
.map((key) => {
|
||||
let multiplicator = (key == "Income") ? -1 : 1;
|
||||
return {
|
||||
key: key,
|
||||
values: _.chain(response.data[key]).map((value) => {
|
||||
let date = new Date(value.date);
|
||||
let period = date.getFullYear() + '-' + (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1);
|
||||
ctrl.periods.push(period);
|
||||
|
||||
return {
|
||||
key: key,
|
||||
x: period,
|
||||
y: parseInt(value.amount) * multiplicator
|
||||
};
|
||||
})
|
||||
.sortBy((item) => { return item.x; })
|
||||
.value()
|
||||
};
|
||||
})
|
||||
.value()
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.periods = _.chain(ctrl.periods).uniq().sort().reverse().value();
|
||||
ctrl.period = _(ctrl.periods).first();
|
||||
});
|
||||
};
|
||||
|
||||
API.accounts()
|
||||
.then((response) => {
|
||||
ctrl.raw_accounts = response.data;
|
||||
ctrl.accounts = ctrl.raw_accounts.map((account_ary) => { return account_ary.join(':'); });
|
||||
});
|
||||
|
||||
retrieve_graph_values({
|
||||
period: '',
|
||||
categories: ctrl.graphed_accounts.join(' ')
|
||||
});
|
||||
}
|
||||
],
|
||||
|
||||
template: `
|
||||
<div class="dashboard">
|
||||
<div class="global-graph" style="height: 300px;">
|
||||
<div class="accounts" style="width: 20%; height: 100%; float: left;">
|
||||
<select style="height: 100%;" multiple ng:model="$ctrl.graphed_accounts">
|
||||
<option ng:repeat="account in $ctrl.accounts">{{account}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="graph" style="width: 80%; float: left;">
|
||||
<nvd3 data="$ctrl.graphiques.monthly_values.data"
|
||||
options="$ctrl.graphiques.monthly_values.options">
|
||||
</nvd3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 style="text-align: center;">
|
||||
<select ng:options="p as p | amDateFormat:'MMMM YYYY' for p in $ctrl.periods" ng:model="$ctrl.period"></select>
|
||||
</h1>
|
||||
|
||||
<bucket categories="'Expenses Income Equity Liabilities'" period="$ctrl.period"></bucket>
|
||||
</div>
|
||||
`
|
||||
});
|
54
public/ts/services/API.ts
Normal file
54
public/ts/services/API.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
app.service('API',
|
||||
['$http',
|
||||
function($http) {
|
||||
let API = this;
|
||||
|
||||
API.balance = function(params) {
|
||||
return $http.get('/api/ledger/balance', {
|
||||
params: {
|
||||
period: params.period,
|
||||
categories: params.categories,
|
||||
depth: params.depth
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
API.register = function(params) {
|
||||
return $http.get('/api/ledger/register', {
|
||||
params: {
|
||||
period: params.period,
|
||||
categories: params.categories
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
API.graph_values = function(params) {
|
||||
return $http.get('/api/ledger/graph_values', {
|
||||
params: {
|
||||
period: params.period,
|
||||
categories: params.categories
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
API.budget = function(params) {
|
||||
return $http.get('/api/ledger/budget', {
|
||||
params: {
|
||||
period: params.period,
|
||||
categories: params.categories
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
API.dates_salaries = function() {
|
||||
return $http.get('/ai/ledger/dates_salaries');
|
||||
};
|
||||
|
||||
API.accounts = function() {
|
||||
return $http.get('/api/ledger/accounts');
|
||||
};
|
||||
|
||||
API.cleared = function() {
|
||||
return $http.get('/api/ledger/cleared');
|
||||
};
|
||||
}]);
|
18
public/tsconfig.json
Normal file
18
public/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "none",
|
||||
"declaration": false,
|
||||
"removeComments": true,
|
||||
"sourceMap": false,
|
||||
"outFile": "js/app.js",
|
||||
"lib": [
|
||||
"dom","es2015"
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
"ts/app.ts",
|
||||
"ts/services/*.ts",
|
||||
"ts/components/*.ts",
|
||||
],
|
||||
}
|
23
public/tsfmt.json
Normal file
23
public/tsfmt.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"baseIndentSize": 0,
|
||||
"indentSize": 2,
|
||||
"tabSize": 2,
|
||||
"indentStyle": 2,
|
||||
"newLineCharacter": "\n",
|
||||
"convertTabsToSpaces": true,
|
||||
"insertSpaceAfterCommaDelimiter": true,
|
||||
"insertSpaceAfterSemicolonInForStatements": true,
|
||||
"insertSpaceBeforeAndAfterBinaryOperators": true,
|
||||
"insertSpaceAfterConstructor": false,
|
||||
"insertSpaceAfterKeywordsInControlFlowStatements": true,
|
||||
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": true,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
|
||||
"insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": true,
|
||||
"insertSpaceAfterTypeAssertion": true,
|
||||
"insertSpaceBeforeFunctionParenthesis": false,
|
||||
"placeOpenBraceOnNewLineForFunctions": false,
|
||||
"placeOpenBraceOnNewLineForControlBlocks": false,
|
||||
}
|
1165
public/vendor/package-lock.json
generated
vendored
Normal file
1165
public/vendor/package-lock.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
41
public/vendor/package.json
vendored
Normal file
41
public/vendor/package.json
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "ledgerrb",
|
||||
"version": "1.0.0",
|
||||
"description": "Ledger reporting for the Web",
|
||||
"main": "./app/index.html",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/cycojesus/ledgerrb.git"
|
||||
},
|
||||
"author": "Gwenhael Le Moine",
|
||||
"license": "GPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/cycojesus/ledgerrb/issues"
|
||||
},
|
||||
"homepage": "https://github.com/cycojesus/ledgerrb#readme",
|
||||
"dependencies": {
|
||||
"@uirouter/angularjs": "^1.0.11",
|
||||
"angular": "^1.5.8",
|
||||
"angular-animate": "^1.5.8",
|
||||
"angular-aria": "^1.5.8",
|
||||
"angular-i18n": "^1.5.8",
|
||||
"angular-loader": "^1.5.8",
|
||||
"angular-loading-bar": "^0.9.0",
|
||||
"angular-messages": "^1.5.8",
|
||||
"angular-mocks": "^1.5.8",
|
||||
"angular-moment": "^1.0.0-beta.6",
|
||||
"angular-nvd3": "^1.0.8",
|
||||
"angularjs-nvd3-directives": "0.0.8",
|
||||
"boilerplate": "^0.6.1",
|
||||
"d3": "^3.5.17",
|
||||
"google-closure-compiler-js": "^20170910.0.1",
|
||||
"hammerjs": "^2.0.8",
|
||||
"moment": "^2.19.2",
|
||||
"nvd3": "^1.8.4",
|
||||
"typescript": "^2.6.1",
|
||||
"underscore": "^1.8.3"
|
||||
}
|
||||
}
|
14
shard.lock
Normal file
14
shard.lock
Normal file
|
@ -0,0 +1,14 @@
|
|||
version: 1.0
|
||||
shards:
|
||||
kemal:
|
||||
github: kemalcr/kemal
|
||||
commit: 8cb9770ec3c6cf5897e644229dad8d0b5c360941
|
||||
|
||||
kilt:
|
||||
github: jeromegn/kilt
|
||||
version: 0.4.0
|
||||
|
||||
radix:
|
||||
github: luislavena/radix
|
||||
version: 0.3.8
|
||||
|
7
shard.yml
Normal file
7
shard.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
name: credger
|
||||
version: 1.99
|
||||
|
||||
dependencies:
|
||||
kemal:
|
||||
github: kemalcr/kemal
|
||||
branch: master
|
Loading…
Reference in a new issue