api_maker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +476 -0
- data/Rakefile +27 -0
- data/app/channels/api_maker/subscriptions_channel.rb +80 -0
- data/app/controllers/api_maker/base_controller.rb +32 -0
- data/app/controllers/api_maker/commands_controller.rb +26 -0
- data/app/controllers/api_maker/devise_controller.rb +60 -0
- data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
- data/app/services/api_maker/application_service.rb +7 -0
- data/app/services/api_maker/collection_command_service.rb +24 -0
- data/app/services/api_maker/command_response.rb +67 -0
- data/app/services/api_maker/command_service.rb +31 -0
- data/app/services/api_maker/create_command.rb +62 -0
- data/app/services/api_maker/create_command_service.rb +18 -0
- data/app/services/api_maker/destroy_command.rb +39 -0
- data/app/services/api_maker/destroy_command_service.rb +22 -0
- data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
- data/app/services/api_maker/index_command.rb +96 -0
- data/app/services/api_maker/index_command_service.rb +22 -0
- data/app/services/api_maker/js_method_namer_service.rb +11 -0
- data/app/services/api_maker/member_command_service.rb +25 -0
- data/app/services/api_maker/model_content_generator_service.rb +108 -0
- data/app/services/api_maker/models_finder_service.rb +22 -0
- data/app/services/api_maker/models_generator_service.rb +104 -0
- data/app/services/api_maker/update_command.rb +43 -0
- data/app/services/api_maker/update_command_service.rb +21 -0
- data/app/services/api_maker/valid_command.rb +35 -0
- data/app/services/api_maker/valid_command_service.rb +21 -0
- data/app/views/api_maker/_data.html.erb +15 -0
- data/config/rails_best_practices.yml +55 -0
- data/config/routes.rb +7 -0
- data/lib/api_maker.rb +36 -0
- data/lib/api_maker/ability.rb +39 -0
- data/lib/api_maker/ability_loader.rb +21 -0
- data/lib/api_maker/action_controller_base_extensions.rb +5 -0
- data/lib/api_maker/base_command.rb +81 -0
- data/lib/api_maker/base_resource.rb +78 -0
- data/lib/api_maker/collection_serializer.rb +69 -0
- data/lib/api_maker/command_spec_helper.rb +57 -0
- data/lib/api_maker/configuration.rb +34 -0
- data/lib/api_maker/engine.rb +5 -0
- data/lib/api_maker/individual_command.rb +37 -0
- data/lib/api_maker/javascript/api.js +92 -0
- data/lib/api_maker/javascript/base-model.js +543 -0
- data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
- data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
- data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
- data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
- data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
- data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
- data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
- data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
- data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
- data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
- data/lib/api_maker/javascript/cable-subscription.js +33 -0
- data/lib/api_maker/javascript/collection.js +186 -0
- data/lib/api_maker/javascript/commands-pool.js +123 -0
- data/lib/api_maker/javascript/custom-error.js +14 -0
- data/lib/api_maker/javascript/deserializer.js +35 -0
- data/lib/api_maker/javascript/devise.js.erb +113 -0
- data/lib/api_maker/javascript/error-logger.js +119 -0
- data/lib/api_maker/javascript/event-connection.jsx +24 -0
- data/lib/api_maker/javascript/event-created.jsx +26 -0
- data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
- data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
- data/lib/api_maker/javascript/event-listener.jsx +41 -0
- data/lib/api_maker/javascript/event-updated.jsx +26 -0
- data/lib/api_maker/javascript/form-data-to-object.js +70 -0
- data/lib/api_maker/javascript/included.js +39 -0
- data/lib/api_maker/javascript/key-value-store.js +47 -0
- data/lib/api_maker/javascript/logger.js +23 -0
- data/lib/api_maker/javascript/model-name.js +21 -0
- data/lib/api_maker/javascript/model-template.js.erb +110 -0
- data/lib/api_maker/javascript/models-response-reader.js +43 -0
- data/lib/api_maker/javascript/paginate.jsx +128 -0
- data/lib/api_maker/javascript/params.js +68 -0
- data/lib/api_maker/javascript/resource-route.jsx +75 -0
- data/lib/api_maker/javascript/resource-routes.jsx +36 -0
- data/lib/api_maker/javascript/result.js +25 -0
- data/lib/api_maker/javascript/session-status-updater.js +113 -0
- data/lib/api_maker/javascript/sort-link.jsx +88 -0
- data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
- data/lib/api_maker/loader.rb +14 -0
- data/lib/api_maker/memory_storage.rb +65 -0
- data/lib/api_maker/model_extensions.rb +96 -0
- data/lib/api_maker/permitted_params_argument.rb +12 -0
- data/lib/api_maker/preloader.rb +91 -0
- data/lib/api_maker/preloader_belongs_to.rb +58 -0
- data/lib/api_maker/preloader_has_many.rb +69 -0
- data/lib/api_maker/preloader_has_one.rb +70 -0
- data/lib/api_maker/preloader_through.rb +101 -0
- data/lib/api_maker/railtie.rb +14 -0
- data/lib/api_maker/relationship_includer.rb +42 -0
- data/lib/api_maker/resource_routing.rb +8 -0
- data/lib/api_maker/result_parser.rb +50 -0
- data/lib/api_maker/serializer.rb +86 -0
- data/lib/api_maker/spec_helper.rb +100 -0
- data/lib/api_maker/version.rb +3 -0
- data/lib/tasks/api_maker_tasks.rake +5 -0
- metadata +581 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
export default class BootstrapAttributeRow extends React.Component {
|
|
4
|
+
render() {
|
|
5
|
+
return (
|
|
6
|
+
<tr>
|
|
7
|
+
<th>
|
|
8
|
+
{this.props.label}
|
|
9
|
+
</th>
|
|
10
|
+
<td>
|
|
11
|
+
{this.props.value || this.props.children}
|
|
12
|
+
</td>
|
|
13
|
+
</tr>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import AttributeRow from "./attribute-row"
|
|
2
|
+
import PropTypes from "prop-types"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
export default class BootstrapAttributeRows extends React.Component {
|
|
6
|
+
static propTypes = {
|
|
7
|
+
attributes: PropTypes.array.isRequired,
|
|
8
|
+
model: PropTypes.object.isRequired
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
constructor(props) {
|
|
12
|
+
super(props)
|
|
13
|
+
this.state = {
|
|
14
|
+
classObject: props.model.modelClass()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
render() {
|
|
19
|
+
return this.props.attributes.map((attribute) =>
|
|
20
|
+
<AttributeRow key={`attribute-${attribute}`} label={this.state.classObject.humanAttributeName(attribute)}>
|
|
21
|
+
{this.valueContent(attribute)}
|
|
22
|
+
</AttributeRow>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
value(attribute) {
|
|
27
|
+
if (!(attribute in this.props.model))
|
|
28
|
+
throw new Error(`Attribute not found: ${this.props.model.modelClassData().name}#${attribute}`)
|
|
29
|
+
|
|
30
|
+
return this.props.model[attribute]()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
valueContent(attribute) {
|
|
34
|
+
var value = this.value(attribute)
|
|
35
|
+
|
|
36
|
+
if (value instanceof Date) {
|
|
37
|
+
return I18n.strftime(value, "%Y-%m-%d %H:%M")
|
|
38
|
+
} else if (typeof value === "boolean") {
|
|
39
|
+
if (value)
|
|
40
|
+
return I18n.t("js.shared.yes")
|
|
41
|
+
|
|
42
|
+
return I18n.t("js.shared.no")
|
|
43
|
+
} else {
|
|
44
|
+
return this.value(attribute)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import PropTypes from "prop-types"
|
|
2
|
+
import PropTypesExact from "prop-types-exact"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
export default class Card extends React.Component {
|
|
6
|
+
static defaultProps = {
|
|
7
|
+
responsiveTable: true
|
|
8
|
+
}
|
|
9
|
+
static propTypes = PropTypesExact({
|
|
10
|
+
className: PropTypes.string,
|
|
11
|
+
children: PropTypes.node,
|
|
12
|
+
controls: PropTypes.node,
|
|
13
|
+
header: PropTypes.string,
|
|
14
|
+
onClick: PropTypes.func,
|
|
15
|
+
striped: PropTypes.bool,
|
|
16
|
+
style: PropTypes.object,
|
|
17
|
+
responsiveTable: PropTypes.bool,
|
|
18
|
+
table: PropTypes.bool
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
render() {
|
|
22
|
+
var { children, controls, header, onClick, style, table } = this.props
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className={this.classNames()} onClick={onClick} ref="card" style={style}>
|
|
26
|
+
{(controls || header) &&
|
|
27
|
+
<div className="card-header">
|
|
28
|
+
{header}
|
|
29
|
+
{controls &&
|
|
30
|
+
<div className="float-right">
|
|
31
|
+
{controls}
|
|
32
|
+
</div>
|
|
33
|
+
}
|
|
34
|
+
</div>
|
|
35
|
+
}
|
|
36
|
+
<div className={this.bodyClassNames()}>
|
|
37
|
+
{table &&
|
|
38
|
+
<table className={this.tableClassNames()}>
|
|
39
|
+
{children}
|
|
40
|
+
</table>
|
|
41
|
+
}
|
|
42
|
+
{!table && children}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
classNames() {
|
|
49
|
+
var classNames = ["component-bootstrap-card", "card", "card-default"]
|
|
50
|
+
|
|
51
|
+
if (this.props.className)
|
|
52
|
+
classNames.push(this.props.className)
|
|
53
|
+
|
|
54
|
+
return classNames.join(" ")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
bodyClassNames() {
|
|
58
|
+
var classNames = ["card-body"]
|
|
59
|
+
|
|
60
|
+
if (this.props.table) {
|
|
61
|
+
if (this.props.responsiveTable){
|
|
62
|
+
classNames.push("table-responsive")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
classNames.push("p-0")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return classNames.join(" ")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
tableClassNames() {
|
|
72
|
+
var classNames = ["table", "table-hover", "mb-0", "w-100"]
|
|
73
|
+
|
|
74
|
+
if (this.props.striped)
|
|
75
|
+
classNames.push("table-striped")
|
|
76
|
+
|
|
77
|
+
return classNames.join(" ")
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import PropTypes from "prop-types"
|
|
2
|
+
import PropTypesExact from "prop-types-exact"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
const inflection = require("inflection")
|
|
6
|
+
|
|
7
|
+
export default class BootstrapCheckbox extends React.Component {
|
|
8
|
+
static propTypes = PropTypesExact({
|
|
9
|
+
attribute: PropTypes.string,
|
|
10
|
+
className: PropTypes.string,
|
|
11
|
+
"data-action": PropTypes.string,
|
|
12
|
+
"data-target": PropTypes.string,
|
|
13
|
+
defaultChecked: PropTypes.bool,
|
|
14
|
+
defaultValue: PropTypes.node,
|
|
15
|
+
hint: PropTypes.node,
|
|
16
|
+
id: PropTypes.string,
|
|
17
|
+
label: PropTypes.node,
|
|
18
|
+
labelClassName: PropTypes.string,
|
|
19
|
+
model: PropTypes.object,
|
|
20
|
+
name: PropTypes.string,
|
|
21
|
+
onChange: PropTypes.func,
|
|
22
|
+
wrapperClassName: PropTypes.string
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
let id = this.inputId()
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={this.wrapperClassName()}>
|
|
30
|
+
<div className="form-check">
|
|
31
|
+
<input defaultValue="0" name={this.inputName()} type="hidden" type="hidden" />
|
|
32
|
+
<input
|
|
33
|
+
data-target={this.props["data-target"]}
|
|
34
|
+
defaultChecked={this.inputDefaultChecked()}
|
|
35
|
+
className={this.className()}
|
|
36
|
+
data-action={this.props["data-action"]}
|
|
37
|
+
defaultValue="1"
|
|
38
|
+
id={id}
|
|
39
|
+
name={this.inputName()}
|
|
40
|
+
onChange={this.props.onChange}
|
|
41
|
+
ref="input"
|
|
42
|
+
type="checkbox"
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
{this.label() &&
|
|
46
|
+
<label className={this.labelClassName()} htmlFor={id}>
|
|
47
|
+
{this.label()}
|
|
48
|
+
</label>
|
|
49
|
+
}
|
|
50
|
+
</div>
|
|
51
|
+
{this.props.hint &&
|
|
52
|
+
<p className="text-muted">
|
|
53
|
+
{this.props.hint}
|
|
54
|
+
</p>
|
|
55
|
+
}
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
className() {
|
|
61
|
+
var classNames = ["form-check-input"]
|
|
62
|
+
|
|
63
|
+
if (this.props.className)
|
|
64
|
+
classNames.push(this.props.className)
|
|
65
|
+
|
|
66
|
+
return classNames.join(" ")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
generatedId() {
|
|
70
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
inputDefaultChecked() {
|
|
74
|
+
if ("defaultChecked" in this.props) {
|
|
75
|
+
return this.props.defaultChecked
|
|
76
|
+
} else if (this.props.model) {
|
|
77
|
+
if (!this.props.model[this.props.attribute])
|
|
78
|
+
throw new Error(`No such attribute: ${this.props.attribute}`)
|
|
79
|
+
|
|
80
|
+
return this.props.model[this.props.attribute]()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
inputId() {
|
|
85
|
+
if (this.props.id) {
|
|
86
|
+
return this.props.id
|
|
87
|
+
} else if (this.props.model) {
|
|
88
|
+
return `${this.props.model.modelClassData().paramKey}_${inflection.underscore(this.props.attribute)}`
|
|
89
|
+
} else {
|
|
90
|
+
return this.generatedId()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
inputName() {
|
|
95
|
+
if (this.props.name) {
|
|
96
|
+
return this.props.name
|
|
97
|
+
} else if (this.props.model) {
|
|
98
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
wrapperClassName() {
|
|
103
|
+
let classNames = ["component-bootstrap-checkbox"]
|
|
104
|
+
|
|
105
|
+
if (this.props.wrapperClassName)
|
|
106
|
+
classNames.push(this.props.wrapperClassName)
|
|
107
|
+
|
|
108
|
+
return classNames.join(" ")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
label() {
|
|
112
|
+
if (this.props.label) {
|
|
113
|
+
return this.props.label
|
|
114
|
+
} else if (this.props.model) {
|
|
115
|
+
return this.props.model.modelClass().humanAttributeName(this.props.attribute)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
labelClassName() {
|
|
120
|
+
let classNames = ["form-check-label"]
|
|
121
|
+
|
|
122
|
+
if (this.props.labelClassName)
|
|
123
|
+
classNames.push(this.props.labelClassName)
|
|
124
|
+
|
|
125
|
+
return classNames.join(" ")
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import PropTypes from "prop-types"
|
|
2
|
+
import PropTypesExact from "prop-types-exact"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
const inflection = require("inflection")
|
|
6
|
+
|
|
7
|
+
export default class BootstrapCheckboxes extends React.Component {
|
|
8
|
+
static propTypes = PropTypesExact({
|
|
9
|
+
attribute: PropTypes.string,
|
|
10
|
+
defaultValue: PropTypes.array,
|
|
11
|
+
label: PropTypes.string,
|
|
12
|
+
labelClassName: PropTypes.string,
|
|
13
|
+
model: PropTypes.object,
|
|
14
|
+
name: PropTypes.string,
|
|
15
|
+
options: PropTypes.array.isRequired
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
render() {
|
|
19
|
+
return (
|
|
20
|
+
<div className="component-bootstrap-checkboxes form-group">
|
|
21
|
+
<label className={this.labelClassName()}>
|
|
22
|
+
{this.label()}
|
|
23
|
+
</label>
|
|
24
|
+
|
|
25
|
+
<input name={this.inputName()} type="hidden" value="" />
|
|
26
|
+
{this.props.options.map((option, index) => this.optionElement(option, index))}
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
inputDefaultValue() {
|
|
32
|
+
if (this.props.defaultValue) {
|
|
33
|
+
return this.props.defaultValue
|
|
34
|
+
} else if (this.props.model) {
|
|
35
|
+
if (!this.props.model[this.props.attribute])
|
|
36
|
+
throw `No such attribute: ${this.props.attribute}`
|
|
37
|
+
|
|
38
|
+
return this.props.model[this.props.attribute]()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
inputName() {
|
|
43
|
+
if (this.props.name) {
|
|
44
|
+
return `${this.props.name}[]`
|
|
45
|
+
} else if (this.props.model) {
|
|
46
|
+
return `${this.props.model.modelClassData().paramKey}[${inflection.underscore(this.props.attribute)}]`
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
isDefaultSelected(option) {
|
|
51
|
+
let defaultValue = this.inputDefaultValue()
|
|
52
|
+
|
|
53
|
+
if (!defaultValue)
|
|
54
|
+
return false
|
|
55
|
+
|
|
56
|
+
if (defaultValue.constructor === Array) {
|
|
57
|
+
return defaultValue.includes(option)
|
|
58
|
+
} else {
|
|
59
|
+
return defaultValue == option
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
label() {
|
|
64
|
+
if (this.props.label === false) {
|
|
65
|
+
return null
|
|
66
|
+
} else if (this.props.label) {
|
|
67
|
+
return this.props.label
|
|
68
|
+
} else if (this.props.model) {
|
|
69
|
+
return this.props.model.modelClass().humanAttributeName(this.props.attribute)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
labelClassName() {
|
|
74
|
+
let classNames = []
|
|
75
|
+
|
|
76
|
+
if (this.props.labelClassName)
|
|
77
|
+
classNames.push(this.props.labelClassName)
|
|
78
|
+
|
|
79
|
+
return classNames.join(" ")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
generatedId() {
|
|
83
|
+
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
optionElement(option) {
|
|
87
|
+
var id = this.generatedId()
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="checkboxes-option" key={`option-${option[1]}`}>
|
|
91
|
+
<input
|
|
92
|
+
data-option-value={option[1]}
|
|
93
|
+
defaultChecked={this.isDefaultSelected(option[1])}
|
|
94
|
+
id={id}
|
|
95
|
+
name={this.inputName()}
|
|
96
|
+
type="checkbox"
|
|
97
|
+
value={option[1]} />
|
|
98
|
+
|
|
99
|
+
<label className="ml-1" htmlFor={id}>
|
|
100
|
+
{option[0]}
|
|
101
|
+
</label>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import Collection from "api-maker/collection"
|
|
2
|
+
import EventCreated from "api-maker/event-created"
|
|
3
|
+
import EventDestroyed from "api-maker/event-destroyed"
|
|
4
|
+
import Paginate from "api-maker/paginate"
|
|
5
|
+
import Params from "api-maker/params"
|
|
6
|
+
|
|
7
|
+
const inflection = require("inflection")
|
|
8
|
+
|
|
9
|
+
export default class LiveTable extends React.Component {
|
|
10
|
+
static defaultProps = {
|
|
11
|
+
preloads: [],
|
|
12
|
+
select: {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static propTypes = PropTypesExact({
|
|
16
|
+
className: PropTypes.string,
|
|
17
|
+
collection: PropTypes.instanceOf(Collection),
|
|
18
|
+
columnsContent: PropTypes.func.isRequired,
|
|
19
|
+
defaultParams: PropTypes.object,
|
|
20
|
+
destroyMessage: PropTypes.string,
|
|
21
|
+
filterContent: PropTypes.func,
|
|
22
|
+
filterSubmitLabel: PropTypes.node,
|
|
23
|
+
headersContent: PropTypes.func.isRequired,
|
|
24
|
+
modelClass: PropTypes.func.isRequired,
|
|
25
|
+
preloads: PropTypes.array.isRequired,
|
|
26
|
+
queryName: PropTypes.string.isRequired,
|
|
27
|
+
select: PropTypes.object
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
constructor(props) {
|
|
31
|
+
super(props)
|
|
32
|
+
this.state = {
|
|
33
|
+
currentHref: location.href,
|
|
34
|
+
queryQName: `${this.props.queryName}_q`,
|
|
35
|
+
queryPageName: `${this.props.queryName}_page`
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
componentDidMount() {
|
|
40
|
+
this.loadQParams().then(() => this.loadModels())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
componentDidUpdate() {
|
|
44
|
+
if (this.state.currentHref != location.href) {
|
|
45
|
+
var { queryQName } = this.state
|
|
46
|
+
var params = Params.parse()
|
|
47
|
+
var qParams = params[queryQName] || {}
|
|
48
|
+
Params.setCachedParams(queryQName, qParams)
|
|
49
|
+
this.setState({currentHref: location.href, qParams}, () => this.loadModels())
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async loadQParams() {
|
|
54
|
+
var { queryQName } = this.state
|
|
55
|
+
var qParams = await Params.getCachedParams(queryQName, {default: this.props.defaultParams || {}})
|
|
56
|
+
return this.setState({qParams})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async loadModels() {
|
|
60
|
+
var params = Params.parse()
|
|
61
|
+
var { modelClass, preloads, select } = this.props
|
|
62
|
+
var { qParams, queryPageName, queryQName } = this.state
|
|
63
|
+
var query
|
|
64
|
+
|
|
65
|
+
if (this.props.collection) {
|
|
66
|
+
query = this.props.collection
|
|
67
|
+
} else {
|
|
68
|
+
query = modelClass
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
query = query
|
|
72
|
+
.ransack(qParams)
|
|
73
|
+
.searchKey(queryQName)
|
|
74
|
+
.page(params[queryPageName])
|
|
75
|
+
.pageKey(queryPageName)
|
|
76
|
+
.preload(preloads)
|
|
77
|
+
.select(select)
|
|
78
|
+
|
|
79
|
+
var result = await query.result()
|
|
80
|
+
|
|
81
|
+
this.setState({query, result, models: result.models()})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
render() {
|
|
85
|
+
var { qParams, query, result, models } = this.state
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className={this.className()}>
|
|
89
|
+
{qParams && query && result && models && this.content()}
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
content() {
|
|
95
|
+
var { filterContent, filterSubmitLabel, modelClass } = this.props
|
|
96
|
+
var { qParams, query, result, models } = this.state
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div className="content-container">
|
|
100
|
+
<EventCreated modelClass={modelClass} onCreated={() => this.onModelCreated()} />
|
|
101
|
+
|
|
102
|
+
{filterContent &&
|
|
103
|
+
<Card className="mb-4">
|
|
104
|
+
<form onSubmit={(e) => this.onFilterFormSubmit(e)} ref="filterForm">
|
|
105
|
+
{filterContent({qParams})}
|
|
106
|
+
<input className="btn btn-primary" label={filterSubmitLabel} type="submit" />
|
|
107
|
+
</form>
|
|
108
|
+
</Card>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
{models.map(model =>
|
|
112
|
+
<EventDestroyed key={`event-destroyed-${model.cacheKey()}`} model={model} onDestroyed={(args) => this.onModelDestroyed(args)} />
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
<Card className="mb-4" striped table>
|
|
116
|
+
<thead>
|
|
117
|
+
<tr>
|
|
118
|
+
{this.props.headersContent({query})}
|
|
119
|
+
</tr>
|
|
120
|
+
</thead>
|
|
121
|
+
<tbody>
|
|
122
|
+
{models.map(model =>
|
|
123
|
+
<tr className={`${inflection.singularize(modelClass.modelClassData().collectionName)}-row`} data-model-id={model.id()} key={model.cacheKey()}>
|
|
124
|
+
{this.props.columnsContent({model})}
|
|
125
|
+
</tr>
|
|
126
|
+
)}
|
|
127
|
+
</tbody>
|
|
128
|
+
</Card>
|
|
129
|
+
|
|
130
|
+
<Paginate result={result} />
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
className() {
|
|
136
|
+
var classNames = ["component-api-maker-live-table"]
|
|
137
|
+
|
|
138
|
+
if (this.props.className)
|
|
139
|
+
classNames.push(this.props.className)
|
|
140
|
+
|
|
141
|
+
return classNames.join(" ")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onFilterFormSubmit(e) {
|
|
145
|
+
e.preventDefault()
|
|
146
|
+
|
|
147
|
+
var qParams = Params.serializeForm(this.refs.filterForm)
|
|
148
|
+
var { queryQName } = this.state
|
|
149
|
+
|
|
150
|
+
var changeParamsParams = {}
|
|
151
|
+
changeParamsParams[queryQName] = qParams
|
|
152
|
+
|
|
153
|
+
Params.changeParams(changeParamsParams)
|
|
154
|
+
Params.setCachedParams(queryQName, qParams)
|
|
155
|
+
|
|
156
|
+
this.setState({currentHref: location.href, qParams}, () => this.loadModels())
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
onModelCreated() {
|
|
160
|
+
this.loadModels()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
onModelDestroyed(args) {
|
|
164
|
+
this.setState({
|
|
165
|
+
models: this.state.models.filter(model => model.id() != args.model.id())
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
}
|