@kaspernj/api-maker 1.0.214 → 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 +5 -4
- 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,60 @@
|
|
|
1
|
+
import "./style"
|
|
2
|
+
|
|
3
|
+
export default class ApiMakerSuperAdminLayoutHeader extends BaseComponent {
|
|
4
|
+
static propTypes = PropTypesExact({
|
|
5
|
+
actions: PropTypes.node,
|
|
6
|
+
onTriggerMenu: PropTypes.func.isRequired,
|
|
7
|
+
title: PropTypes.string
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
headerActionsRef = React.createRef()
|
|
11
|
+
shape = new Shape(this, {headerActionsActive: false})
|
|
12
|
+
|
|
13
|
+
render() {
|
|
14
|
+
const {headerActionsRef} = digs(this, "headerActionsRef")
|
|
15
|
+
const {onGearsClicked} = digs(this, "onGearsClicked")
|
|
16
|
+
const {actions, onTriggerMenu, title} = this.props
|
|
17
|
+
const {headerActionsActive} = digs(this.shape, "headerActionsActive")
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="components--admin--layout--header">
|
|
21
|
+
<EventListener event="mouseup" onCalled={digg(this, "onWindowMouseUp")} target={window} />
|
|
22
|
+
<div className="header-title-container">
|
|
23
|
+
{title}
|
|
24
|
+
</div>
|
|
25
|
+
{actions &&
|
|
26
|
+
<div className="header-actions-container" data-active={headerActionsActive}>
|
|
27
|
+
<div className="header-actions" ref={headerActionsRef}>
|
|
28
|
+
{actions}
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
}
|
|
32
|
+
<div className="burger-menu-container">
|
|
33
|
+
{actions &&
|
|
34
|
+
<a className="actions-link" href="#" onClick={onGearsClicked}>
|
|
35
|
+
<i className="fa fa-gear" />
|
|
36
|
+
</a>
|
|
37
|
+
}
|
|
38
|
+
<a className="burger-menu-link" href="#" onClick={onTriggerMenu}>
|
|
39
|
+
<i className="fa fa-bars" />
|
|
40
|
+
</a>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onGearsClicked = (e) => {
|
|
47
|
+
e.preventDefault()
|
|
48
|
+
this.shape.set({
|
|
49
|
+
headerActionsActive: !this.shape.headerActionsActive
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onWindowMouseUp = (e) => {
|
|
54
|
+
const {headerActionsRef} = digs(this, "headerActionsRef")
|
|
55
|
+
const {headerActionsActive} = digs(this.shape, "headerActionsActive")
|
|
56
|
+
|
|
57
|
+
// Close the header actions menu if clicked happened outside
|
|
58
|
+
if (headerActionsActive && headerActionsRef.current && !headerActionsRef.current.contains(e.target)) this.shape.set({headerActionsActive: false})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
@import "stylesheets/variables";
|
|
2
|
+
|
|
3
|
+
.components--admin--layout--header {
|
|
4
|
+
top: 0;
|
|
5
|
+
display: flex;
|
|
6
|
+
height: 100px;
|
|
7
|
+
align-items: center;
|
|
8
|
+
padding-right: 30px;
|
|
9
|
+
padding-left: 30px;
|
|
10
|
+
background: #fff;
|
|
11
|
+
color: #282a33;
|
|
12
|
+
|
|
13
|
+
@media (max-width: $sm-to) {
|
|
14
|
+
position: absolute;
|
|
15
|
+
width: 100%;
|
|
16
|
+
|
|
17
|
+
.header-actions-container {
|
|
18
|
+
position: fixed;
|
|
19
|
+
top: 0;
|
|
20
|
+
left: 0;
|
|
21
|
+
|
|
22
|
+
display: flex;
|
|
23
|
+
width: 100vw;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
|
|
28
|
+
background: rgba(#000, .8);
|
|
29
|
+
|
|
30
|
+
&[data-active="false"] {
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.header-actions {
|
|
36
|
+
min-width: 80vw;
|
|
37
|
+
max-width: 100vw;
|
|
38
|
+
background: #fff;
|
|
39
|
+
|
|
40
|
+
.action-button {
|
|
41
|
+
display: block;
|
|
42
|
+
padding: 11px;
|
|
43
|
+
|
|
44
|
+
.fa {
|
|
45
|
+
margin-right: 5px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
+ .action-button {
|
|
49
|
+
border-top: 1px solid #c9c9c9;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:link,
|
|
53
|
+
&:visited {
|
|
54
|
+
color: #000;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&:active,
|
|
58
|
+
&:hover {
|
|
59
|
+
background: #dddcf0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (min-width: $md-from) {
|
|
66
|
+
position: fixed;
|
|
67
|
+
left: 250px;
|
|
68
|
+
width: calc(100% - 250px);
|
|
69
|
+
|
|
70
|
+
.header-actions-container {
|
|
71
|
+
margin-left: auto;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.header-actions {
|
|
75
|
+
.action-button {
|
|
76
|
+
display: inline-block;
|
|
77
|
+
padding: 7px 10px;
|
|
78
|
+
border: 1px solid #cbd5e1;
|
|
79
|
+
margin-right: 4px;
|
|
80
|
+
margin-bottom: 4px;
|
|
81
|
+
border-radius: 5px;
|
|
82
|
+
font-size: 13px;
|
|
83
|
+
|
|
84
|
+
&:link,
|
|
85
|
+
&:visited {
|
|
86
|
+
color: #000;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media (min-width: $lg-from) {
|
|
93
|
+
left: 290px;
|
|
94
|
+
width: calc(100% - 290px);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.burger-menu-container {
|
|
98
|
+
@media (max-width: $sm-to) {
|
|
99
|
+
margin-left: auto;
|
|
100
|
+
font-size: 28px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (min-width: $md-from) {
|
|
104
|
+
display: none;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.actions-link {
|
|
109
|
+
margin-right: 8px;
|
|
110
|
+
font-size: 22px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.actions-link,
|
|
114
|
+
.burger-menu-link {
|
|
115
|
+
&:link,
|
|
116
|
+
&:visited {
|
|
117
|
+
color: #1b1c1e;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.header-title-container {
|
|
122
|
+
font-size: 22px;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import "./style"
|
|
2
|
+
import CommandsPool from "@kaspernj/api-maker/src/commands-pool"
|
|
3
|
+
import Header from "./header"
|
|
4
|
+
import Menu from "./menu"
|
|
5
|
+
|
|
6
|
+
const UsersSignIn = React.lazy(() => import("components/users/sign-in"))
|
|
7
|
+
const NoAccess = React.lazy(() => import("./no-access"))
|
|
8
|
+
|
|
9
|
+
class ApiMakerSuperAdminLayout extends React.PureComponent {
|
|
10
|
+
static defaultProps = {
|
|
11
|
+
requireAdmin: true
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static propTypes = PropTypesExact({
|
|
15
|
+
actions: PropTypes.node,
|
|
16
|
+
active: PropTypes.string,
|
|
17
|
+
children: PropTypes.node,
|
|
18
|
+
className: PropTypes.string,
|
|
19
|
+
currentCustomer: PropTypes.instanceOf(User),
|
|
20
|
+
currentCustomerId: PropTypes.string,
|
|
21
|
+
currentUser: PropTypes.instanceOf(User),
|
|
22
|
+
headTitle: PropTypes.string,
|
|
23
|
+
headerTitle: PropTypes.string,
|
|
24
|
+
requireAdmin: PropTypes.bool.isRequired
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
componentDidMount() {
|
|
28
|
+
CommandsPool.current().globalRequestData.layout = "admin"
|
|
29
|
+
CommandsPool.current().globalRequestData.locale = I18n.locale
|
|
30
|
+
|
|
31
|
+
this.setDocumentTitle()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
componentDidUpdate() {
|
|
35
|
+
this.setDocumentTitle()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setDocumentTitle() {
|
|
39
|
+
const headTitle = this.props.headTitle || this.props.headerTitle
|
|
40
|
+
|
|
41
|
+
if (headTitle) {
|
|
42
|
+
document.title = headTitle
|
|
43
|
+
} else {
|
|
44
|
+
document.title = "Wooftech"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
shape = new Shape(this, {
|
|
49
|
+
menuTriggered: false
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
render() {
|
|
53
|
+
const {
|
|
54
|
+
actions,
|
|
55
|
+
active,
|
|
56
|
+
children,
|
|
57
|
+
className,
|
|
58
|
+
currentCustomer,
|
|
59
|
+
currentCustomerId,
|
|
60
|
+
currentUser,
|
|
61
|
+
headerTitle,
|
|
62
|
+
menu,
|
|
63
|
+
requireAdmin,
|
|
64
|
+
...restProps
|
|
65
|
+
} = this.props
|
|
66
|
+
const {menuTriggered} = digs(this.shape, "menuTriggered")
|
|
67
|
+
const noAccess = this.noAccess()
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className={classNames("components--admin--layout", className)} data-menu-triggered={menuTriggered} {...restProps}>
|
|
71
|
+
<Menu
|
|
72
|
+
active={active}
|
|
73
|
+
noAccess={noAccess}
|
|
74
|
+
onRequestMenuClose={digg(this, "onRequestMenuClose")}
|
|
75
|
+
triggered={menuTriggered}
|
|
76
|
+
/>
|
|
77
|
+
<Header actions={actions} onTriggerMenu={digg(this, "onTriggerMenu")} title={headerTitle} />
|
|
78
|
+
<div className="app-layout-content-container">
|
|
79
|
+
{noAccess &&
|
|
80
|
+
<>
|
|
81
|
+
<NoAccess />
|
|
82
|
+
{currentUser &&
|
|
83
|
+
<>
|
|
84
|
+
<div className="mb-4">
|
|
85
|
+
{I18n.t("js.components.app_layout.try_signing_out_and_in_with_a_different_user")}
|
|
86
|
+
</div>
|
|
87
|
+
{(isCurrentUserA("teacher") || isCurrentUserA("student")) &&
|
|
88
|
+
<div className="mb-4">
|
|
89
|
+
{this.clickHereToAccessTheUserUniverse()}
|
|
90
|
+
</div>
|
|
91
|
+
}
|
|
92
|
+
</>
|
|
93
|
+
}
|
|
94
|
+
{!currentUser &&
|
|
95
|
+
<>
|
|
96
|
+
<div className="mb-4">
|
|
97
|
+
{I18n.t("js.components.app_layout.try_signing_in")}
|
|
98
|
+
</div>
|
|
99
|
+
<UsersSignIn />
|
|
100
|
+
</>
|
|
101
|
+
}
|
|
102
|
+
</>
|
|
103
|
+
}
|
|
104
|
+
{!noAccess && children}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
clickHereToAccessTheUserUniverse() {
|
|
111
|
+
const replaces = [
|
|
112
|
+
{
|
|
113
|
+
component: (
|
|
114
|
+
<Link key="here-user-universe-link" to={Routes.userRootPath()}>
|
|
115
|
+
{I18n.t("js.components.app_layout.here")}
|
|
116
|
+
</Link>
|
|
117
|
+
),
|
|
118
|
+
text: "%{here}"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
component: (
|
|
122
|
+
<Link key="user-universe-link" to={Routes.userRootPath()}>
|
|
123
|
+
{I18n.t("js.components.app_layout.user_universe")}
|
|
124
|
+
</Link>
|
|
125
|
+
),
|
|
126
|
+
text: "%{user_universe}"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<TextComponentReplace
|
|
132
|
+
replaces={replaces}
|
|
133
|
+
text={I18n.t("js.components.app_layout.click_here_to_access_the_user_universe")}
|
|
134
|
+
/>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
onRequestMenuClose = () => this.shape.set({menuTriggered: false})
|
|
139
|
+
|
|
140
|
+
onTriggerMenu = (e) => {
|
|
141
|
+
e.preventDefault()
|
|
142
|
+
|
|
143
|
+
this.shape.set({menuTriggered: !this.shape.menuTriggered})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
noAccess() {
|
|
147
|
+
const {currentUser, requireAdmin} = digs(this.props, "currentUser", "requireAdmin")
|
|
148
|
+
|
|
149
|
+
if (requireAdmin && currentUser && !isCurrentUserA("admin") && !isCurrentUserA("hacker")) return true
|
|
150
|
+
if (requireAdmin && !currentUser) return true
|
|
151
|
+
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default withCurrentUser(ApiMakerSuperAdminLayout)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import "./style"
|
|
2
|
+
import {PopupMenu, PopupMenuItem} from "components/popup-menu"
|
|
3
|
+
import MenuContent from "./menu-content"
|
|
4
|
+
import MenuItem from "./menu-item"
|
|
5
|
+
|
|
6
|
+
class ComponentsAdminLayoutMenu extends BaseComponent {
|
|
7
|
+
static propTypes = PropTypesExact({
|
|
8
|
+
active: PropTypes.string,
|
|
9
|
+
currentUser: PropTypes.instanceOf(User),
|
|
10
|
+
noAccess: PropTypes.bool.isRequired,
|
|
11
|
+
onRequestMenuClose: PropTypes.func.isRequired,
|
|
12
|
+
triggered: PropTypes.bool.isRequired
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
menuUserItemsRef = React.createRef()
|
|
16
|
+
rootRef = React.createRef()
|
|
17
|
+
shape = new Shape(this, {userMenuItemOpen: false})
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
const {menuUserItemsRef, onUserItemsClicked, rootRef} = digs(this, "menuUserItemsRef", "onUserItemsClicked", "rootRef")
|
|
21
|
+
const {active} = this.props
|
|
22
|
+
const {
|
|
23
|
+
currentUser,
|
|
24
|
+
noAccess,
|
|
25
|
+
triggered
|
|
26
|
+
} = digs(
|
|
27
|
+
this.props,
|
|
28
|
+
"currentUser",
|
|
29
|
+
"noAccess",
|
|
30
|
+
"triggered"
|
|
31
|
+
)
|
|
32
|
+
const {userMenuItemOpen} = digs(this.shape, "userMenuItemOpen")
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="components--admin--layout--menu" data-triggered={triggered} ref={rootRef}>
|
|
36
|
+
<EventListener event="mouseup" onCalled={digg(this, "onWindowMouseUp")} target={window} />
|
|
37
|
+
<div className="menu-logo">
|
|
38
|
+
<Link className="menu-logo-link" to={Routes.adminRootPath()}>
|
|
39
|
+
Admin
|
|
40
|
+
</Link>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="menu-items-center">
|
|
43
|
+
{!noAccess &&
|
|
44
|
+
<MenuContent active={active} />
|
|
45
|
+
}
|
|
46
|
+
</div>
|
|
47
|
+
<div className="menu-items-bottom">
|
|
48
|
+
{currentUser &&
|
|
49
|
+
<div className="menu-user-section">
|
|
50
|
+
<div className="menu-user-icon">
|
|
51
|
+
<i className="fa fa-user" />
|
|
52
|
+
</div>
|
|
53
|
+
<div className="menu-user-name">
|
|
54
|
+
<div className="menu-user-name-container">
|
|
55
|
+
{currentUser.name()}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
<div className="menu-user-items" ref={menuUserItemsRef}>
|
|
59
|
+
{userMenuItemOpen &&
|
|
60
|
+
<PopupMenu>
|
|
61
|
+
<PopupMenuItem
|
|
62
|
+
children={I18n.t("js.components.app_layout.menu.notification_settings")}
|
|
63
|
+
className="notifications-settings-menu-item"
|
|
64
|
+
to="#"
|
|
65
|
+
/>
|
|
66
|
+
</PopupMenu>
|
|
67
|
+
}
|
|
68
|
+
<a className="menu-user-items-link" href="#" onClick={onUserItemsClicked}>
|
|
69
|
+
<i className="fa fa-ellipsis" />
|
|
70
|
+
</a>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
}
|
|
74
|
+
{currentUser &&
|
|
75
|
+
<MenuItem
|
|
76
|
+
active
|
|
77
|
+
className="sign-out-menu-item"
|
|
78
|
+
icon="sign-out-alt"
|
|
79
|
+
label={I18n.t("js.components.admin.layout.menu.sign_out")}
|
|
80
|
+
onClick={digg(this, "onSignOutClicked")}
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onSignOutClicked = async (e) => {
|
|
89
|
+
e.preventDefault()
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await Devise.signOut()
|
|
93
|
+
FlashMessage.success(I18n.t("js.components.admin.layout.menu.you_have_been_signed_out"))
|
|
94
|
+
} catch (error) {
|
|
95
|
+
FlashMessage.errorResponse(error)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
onUserItemsClicked = (e) => {
|
|
100
|
+
e.preventDefault()
|
|
101
|
+
this.shape.set({userMenuItemOpen: !this.shape.userMenuItemOpen})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onWindowMouseUp = (e) => {
|
|
105
|
+
const {menuUserItemsRef, rootRef} = digs(this, "menuUserItemsRef", "rootRef")
|
|
106
|
+
const {triggered} = digs(this.props, "triggered")
|
|
107
|
+
|
|
108
|
+
// Close the menu if triggered (menu is open on mobile)
|
|
109
|
+
if (triggered && !rootRef.current.contains(e.target)) setTimeout(this.props.onRequestMenuClose)
|
|
110
|
+
|
|
111
|
+
// Close the user items menu if clicked happened outside of that
|
|
112
|
+
if (!menuUserItemsRef?.current?.contains(e.target)) this.shape.set({userMenuItemOpen: false})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default withCurrentUser(ComponentsAdminLayoutMenu)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import CanCanLoader from "@kaspernj/api-maker/src/can-can-loader"
|
|
2
|
+
import MenuItem from "components/admin/layout/menu/menu-item"
|
|
3
|
+
|
|
4
|
+
const abilities = [
|
|
5
|
+
[CheckIn, ["index"]],
|
|
6
|
+
[ClassStep, ["index"]],
|
|
7
|
+
[ScoreFactor, ["index"]],
|
|
8
|
+
[ScoreFactorGroup, ["index"]],
|
|
9
|
+
[School, ["index"]],
|
|
10
|
+
[SchoolClass, ["index"]],
|
|
11
|
+
[Survey, ["index"]],
|
|
12
|
+
[User, ["index"]]
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
export default class ComponentsAdminLayoutMenuContent extends BaseComponent {
|
|
16
|
+
static propTypes = PropTypesExact({
|
|
17
|
+
active: PropTypes.string
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
shape = new Shape(this, {
|
|
21
|
+
canCan: undefined
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
render() {
|
|
25
|
+
const {active} = digs(this.props, "active")
|
|
26
|
+
const {canCan} = digs(this.shape, "canCan")
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
<CanCanLoader abilities={abilities} component={this} />
|
|
31
|
+
{canCan?.can("index", CheckIn) &&
|
|
32
|
+
<MenuItem
|
|
33
|
+
active={active}
|
|
34
|
+
icon="sitemap"
|
|
35
|
+
identifier="check-ins"
|
|
36
|
+
label={CheckIn.modelName().human({count: 2})}
|
|
37
|
+
to={Routes.adminCheckInsPath()}
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
40
|
+
{canCan?.can("index", ClassStep) &&
|
|
41
|
+
<MenuItem
|
|
42
|
+
active={active}
|
|
43
|
+
icon="sitemap"
|
|
44
|
+
identifier="class-steps"
|
|
45
|
+
label={ClassStep.modelName().human({count: 2})}
|
|
46
|
+
to={Routes.adminClassStepsPath()}
|
|
47
|
+
/>
|
|
48
|
+
}
|
|
49
|
+
{canCan?.can("index", School) &&
|
|
50
|
+
<MenuItem
|
|
51
|
+
active={active}
|
|
52
|
+
icon="sitemap"
|
|
53
|
+
identifier="schools"
|
|
54
|
+
label={School.modelName().human({count: 2})}
|
|
55
|
+
to={Routes.adminSchoolsPath()}
|
|
56
|
+
/>
|
|
57
|
+
}
|
|
58
|
+
{canCan?.can("index", SchoolClass) &&
|
|
59
|
+
<MenuItem
|
|
60
|
+
active={active}
|
|
61
|
+
icon="sitemap"
|
|
62
|
+
identifier="school-classes"
|
|
63
|
+
label={SchoolClass.modelName().human({count: 2})}
|
|
64
|
+
to={Routes.adminSchoolClassesPath()}
|
|
65
|
+
/>
|
|
66
|
+
}
|
|
67
|
+
{canCan?.can("index", ScoreFactor) &&
|
|
68
|
+
<MenuItem
|
|
69
|
+
active={active}
|
|
70
|
+
icon="list-ol"
|
|
71
|
+
identifier="score-factors"
|
|
72
|
+
label={ScoreFactor.modelName().human({count: 2})}
|
|
73
|
+
to={Routes.adminScoreFactorsPath()}
|
|
74
|
+
/>
|
|
75
|
+
}
|
|
76
|
+
{canCan?.can("index", ScoreFactorGroup) &&
|
|
77
|
+
<MenuItem
|
|
78
|
+
active={active}
|
|
79
|
+
icon="list-ol"
|
|
80
|
+
identifier="score-factor-groups"
|
|
81
|
+
label={ScoreFactorGroup.modelName().human({count: 2})}
|
|
82
|
+
to={Routes.adminScoreFactorGroupsPath()}
|
|
83
|
+
/>
|
|
84
|
+
}
|
|
85
|
+
{canCan?.can("index", Survey) &&
|
|
86
|
+
<MenuItem
|
|
87
|
+
active={active}
|
|
88
|
+
icon="list-ol"
|
|
89
|
+
identifier="surveys"
|
|
90
|
+
label={Survey.modelName().human({count: 2})}
|
|
91
|
+
to={Routes.adminSurveysPath()}
|
|
92
|
+
/>
|
|
93
|
+
}
|
|
94
|
+
{canCan?.can("index", User) &&
|
|
95
|
+
<MenuItem
|
|
96
|
+
active={active}
|
|
97
|
+
icon="users"
|
|
98
|
+
identifier="users"
|
|
99
|
+
label={User.modelName().human({count: 2})}
|
|
100
|
+
to={Routes.adminUsersPath()}
|
|
101
|
+
/>
|
|
102
|
+
}
|
|
103
|
+
</>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import "./style"
|
|
2
|
+
|
|
3
|
+
export default class ComponentsAdminLayoutMenuMenuItem extends BaseComponent {
|
|
4
|
+
static propTypes = {
|
|
5
|
+
active: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
|
6
|
+
className: PropTypes.string,
|
|
7
|
+
icon: PropTypes.string.isRequired,
|
|
8
|
+
label: PropTypes.node
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
const {active, children, className, icon, identifier, label, to, ...restProps} = this.props
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Link
|
|
16
|
+
className={classNames("components--admin--layout--menu--menu-item", className)}
|
|
17
|
+
data-active={active === true || active == identifier}
|
|
18
|
+
data-identifier={identifier}
|
|
19
|
+
to={to || "#"}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
<i className={`fa fa-fw fa-${icon} menu-item-icon`} />
|
|
23
|
+
{children || label}
|
|
24
|
+
</Link>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.components--admin--layout--menu--menu-item {
|
|
2
|
+
display: flex;
|
|
3
|
+
width: 80%;
|
|
4
|
+
align-items: center;
|
|
5
|
+
padding: 10px 14px;
|
|
6
|
+
margin-right: auto;
|
|
7
|
+
margin-left: auto;
|
|
8
|
+
text-decoration: none;
|
|
9
|
+
|
|
10
|
+
&:link,
|
|
11
|
+
&:visited {
|
|
12
|
+
color: #6f6f71;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&[data-active="true"],
|
|
16
|
+
&:hover {
|
|
17
|
+
background: #323435;
|
|
18
|
+
border-radius: 7px;
|
|
19
|
+
color: #b9b9bb;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.menu-item-icon {
|
|
23
|
+
margin-right: 4px;
|
|
24
|
+
font-size: 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
+ .components--admin--layout--menu--menu-item {
|
|
28
|
+
margin-top: 6px;
|
|
29
|
+
}
|
|
30
|
+
}
|