spyro 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +37 -0
- data/app/assets/OLDjavascripts/application.js +25 -0
- data/app/assets/OLDjavascripts/components.es6 +10 -0
- data/app/assets/OLDjavascripts/components/base/base.es6 +197 -0
- data/app/assets/OLDjavascripts/components/base/collection.es6 +199 -0
- data/app/assets/OLDjavascripts/components/base/element.es6 +134 -0
- data/app/assets/OLDjavascripts/components/base/field.es6 +26 -0
- data/app/assets/OLDjavascripts/components/base/inputs/text.es6 +21 -0
- data/app/assets/OLDjavascripts/components/base/pagination.es6 +23 -0
- data/app/assets/OLDjavascripts/vendor/lodash.js +121 -0
- data/app/assets/OLDjavascripts/vendor/moment-twitter.js +86 -0
- data/app/assets/OLDjavascripts/vendor/moment.js +80 -0
- data/app/assets/OLDjavascripts/vendor/pluralize.js +433 -0
- data/app/views/base/_associations.html.haml +11 -0
- data/app/views/base/_custom.html.haml +0 -0
- data/app/views/base/_details.html.haml +3 -0
- data/app/views/base/_edit_header.html.haml +3 -0
- data/app/views/base/_fields.html.haml +11 -0
- data/app/views/base/_form.html.haml +6 -0
- data/app/views/base/_index_header.html.haml +4 -0
- data/app/views/base/_new_header.html.haml +3 -0
- data/app/views/base/_other_form_fields.html.haml +0 -0
- data/app/views/base/_show_header.html.haml +9 -0
- data/app/views/base/_upload_fields.html.haml +5 -0
- data/app/views/base/copy.html.haml +10 -0
- data/app/views/base/edit.html.haml +4 -0
- data/app/views/base/index.csv.haml +8 -0
- data/app/views/base/index.html.haml +7 -0
- data/app/views/base/index.js.haml +2 -0
- data/app/views/base/index.xls.ruby +12 -0
- data/app/views/base/mailer.html.haml +46 -0
- data/app/views/base/new.html.haml +4 -0
- data/app/views/base/notify.html.haml +11 -0
- data/app/views/base/show.html.haml +8 -0
- data/app/views/base/stats.html.haml +36 -0
- data/app/views/base/trombi.html.haml +1 -0
- data/app/views/base/upload.html.haml +24 -0
- data/config/initializers/assets.rb +1 -0
- data/config/routes.rb +2 -0
- data/lib/spyro.rb +24 -0
- data/lib/spyro/active_record_add_on.rb +38 -0
- data/lib/spyro/application_controller_add_on.rb +24 -0
- data/lib/spyro/collections/outputs/admin_table.rb +28 -0
- data/lib/spyro/collections/outputs/bar_graph_table.rb +46 -0
- data/lib/spyro/collections/outputs/base.rb +23 -0
- data/lib/spyro/collections/outputs/csv.rb +34 -0
- data/lib/spyro/collections/outputs/fields.rb +118 -0
- data/lib/spyro/collections/outputs/flatui_table.rb +40 -0
- data/lib/spyro/collections/outputs/inplace_table.rb +29 -0
- data/lib/spyro/collections/outputs/map.rb +22 -0
- data/lib/spyro/collections/outputs/table.rb +126 -0
- data/lib/spyro/collections/outputs/xlsx.rb +39 -0
- data/lib/spyro/collections/parsers/active_ldap_relation.rb +22 -0
- data/lib/spyro/collections/parsers/active_record_relation.rb +138 -0
- data/lib/spyro/collections/parsers/array.rb +38 -0
- data/lib/spyro/collections/parsers/base.rb +60 -0
- data/lib/spyro/collections/parsers/kaminari_array.rb +38 -0
- data/lib/spyro/collections/parsers/model.rb +31 -0
- data/lib/spyro/controllers/strong_parameted.rb +33 -0
- data/lib/spyro/engine.rb +6 -0
- data/lib/spyro/filters_controller_add_on.rb +161 -0
- data/lib/spyro/helpers/action_view_extension.rb +726 -0
- data/lib/spyro/namespace_template_inheritance.rb +30 -0
- data/lib/spyro/usefull_attributes.rb +14 -0
- data/lib/spyro/version.rb +3 -0
- data/lib/tasks/spyro_tasks.rake +4 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/spyro_test.rb +7 -0
- data/test/test_helper.rb +21 -0
- metadata +256 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b17cc91748c789c340ec83c1770b37dbc215d088
|
4
|
+
data.tar.gz: dbd5bca264895e282867fb877eb6cd0bf5734cf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e2ab546e75cd3f076aaf7b6482b7e9f750425f1594d3fc9a64993144d85825eb15c7ea7b097d6f4bbccf1ad9ec141738ac8ff45fbe68bf033f7a11fead0ac583
|
7
|
+
data.tar.gz: 26cece3c4ff8ebbc52f4f42c7837229d5a39c68175775d83834aeec06adbbde7dcb6ee7c3fd09c1dcbc90fea13843f6882bd7014458ca664bf300bc1a8c2ad5e
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Andre Aubin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Spyro'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
var Spyro = {}
|
3
|
+
|
4
|
+
Spyro.truc = "machin";
|
5
|
+
|
6
|
+
console.log(this);
|
7
|
+
|
8
|
+
var e = function(w) {
|
9
|
+
w.Spyro = Spyro;
|
10
|
+
window.Spyro = Spyro;
|
11
|
+
console.log("w", window);
|
12
|
+
}(window);
|
13
|
+
|
14
|
+
//= require react
|
15
|
+
//= require react_ujs
|
16
|
+
//= require lodash
|
17
|
+
//= require pluralize
|
18
|
+
//= require moment
|
19
|
+
//= require moment.twitter
|
20
|
+
|
21
|
+
//= require components/base/field
|
22
|
+
//= require_tree ./components/base/inputs
|
23
|
+
//= require components/base/base
|
24
|
+
//= require_tree components/base
|
25
|
+
|
@@ -0,0 +1,197 @@
|
|
1
|
+
|
2
|
+
let Spyro = (Spyro || {});
|
3
|
+
let Field = Spyro.Field
|
4
|
+
let Text = Spyro.Text
|
5
|
+
|
6
|
+
let e = function() {
|
7
|
+
|
8
|
+
class Base extends React.Component {
|
9
|
+
constructor(props) {
|
10
|
+
super(props);
|
11
|
+
this.handleTextChange = this.handleTextChange.bind(this)
|
12
|
+
this.handleEdition = this.handleEdition.bind(this)
|
13
|
+
this.save = this.save.bind(this)
|
14
|
+
this.modelName = _.snakeCase(this.constructor.name)
|
15
|
+
this.created = (_.keys(props).includes("id"))
|
16
|
+
this._setupProperties(props)
|
17
|
+
|
18
|
+
console.log("Constructor")
|
19
|
+
}
|
20
|
+
|
21
|
+
_setupProperties(props) {
|
22
|
+
let urlBase = `/${pluralize.plural(this.modelName)}`
|
23
|
+
if (this.created)
|
24
|
+
{
|
25
|
+
urlBase += `/${props.id}`
|
26
|
+
}
|
27
|
+
|
28
|
+
if (this.state === undefined)
|
29
|
+
{
|
30
|
+
this.state = {}
|
31
|
+
}
|
32
|
+
this.state = _.extend({attributes: props, editing: false, url: urlBase}, this.state)
|
33
|
+
return true;
|
34
|
+
}
|
35
|
+
|
36
|
+
componentDidMount () {
|
37
|
+
console.log("mounted")
|
38
|
+
}
|
39
|
+
|
40
|
+
handleTextChange (e) {
|
41
|
+
let new_attributes = this.state.attributes
|
42
|
+
new_attributes[e.target.attributes.getNamedItem("data-field").value] = e.target.value
|
43
|
+
this.setState({attributes: new_attributes});
|
44
|
+
e.target.focus();
|
45
|
+
return true;
|
46
|
+
}
|
47
|
+
|
48
|
+
handleEdition (e) {
|
49
|
+
// console.log(this)
|
50
|
+
if (this) {
|
51
|
+
this.setState({editing: (this.state.editing ? false : true)});
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
render () {
|
56
|
+
let className = `${this.modelName} object-component`
|
57
|
+
return (
|
58
|
+
<div className={className}>
|
59
|
+
{(this.state.editing ? this.updateForm() : this.displayDom())}
|
60
|
+
</div>
|
61
|
+
);
|
62
|
+
}
|
63
|
+
|
64
|
+
displayDom () {
|
65
|
+
console.log(this.state)
|
66
|
+
console.log(this.constructor.propTypes)
|
67
|
+
let self = this
|
68
|
+
let fieldsDom = _.map(this.state.attributes, (value, name) => {
|
69
|
+
return this._domForItem(name, value)
|
70
|
+
return (<p key={name}>
|
71
|
+
<b>{name}</b>:
|
72
|
+
<span onClick={this.handleEdition} className={className}>{value}</span>
|
73
|
+
</p>)
|
74
|
+
})
|
75
|
+
return (
|
76
|
+
<div>
|
77
|
+
{fieldsDom}
|
78
|
+
</div>
|
79
|
+
);
|
80
|
+
}
|
81
|
+
|
82
|
+
_domForItem(name, item) {
|
83
|
+
|
84
|
+
console.log("Looking for array: ", Object.prototype.toString.call(item) === '[object Array]', item)
|
85
|
+
console.log("Looking for object: ",Object.prototype.toString.call(item) === '[object Object]', item)
|
86
|
+
if (Object.prototype.toString.call(item) === '[object Array]')
|
87
|
+
{
|
88
|
+
return _.map(item, (e) => { return this._domForItem(name, e)})
|
89
|
+
}
|
90
|
+
else if (Object.prototype.toString.call(item) === '[object Object]')
|
91
|
+
{
|
92
|
+
let objectName = _.startCase(name).replace(/s$/, '').replace(' ', '');
|
93
|
+
console.log("Object name: ", objectName)
|
94
|
+
if (typeof(window[objectName]) === "function")
|
95
|
+
{
|
96
|
+
let Component = window[objectName]
|
97
|
+
console.log("component: ", Component)
|
98
|
+
return (<Component {...item} />)
|
99
|
+
}
|
100
|
+
}
|
101
|
+
let className = `${this.modelName}-edit-field-${item}`;
|
102
|
+
|
103
|
+
return (
|
104
|
+
<p key={name}>
|
105
|
+
<b>{name}</b>:
|
106
|
+
<span onClick={this.handleEdition} className={className}>{item}</span>
|
107
|
+
</p>
|
108
|
+
)
|
109
|
+
}
|
110
|
+
|
111
|
+
fieldFor(name, value, addons = {})
|
112
|
+
{
|
113
|
+
if (this.constructor.Types && this.constructor.Types[name])
|
114
|
+
{
|
115
|
+
let dataType = this.constructor.Types[name]
|
116
|
+
let CustomField = Field[_.startCase(dataType).replace(/s$/, '').replace(' ', '')]
|
117
|
+
console.log("CustomField: ", CustomField)
|
118
|
+
return (<CustomField name={name} value={value} {...addons} />)
|
119
|
+
}
|
120
|
+
else
|
121
|
+
{
|
122
|
+
console.log("Text: ", Text)
|
123
|
+
return (<Text name={name} value={value} {...addons} />)
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
updateForm () {
|
128
|
+
let fieldsDom = _.map(this.state.attributes, (value, name) => {
|
129
|
+
let className = `${this.modelName}-edit-field-${value} input__field`;
|
130
|
+
let inputName = `${this.modelName}[${name}]`;
|
131
|
+
return (this.fieldFor(name, value, {onChange: this.handleTextChange, 'data-field': name, className: className}))
|
132
|
+
// return <Field name={name} value={value} className={className} />
|
133
|
+
// return <p key={name}><b>{name}</b>: <input type="text" onChange={this.handleTextChange} data-field={name} name={inputName} value={value} className={className}/></p>
|
134
|
+
})
|
135
|
+
return (
|
136
|
+
<form action={this.state.url} onSubmit={this.save} method={this._updateMethod()}>
|
137
|
+
{fieldsDom}
|
138
|
+
<button type="submit">Save</button>
|
139
|
+
<button onClick={this.handleEdition} type="button">Back</button>
|
140
|
+
</form>
|
141
|
+
);
|
142
|
+
}
|
143
|
+
|
144
|
+
save (e = false) {
|
145
|
+
if (e) {
|
146
|
+
e.preventDefault()
|
147
|
+
}
|
148
|
+
let action = `${this.state.url}.json`
|
149
|
+
let method = this._updateMethod()
|
150
|
+
let data = {}
|
151
|
+
data[this.modelName] = this.state.attributes
|
152
|
+
$.signedAjax({
|
153
|
+
method: method,
|
154
|
+
url: action,
|
155
|
+
data: data
|
156
|
+
})
|
157
|
+
.success((data) =>
|
158
|
+
{
|
159
|
+
console.log("SUCCESS ! ", data)
|
160
|
+
console.log(this)
|
161
|
+
this.handleEdition()
|
162
|
+
})
|
163
|
+
.error((a, b, c) => {
|
164
|
+
console.warn("ERROR", a, b, c)
|
165
|
+
})
|
166
|
+
}
|
167
|
+
|
168
|
+
refresh () {
|
169
|
+
|
170
|
+
}
|
171
|
+
|
172
|
+
_fetch (url) {
|
173
|
+
|
174
|
+
}
|
175
|
+
|
176
|
+
_url () {
|
177
|
+
|
178
|
+
}
|
179
|
+
|
180
|
+
_updateMethod () {
|
181
|
+
return (this.created ? this._method().update : this._method().create)
|
182
|
+
}
|
183
|
+
|
184
|
+
_method () {
|
185
|
+
return {
|
186
|
+
show: "GET",
|
187
|
+
index: "GET",
|
188
|
+
update: "PATCH",
|
189
|
+
create: "POST",
|
190
|
+
destroy: "DELETE"
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
Spyro.Base = Base
|
196
|
+
}()
|
197
|
+
|
@@ -0,0 +1,199 @@
|
|
1
|
+
|
2
|
+
let Spyro = (Spyro || {});
|
3
|
+
let Field = Spyro.Field
|
4
|
+
let Text = Spyro.Text
|
5
|
+
|
6
|
+
class Collection extends React.Component {
|
7
|
+
constructor(props) {
|
8
|
+
super(props);
|
9
|
+
console.log("ON CONSTRUCT", props)
|
10
|
+
this.url = props.url || document.location.pathname
|
11
|
+
this.state = {
|
12
|
+
collection: props.collection || [],
|
13
|
+
columns: this.refreshColumns(props.collection) || [],
|
14
|
+
page: props.page || 1,
|
15
|
+
order: props.order || "id",
|
16
|
+
orderDirection: props.orderDirection || "asc",
|
17
|
+
search: "",
|
18
|
+
}
|
19
|
+
this.searchableFields = props.searchableFields || _.intersection(this.state.columns, ["name", "title"])
|
20
|
+
this.perPage = props.perPage || (this.state.collection.length > 0 ? this.state.collection.length : 30)
|
21
|
+
this.model = props.model || document.location.pathname.split("/").slice(-1)
|
22
|
+
this.actions = props.actions || true
|
23
|
+
this.exclude = props.exclude || []
|
24
|
+
|
25
|
+
// Si on a pas donné de collection initiale, on va la fetcher
|
26
|
+
if (this.state.collection.length === 0)
|
27
|
+
{
|
28
|
+
this.load((d) =>
|
29
|
+
{
|
30
|
+
this.setState({columns: this.refreshColumns(this.state.collection)})
|
31
|
+
})
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
refreshColumns(collection = false) {
|
36
|
+
if (!collection)
|
37
|
+
{
|
38
|
+
return [];
|
39
|
+
}
|
40
|
+
else if (collection && collection.length > 0)
|
41
|
+
{
|
42
|
+
return _.keys(collection[0])
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
getCurrentParams () {
|
47
|
+
let additionalParams = {}
|
48
|
+
if (!this.state)
|
49
|
+
return {}
|
50
|
+
if (this.state.order)
|
51
|
+
additionalParams[(this.state.orderDirection == "asc" ? "sort" : "rev_sort")] = this.state.order
|
52
|
+
if (this.state.search && this.state.search != "")
|
53
|
+
{
|
54
|
+
console.log(this.searchableFields)
|
55
|
+
let searchFields = this.searchableFields.map((e) => `in:${e}`).join(" ")
|
56
|
+
additionalParams["search"] = `${searchFields} ${this.state.search}`
|
57
|
+
}
|
58
|
+
return _.extend({
|
59
|
+
page: this.state.page,
|
60
|
+
}, additionalParams)
|
61
|
+
}
|
62
|
+
|
63
|
+
load (callback = false) {
|
64
|
+
// console.log("load with params: ", this.getCurrentParams())
|
65
|
+
$.signedAjax({
|
66
|
+
method: 'GET',
|
67
|
+
data: this.getCurrentParams(),
|
68
|
+
url: this.url,
|
69
|
+
dataType: 'json'
|
70
|
+
})
|
71
|
+
.success((data) =>
|
72
|
+
{
|
73
|
+
// console.log("SUCCESS ! ", data)
|
74
|
+
// console.log(this)
|
75
|
+
this.setState({collection: data})
|
76
|
+
if (callback)
|
77
|
+
callback(data)
|
78
|
+
})
|
79
|
+
.error((a, b, c) => {
|
80
|
+
console.warn("ERROR", a, b, c)
|
81
|
+
if (callback)
|
82
|
+
callback(data)
|
83
|
+
})
|
84
|
+
}
|
85
|
+
|
86
|
+
destroy (id) {
|
87
|
+
if (id) {
|
88
|
+
$.signedAjax({
|
89
|
+
method: 'DELETE',
|
90
|
+
url: `${this.url}/${id}`,
|
91
|
+
dataType: 'json'
|
92
|
+
})
|
93
|
+
.success((data) => { this.setState({collection: _.reject(this.state.collection, {id: id})}) })
|
94
|
+
.error((a, b, c) => { console.warn("ERROR", a, b, c) })
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
nextPage () {
|
99
|
+
this.setState({page: this.state.page + 1})
|
100
|
+
}
|
101
|
+
|
102
|
+
previousPage () {
|
103
|
+
this.setState({page: this.state.page - 1})
|
104
|
+
}
|
105
|
+
|
106
|
+
setOrder (field) {
|
107
|
+
// console.log("ordeer: ", field)
|
108
|
+
if (this.state.order == field)
|
109
|
+
{
|
110
|
+
this.setState({orderDirection: (this.state.orderDirection == "asc" ? "desc" : "asc"), page: 1})
|
111
|
+
}
|
112
|
+
else
|
113
|
+
{
|
114
|
+
this.setState({order: field, page: 1})
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
// ====================== Lifecycle hooks =====================
|
119
|
+
|
120
|
+
componentDidUpdate (props, state)
|
121
|
+
{
|
122
|
+
let requestFields = ['page', 'order', 'orderDirection', 'search']
|
123
|
+
|
124
|
+
console.log("[MAP] new props !", props, state)
|
125
|
+
if (_.filter(requestFields, ((e) => state[e] != this.state[e])).length > 0)
|
126
|
+
{
|
127
|
+
this.load()
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
componentDidMount () {
|
132
|
+
console.log("mounted")
|
133
|
+
loadImages(document);
|
134
|
+
}
|
135
|
+
|
136
|
+
// ==================== Rendering functions ====================
|
137
|
+
|
138
|
+
|
139
|
+
renderSearch () {
|
140
|
+
let onSearch = ((e) => this.setState({search: e.target.value}))
|
141
|
+
return <input type="search" placeholder="search..." onChange={onSearch} />
|
142
|
+
}
|
143
|
+
|
144
|
+
renderHeader (columns, actions) {
|
145
|
+
// console.log("renderHeader: ", columns)
|
146
|
+
let _columns = _.without(columns, ...this.exclude)
|
147
|
+
if (actions)
|
148
|
+
_columns = _columns.concat(["actions"])
|
149
|
+
return (_columns.map((e) => {
|
150
|
+
let changeOrder = ((ev) => this.setOrder(e))
|
151
|
+
let sortActive = (this.state.order == e && this.state.orderDirection)
|
152
|
+
return <th onClick={changeOrder} data-field={_.kebabCase(e)} data-sort={sortActive}>{e}</th>
|
153
|
+
}))
|
154
|
+
}
|
155
|
+
|
156
|
+
renderActions (item) {
|
157
|
+
let url = `${this.url}/${item.id}`
|
158
|
+
let editUrl = `${url}/edit`
|
159
|
+
let deleteWrapper = ((e) => this.destroy(item.id))
|
160
|
+
return (
|
161
|
+
<span className="item-actions">
|
162
|
+
<a href={url} title="show"><i className="material-icons">visibility</i></a>
|
163
|
+
<a href={editUrl} title="edit"><i className="material-icons">create</i></a>
|
164
|
+
<a onClick={deleteWrapper} title="delete"><i className="material-icons">delete</i></a>
|
165
|
+
</span>
|
166
|
+
)
|
167
|
+
}
|
168
|
+
|
169
|
+
renderRow (item) {
|
170
|
+
// console.log("render row: ", item)
|
171
|
+
let columns = _.omit(item, this.exclude)
|
172
|
+
if (this.actions)
|
173
|
+
columns["actions"] = this.renderActions(columns)
|
174
|
+
return (_.map(columns, function(v, k) {
|
175
|
+
return <td data-field={_.kebabCase(k)}><Element name={k} value={v} /></td>
|
176
|
+
}))
|
177
|
+
}
|
178
|
+
|
179
|
+
render () {
|
180
|
+
let className = `${this.model} object-collection`
|
181
|
+
let content = this.state.collection.map((e) => {
|
182
|
+
return (<tr>{this.renderRow(e)}</tr>)
|
183
|
+
})
|
184
|
+
return (
|
185
|
+
<div className={className}>
|
186
|
+
{this.renderSearch()}
|
187
|
+
<Pagination nextPage={this.nextPage.bind(this)} previousPage={this.previousPage.bind(this)} total={this.state.collection.length} perPage={this.perPage} page={this.state.page}/>
|
188
|
+
<table>
|
189
|
+
<thead><tr>{this.renderHeader(this.state.columns, this.actions)}</tr></thead>
|
190
|
+
<tbody>{content}</tbody>
|
191
|
+
</table>
|
192
|
+
<Pagination nextPage={this.nextPage.bind(this)} previousPage={this.previousPage.bind(this)} total={this.state.collection.length} perPage={this.perPage} page={this.state.page}/>
|
193
|
+
<a onClick={this.load.bind(this)}>Refresh all</a>
|
194
|
+
</div>
|
195
|
+
);
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
|