@kaspernj/api-maker 1.0.216 → 1.0.217
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.
- package/package.json +2 -1
- package/src/base-model.mjs +1 -1
- package/src/bootstrap/attribute-row/basic-style.scss +9 -0
- package/src/bootstrap/attribute-row/index.jsx +84 -0
- package/src/bootstrap/attribute-rows.jsx +27 -0
- package/src/bootstrap/card.jsx +135 -0
- package/src/bootstrap/checkbox.jsx +79 -0
- package/src/bootstrap/checkboxes.jsx +122 -0
- package/src/bootstrap/index.js +0 -0
- package/src/bootstrap/input.jsx +160 -0
- package/src/bootstrap/invalid-feedback.jsx +31 -0
- package/src/bootstrap/live-table/model-row.jsx +150 -0
- package/src/bootstrap/live-table.jsx +399 -0
- package/src/bootstrap/paginate.jsx +153 -0
- package/src/bootstrap/radio-buttons.jsx +87 -0
- package/src/bootstrap/select.jsx +110 -0
- package/src/bootstrap/sort-link.jsx +102 -0
- package/src/collection-loader.jsx +7 -8
- package/src/inputs/auto-submit.mjs +37 -0
- package/src/inputs/checkbox.jsx +97 -0
- package/src/inputs/checkboxes.jsx +113 -0
- package/src/inputs/id-for-component.mjs +15 -0
- package/src/inputs/input-wrapper.jsx +170 -0
- package/src/inputs/input.jsx +235 -0
- package/src/inputs/money.jsx +177 -0
- package/src/inputs/name-for-component.mjs +15 -0
- package/src/inputs/select.jsx +87 -0
- package/src/model-class-require.mjs +1 -1
- package/src/model-name.mjs +1 -1
- package/src/super-admin/index.jsx +11 -0
- package/src/super-admin/layout/header/index.jsx +60 -0
- package/src/super-admin/layout/header/style.scss +124 -0
- package/src/super-admin/layout/index.jsx +156 -0
- package/src/super-admin/layout/menu/index.jsx +116 -0
- package/src/super-admin/layout/menu/menu-content.jsx +106 -0
- package/src/super-admin/layout/menu/menu-item/index.jsx +27 -0
- package/src/super-admin/layout/menu/menu-item/style.scss +30 -0
- package/src/super-admin/layout/menu/style.scss +103 -0
- package/src/super-admin/layout/style.scss +25 -0
- package/src/table/column-identifier.mjs +23 -0
- package/src/table/column-visible.mjs +7 -0
- package/src/table/model-row.jsx +182 -0
- package/src/table/select-calculator.mjs +48 -0
- package/src/table/style.scss +72 -0
- package/src/table/table-settings.js +175 -0
- package/src/table/table.jsx +498 -0
- package/src/table/variables.scss +11 -0
- package/src/table/with-breakpoint.jsx +48 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
@import "stylesheets/variables";
|
|
2
|
+
|
|
3
|
+
.components--admin--layout--menu {
|
|
4
|
+
position: fixed;
|
|
5
|
+
z-index: 9;
|
|
6
|
+
top: 0;
|
|
7
|
+
left: 0;
|
|
8
|
+
display: flex;
|
|
9
|
+
height: 100%;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
background: #1b1c1e;
|
|
12
|
+
color: #fff;
|
|
13
|
+
|
|
14
|
+
@media (max-width: $sm-to) {
|
|
15
|
+
width: 100%;
|
|
16
|
+
max-width: 250px;
|
|
17
|
+
max-height: 100vh;
|
|
18
|
+
overflow-y: auto;
|
|
19
|
+
|
|
20
|
+
&[data-triggered="false"] {
|
|
21
|
+
display: none;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@media (min-width: $md-from) {
|
|
26
|
+
width: 250px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@media (min-width: $lg-from) {
|
|
30
|
+
width: 290px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.menu-logo {
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
width: 80%;
|
|
36
|
+
margin-top: 25px;
|
|
37
|
+
margin-right: auto;
|
|
38
|
+
margin-left: auto;
|
|
39
|
+
font-size: 42px;
|
|
40
|
+
text-align: center;
|
|
41
|
+
text-overflow: ellipsis;
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.menu-logo-link {
|
|
46
|
+
&:link,
|
|
47
|
+
&:visited {
|
|
48
|
+
color: #dededf;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.menu-items-center {
|
|
54
|
+
margin-top: 25px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.menu-items-bottom {
|
|
58
|
+
margin-top: auto;
|
|
59
|
+
margin-bottom: 25px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.menu-user-section {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
margin-right: 25px;
|
|
66
|
+
margin-bottom: 25px;
|
|
67
|
+
margin-left: 25px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.menu-user-icon {
|
|
71
|
+
display: flex;
|
|
72
|
+
width: 44px;
|
|
73
|
+
min-width: 44px;
|
|
74
|
+
max-width: 44px;
|
|
75
|
+
height: 44px;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
background: #abbcd0;
|
|
79
|
+
border-radius: 50%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.menu-user-name {
|
|
83
|
+
flex-shrink: 1;
|
|
84
|
+
margin-left: 8px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.menu-user-name-container {
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
max-width: 140px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.menu-user-items {
|
|
93
|
+
position: relative;
|
|
94
|
+
margin-left: auto;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.menu-user-items-link {
|
|
98
|
+
&:link,
|
|
99
|
+
&:visited {
|
|
100
|
+
color: #dededf;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@import "stylesheets/variables";
|
|
2
|
+
|
|
3
|
+
.components--admin--layout {
|
|
4
|
+
width: 100%;
|
|
5
|
+
min-height: 100vh;
|
|
6
|
+
background: #fff;
|
|
7
|
+
color: #000;
|
|
8
|
+
|
|
9
|
+
.app-layout-content-container {
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
background: #f7f7f7;
|
|
12
|
+
|
|
13
|
+
@media (max-width: $sm-to) {
|
|
14
|
+
padding: 130px 30px 30px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@media (min-width: $md-from) {
|
|
18
|
+
padding: 130px 30px 30px 280px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@media (min-width: $lg-from) {
|
|
22
|
+
padding: 130px 30px 30px 320px;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default function columnIdentifier(column) {
|
|
2
|
+
if ("identifier" in column) return column.identifier
|
|
3
|
+
|
|
4
|
+
const parts = []
|
|
5
|
+
|
|
6
|
+
if ("path" in column) {
|
|
7
|
+
for (const pathPart of column.path) {
|
|
8
|
+
parts.push(pathPart)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if ("attribute" in column) {
|
|
13
|
+
parts.push(`attribute-${column.attribute}`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if ("sortKey" in column) {
|
|
17
|
+
parts.push(`sort-key-${column.sortKey}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (parts.length == 0) throw new Error(`Couldn't figure out the identifier for that column: ${JSON.stringify(column)}`)
|
|
21
|
+
|
|
22
|
+
return parts.join("--")
|
|
23
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import classNames from "classnames"
|
|
2
|
+
import columnIdentifier from "./column-identifier.mjs"
|
|
3
|
+
import columnVisible from "./column-visible.mjs"
|
|
4
|
+
import {digg, digs} from "diggerize"
|
|
5
|
+
import inflection from "inflection"
|
|
6
|
+
import Link from "../link"
|
|
7
|
+
import MoneyFormatter from "../money-formatter"
|
|
8
|
+
import PropTypes from "prop-types"
|
|
9
|
+
|
|
10
|
+
export default class ApiMakerBootStrapLiveTableModelRow extends React.PureComponent {
|
|
11
|
+
static propTypes = {
|
|
12
|
+
model: PropTypes.object.isRequired,
|
|
13
|
+
liveTable: PropTypes.object.isRequired,
|
|
14
|
+
preparedColumns: PropTypes.array
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
modelCallbackArgs = this._modelCallbackArgs()
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
const {model} = digs(this.props, "model")
|
|
21
|
+
const {modelClass} = digs(this.props.liveTable.props, "modelClass")
|
|
22
|
+
const {actionsContent, columnsContent, destroyEnabled, editModelPath, viewModelPath} = digg(this, "props", "liveTable", "props")
|
|
23
|
+
const {columns} = digg(this, "props", "liveTable", "shape")
|
|
24
|
+
|
|
25
|
+
let editPath, viewPath
|
|
26
|
+
|
|
27
|
+
if (editModelPath && model.can("edit")) editPath = editModelPath(this.modelCallbackArgs)
|
|
28
|
+
if (viewModelPath && model.can("show")) viewPath = viewModelPath(this.modelCallbackArgs)
|
|
29
|
+
|
|
30
|
+
const RowComponent = this.props.rowComponent
|
|
31
|
+
const ColumnComponent = this.props.columnComponent
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<RowComponent className={`live-table-row ${inflection.dasherize(modelClass.modelClassData().paramKey)}-row`} data-model-id={model.id()}>
|
|
35
|
+
{columns && this.columnsContentFromColumns(model)}
|
|
36
|
+
{!columns && columnsContent && columnsContent(this.modelCallbackArgs)}
|
|
37
|
+
<ColumnComponent className="actions-column">
|
|
38
|
+
{actionsContent && actionsContent(this.modelCallbackArgs)}
|
|
39
|
+
{viewPath &&
|
|
40
|
+
<Link className="view-button" to={viewPath}>
|
|
41
|
+
<i className="fa fa-search la la-search" />
|
|
42
|
+
</Link>
|
|
43
|
+
}
|
|
44
|
+
{editPath &&
|
|
45
|
+
<Link className="edit-button" to={editPath}>
|
|
46
|
+
<i className="fa fa-edit la la-edit" />
|
|
47
|
+
</Link>
|
|
48
|
+
}
|
|
49
|
+
{destroyEnabled && model.can("destroy") &&
|
|
50
|
+
<a className="destroy-button" href="#" onClick={this.onDestroyClicked}>
|
|
51
|
+
<i className="fa fa-trash la la-trash" />
|
|
52
|
+
</a>
|
|
53
|
+
}
|
|
54
|
+
</ColumnComponent>
|
|
55
|
+
</RowComponent>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
columnClassNamesForColumn (column) {
|
|
60
|
+
const classNames = ["live-table-column"]
|
|
61
|
+
|
|
62
|
+
if (column.commonProps && column.commonProps.className) classNames.push(column.commonProps.className)
|
|
63
|
+
if (column.columnProps && column.columnProps.className) classNames.push(column.columnProps.className)
|
|
64
|
+
|
|
65
|
+
return classNames
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
columnsContentFromColumns (model) {
|
|
69
|
+
const {preparedColumns} = digs(this.props, "preparedColumns")
|
|
70
|
+
const ColumnComponent = this.props.columnComponent
|
|
71
|
+
|
|
72
|
+
return preparedColumns?.map(({column, tableSettingColumn}) => columnVisible(column, tableSettingColumn) &&
|
|
73
|
+
<ColumnComponent
|
|
74
|
+
className={classNames(this.columnClassNamesForColumn(column))}
|
|
75
|
+
data-identifier={columnIdentifier(column)}
|
|
76
|
+
key={columnIdentifier(column)}
|
|
77
|
+
{...this.props.liveTable.columnProps(column)}
|
|
78
|
+
>
|
|
79
|
+
<div className="live-table-column-label">
|
|
80
|
+
{this.props.liveTable.headerLabelForColumn(column)}
|
|
81
|
+
</div>
|
|
82
|
+
<div className="live-table-column-value">
|
|
83
|
+
{column.content && this.columnContentFromContentArg(column, model)}
|
|
84
|
+
{!column.content && column.attribute && this.columnsContentFromAttributeAndPath(column, model)}
|
|
85
|
+
</div>
|
|
86
|
+
</ColumnComponent>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
columnContentFromContentArg (column, model) {
|
|
91
|
+
const value = column.content(this.modelCallbackArgs)
|
|
92
|
+
|
|
93
|
+
return this.presentColumnValue(value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
columnsContentFromAttributeAndPath (column, model) {
|
|
97
|
+
const {attribute} = digs(column, "attribute")
|
|
98
|
+
const currentModelClass = this.props.modelClass
|
|
99
|
+
const path = column.path || []
|
|
100
|
+
|
|
101
|
+
let currentModel = model
|
|
102
|
+
|
|
103
|
+
if (path.length > 0) {
|
|
104
|
+
for (const pathPart of path) {
|
|
105
|
+
currentModel = currentModel[pathPart]()
|
|
106
|
+
if (!currentModel) return
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!(attribute in currentModel)) throw new Error(`${currentModelClass.modelName().name} doesn't respond to ${attribute}`)
|
|
111
|
+
|
|
112
|
+
const value = currentModel[attribute]()
|
|
113
|
+
|
|
114
|
+
return this.presentColumnValue(value)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_modelCallbackArgs () {
|
|
118
|
+
const {model} = digs(this.props, "model")
|
|
119
|
+
const modelArgName = inflection.camelize(this.props.liveTable.props.modelClass.modelClassData().name, true)
|
|
120
|
+
const modelCallbackArgs = {}
|
|
121
|
+
|
|
122
|
+
modelCallbackArgs[modelArgName] = model
|
|
123
|
+
|
|
124
|
+
return modelCallbackArgs
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
onDestroyClicked = async (e) => {
|
|
128
|
+
e.preventDefault()
|
|
129
|
+
|
|
130
|
+
const {destroyMessage} = digg(this, "props", "liveTable", "props")
|
|
131
|
+
const {model} = digs(this.props, "model")
|
|
132
|
+
|
|
133
|
+
if (!confirm(I18n.t("js.shared.are_you_sure"))) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
await model.destroy()
|
|
139
|
+
|
|
140
|
+
if (destroyMessage) {
|
|
141
|
+
FlashMessage.success(destroyMessage)
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
FlashMessage.errorResponse(error)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
presentColumnValue (value) {
|
|
149
|
+
if (value instanceof Date) {
|
|
150
|
+
return this.presentDateTime(value)
|
|
151
|
+
} else if (MoneyFormatter.isMoney(value)) {
|
|
152
|
+
return MoneyFormatter.format(value)
|
|
153
|
+
} else if (typeof value == "boolean") {
|
|
154
|
+
if (value) return I18n.t("js.shared.yes")
|
|
155
|
+
|
|
156
|
+
return I18n.t("js.shared.no")
|
|
157
|
+
} else if (Array.isArray(value)) {
|
|
158
|
+
return value
|
|
159
|
+
.map((valuePart) => this.presentColumnValue(valuePart))
|
|
160
|
+
.filter((valuePart) => Boolean(valuePart))
|
|
161
|
+
.join(", ")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return value
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
presentDateTime(value) {
|
|
168
|
+
const apiMakerType = value.apiMakerType || "time"
|
|
169
|
+
|
|
170
|
+
if (apiMakerType == "time") {
|
|
171
|
+
const dateTimeFormatName = this.props.liveTable.props.defaultDateTimeFormatName || "time.formats.default"
|
|
172
|
+
|
|
173
|
+
return I18n.l(dateTimeFormatName, value)
|
|
174
|
+
} else if (apiMakerType == "date") {
|
|
175
|
+
const dateFormatName = this.props.liveTable.props.defaultDateTimeFormatName || "date.formats.default"
|
|
176
|
+
|
|
177
|
+
return I18n.l(dateFormatName, value)
|
|
178
|
+
} else {
|
|
179
|
+
throw new Error(`Unhandled type: ${apiMakerType}`)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {digg, digs} from "diggerize"
|
|
2
|
+
import inflection from "inflection"
|
|
3
|
+
import modelClassRequire from "../model-class-require.mjs"
|
|
4
|
+
|
|
5
|
+
class SelectCalculator {
|
|
6
|
+
constructor({table}) {
|
|
7
|
+
this.table = table
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
selects() {
|
|
11
|
+
const {modelClass} = digs(this.table.props, "modelClass")
|
|
12
|
+
const select = this.table.props.select || {}
|
|
13
|
+
const {preparedColumns} = digs(this.table.shape, "preparedColumns")
|
|
14
|
+
|
|
15
|
+
for (const preparedColumn of preparedColumns) {
|
|
16
|
+
const {column} = digs(preparedColumn, "column")
|
|
17
|
+
|
|
18
|
+
if (!column?.attribute) continue // 'column' might not exist if has been removed in code but still saved in DB
|
|
19
|
+
|
|
20
|
+
const {attribute} = digs(column, "attribute")
|
|
21
|
+
const {path} = column
|
|
22
|
+
|
|
23
|
+
let currentModelClass = modelClass
|
|
24
|
+
|
|
25
|
+
if (path) {
|
|
26
|
+
for (const pathPart of path) {
|
|
27
|
+
const relationships = digg(currentModelClass.modelClassData(), "relationships")
|
|
28
|
+
const relationship = relationships.find((relationshipInArray) => relationshipInArray.name == inflection.underscore(pathPart))
|
|
29
|
+
|
|
30
|
+
if (!relationship) throw new Error(`No such relationship: ${currentModelClass.modelClassData().name}#${pathPart}`)
|
|
31
|
+
|
|
32
|
+
currentModelClass = modelClassRequire(digg(relationship, "className"))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const currentModelClassName = digg(currentModelClass.modelClassData(), "name")
|
|
37
|
+
|
|
38
|
+
if (!(currentModelClassName in select)) select[currentModelClassName] = []
|
|
39
|
+
if (!select[currentModelClassName].includes(attribute)) select[currentModelClassName].push(attribute)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return select
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default function selectCalculator(...props) {
|
|
47
|
+
return new SelectCalculator(...props).selects()
|
|
48
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
@import "./variables";
|
|
2
|
+
|
|
3
|
+
.component-api-maker-live-table {
|
|
4
|
+
.live-table-header {
|
|
5
|
+
text-align: left;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@media (max-width: $sm-to) {
|
|
9
|
+
.live-table-column {
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
|
|
13
|
+
.live-table-column-value {
|
|
14
|
+
text-align: right;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.live-table-header-row {
|
|
19
|
+
margin-bottom: 15px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.live-table-row {
|
|
23
|
+
+ .live-table-row {
|
|
24
|
+
margin-top: 15px;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@media (min-width: $md-from) {
|
|
30
|
+
.actions-column {
|
|
31
|
+
text-align: right;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.live-table-header:not(:first-child),
|
|
35
|
+
.live-table-column:not(:first-child) {
|
|
36
|
+
padding-left: 10px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.live-table-header:not(:last-child),
|
|
40
|
+
.live-table-column:not(:last-child) {
|
|
41
|
+
padding-right: 10px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.live-table-column {
|
|
45
|
+
.live-table-column-label {
|
|
46
|
+
display: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&[data-text-align="center"] {
|
|
50
|
+
.live-table-column-value {
|
|
51
|
+
text-align: center;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&[data-text-align="right"] {
|
|
56
|
+
.live-table-column-value {
|
|
57
|
+
text-align: right;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.live-table-header {
|
|
63
|
+
&[data-text-align="center"] {
|
|
64
|
+
text-align: center;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&[data-text-align="right"] {
|
|
68
|
+
text-align: right;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import columnIdentifier from "./column-identifier.mjs"
|
|
2
|
+
import columnVisible from "./column-visible.mjs"
|
|
3
|
+
import {digg} from "diggerize"
|
|
4
|
+
import inflection from "inflection"
|
|
5
|
+
import {serialize as objectToFormData} from "object-to-formdata"
|
|
6
|
+
import {TableSetting} from "../models.mjs.erb"
|
|
7
|
+
|
|
8
|
+
export default class ApiMakerTableSettings {
|
|
9
|
+
constructor({table}) {
|
|
10
|
+
this.table = table
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
columns = () => digg(this, "table", "columnsAsArray")()
|
|
14
|
+
currentUser = () => digg(this, "table", "props", "currentUser")
|
|
15
|
+
identifier = () => digg(this, "table", "shape", "identifier")
|
|
16
|
+
|
|
17
|
+
preparedColumns = (tableSetting) => {
|
|
18
|
+
const columns = this.table.columnsAsArray()
|
|
19
|
+
const ordered = this.orderedTableSettingColumns(tableSetting)
|
|
20
|
+
const result = {
|
|
21
|
+
columns: [],
|
|
22
|
+
preload: []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!ordered) return
|
|
26
|
+
|
|
27
|
+
for (const tableSettingColumn of ordered) {
|
|
28
|
+
const column = columns.find((column) => columnIdentifier(column) == tableSettingColumn.identifier())
|
|
29
|
+
|
|
30
|
+
result.columns.push({column, tableSettingColumn})
|
|
31
|
+
|
|
32
|
+
// Add needed preloads if column is visible
|
|
33
|
+
if (columnVisible(column, tableSettingColumn)) {
|
|
34
|
+
if (column.path) {
|
|
35
|
+
const preload = column.path.map((pathPart) => inflection.underscore(pathPart)).join(".")
|
|
36
|
+
|
|
37
|
+
if (!result.preload.includes(preload)) result.preload.push(preload)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
orderedTableSettingColumns = (tableSetting) => {
|
|
46
|
+
return tableSetting
|
|
47
|
+
.columns()
|
|
48
|
+
.loaded()
|
|
49
|
+
.sort((tableSettingColumn1, tableSettingColumn2) => tableSettingColumn1.position() - tableSettingColumn2.position())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
loadExistingOrCreateTableSettings = async () => {
|
|
53
|
+
let tableSetting = await this.loadTableSetting()
|
|
54
|
+
|
|
55
|
+
if (tableSetting) {
|
|
56
|
+
tableSetting = await this.updateTableSetting(tableSetting)
|
|
57
|
+
} else {
|
|
58
|
+
tableSetting = await this.createInitialTableSetting()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return tableSetting
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
loadTableSetting = async () => {
|
|
65
|
+
if (!TableSetting) throw new Error("TableSetting model couldn't be imported")
|
|
66
|
+
|
|
67
|
+
const tableSetting = await TableSetting
|
|
68
|
+
.ransack({
|
|
69
|
+
identifier_eq: this.identifier(),
|
|
70
|
+
user_id_eq: this.currentUser().id(),
|
|
71
|
+
user_type_eq: digg(this.currentUser().modelClassData(), "name")
|
|
72
|
+
})
|
|
73
|
+
.preload("columns")
|
|
74
|
+
.first()
|
|
75
|
+
|
|
76
|
+
return tableSetting
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
createInitialTableSetting = async () => {
|
|
80
|
+
const tableSettingData = {
|
|
81
|
+
identifier: this.identifier(),
|
|
82
|
+
user_id: this.currentUser().id(),
|
|
83
|
+
user_type: digg(this.currentUser().modelClassData(), "name"),
|
|
84
|
+
columns_attributes: {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const columns = this.columns()
|
|
88
|
+
|
|
89
|
+
for (const columnKey in columns) {
|
|
90
|
+
const column = digg(columns, columnKey)
|
|
91
|
+
const identifier = columnIdentifier(column)
|
|
92
|
+
const columnData = this.columnSaveData(column, {identifier, position: columnKey})
|
|
93
|
+
|
|
94
|
+
tableSettingData.columns_attributes[columnKey] = columnData
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const tableSetting = new TableSetting()
|
|
98
|
+
const tableSettingFormData = objectToFormData({table_setting: tableSettingData})
|
|
99
|
+
|
|
100
|
+
await tableSetting.saveRaw(tableSettingFormData)
|
|
101
|
+
|
|
102
|
+
const reloadedTableSetting = await this.loadTableSetting()
|
|
103
|
+
|
|
104
|
+
return reloadedTableSetting
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
columnSaveData(column, {identifier, position}) {
|
|
108
|
+
return {
|
|
109
|
+
attribute_name: column.attribute,
|
|
110
|
+
identifier,
|
|
111
|
+
path: column.path,
|
|
112
|
+
position,
|
|
113
|
+
sort_key: column.sortKey,
|
|
114
|
+
visible: null
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
updateTableSetting = async (tableSetting) => {
|
|
119
|
+
// This should remove columns no longer found
|
|
120
|
+
// This should update columns that have changed
|
|
121
|
+
|
|
122
|
+
const columns = this.columns()
|
|
123
|
+
const columnsData = {}
|
|
124
|
+
const tableSettingData = {columns_attributes: columnsData}
|
|
125
|
+
let columnsKeyCount = 0
|
|
126
|
+
let changed = false
|
|
127
|
+
|
|
128
|
+
// Add missing columns
|
|
129
|
+
for (const column of columns) {
|
|
130
|
+
const identifier = columnIdentifier(column)
|
|
131
|
+
const tableSettingColumn = tableSetting.columns().loaded().find((tableSettingColumn) => tableSettingColumn.identifier() == identifier)
|
|
132
|
+
|
|
133
|
+
if (!tableSettingColumn) {
|
|
134
|
+
const columnKey = ++columnsKeyCount
|
|
135
|
+
|
|
136
|
+
columnsData[columnKey] = this.columnSaveData(
|
|
137
|
+
column,
|
|
138
|
+
{
|
|
139
|
+
identifier,
|
|
140
|
+
position: tableSetting.columns().loaded().length + columnKey
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
changed = true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const tableSettingColumn of tableSetting.columns().loaded()) {
|
|
149
|
+
const column = columns.find((column) => columnIdentifier(column) == tableSettingColumn.identifier())
|
|
150
|
+
|
|
151
|
+
if (column) {
|
|
152
|
+
// Update column if changed
|
|
153
|
+
} else {
|
|
154
|
+
// Removed saved columns no longer found
|
|
155
|
+
const columnKey = ++columnsKeyCount
|
|
156
|
+
|
|
157
|
+
columnsData[columnKey] = {
|
|
158
|
+
id: tableSettingColumn.id(),
|
|
159
|
+
_destroy: true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (changed) {
|
|
165
|
+
const tableSettingFormData = objectToFormData({table_setting: tableSettingData})
|
|
166
|
+
|
|
167
|
+
await tableSetting.saveRaw(tableSettingFormData)
|
|
168
|
+
|
|
169
|
+
// Maybe not necessary?
|
|
170
|
+
// tableSetting = this.loadTableSetting()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return tableSetting
|
|
174
|
+
}
|
|
175
|
+
}
|