admin_core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +53 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +3 -0
- data/README.md +48 -0
- data/Rakefile +31 -0
- data/admin_core.gemspec +32 -0
- data/client/.babelrc +19 -0
- data/client/.eslintignore +3 -0
- data/client/.eslintrc.yml +20 -0
- data/client/.flowconfig +7 -0
- data/client/.gitignore +64 -0
- data/client/README.md +3 -0
- data/client/admin-core.scss +8 -0
- data/client/flow-typed/npm/axios_v0.16.x.js +120 -0
- data/client/flow-typed/npm/classnames_v2.x.x.js +16 -0
- data/client/flow-typed/npm/lodash_v4.x.x.js +514 -0
- data/client/flow-typed/npm/react-router-dom_v4.x.x.js +166 -0
- data/client/flow-typed/npm/reactstrap_vx.x.x.js +536 -0
- data/client/package.json +60 -0
- data/client/src/.eslintrc.yml +23 -0
- data/client/src/AdminCore.jsx +44 -0
- data/client/src/components/Breadcrumb.jsx +18 -0
- data/client/src/components/Header.jsx +45 -0
- data/client/src/components/Pagination.jsx +72 -0
- data/client/src/components/ResourceFilters.jsx +87 -0
- data/client/src/components/ResourceForm.jsx +103 -0
- data/client/src/components/ResourcesCollection.jsx +41 -0
- data/client/src/components/Sidebar.jsx +90 -0
- data/client/src/decls.js +119 -0
- data/client/src/http-client.js +18 -0
- data/client/src/main.js +9 -0
- data/client/src/resource-field/BelongsTo.jsx +26 -0
- data/client/src/resource-field/Boolean.jsx +43 -0
- data/client/src/resource-field/Date.jsx +29 -0
- data/client/src/resource-field/DateTime.jsx +29 -0
- data/client/src/resource-field/Enum.jsx +34 -0
- data/client/src/resource-field/Number.jsx +28 -0
- data/client/src/resource-field/String.jsx +28 -0
- data/client/src/resource-field/Text.jsx +27 -0
- data/client/src/resource-field-renderer.js +45 -0
- data/client/src/resource-filter/Boolean.jsx +22 -0
- data/client/src/resource-filter/Number.jsx +45 -0
- data/client/src/resource-filter/String.jsx +46 -0
- data/client/src/resource-filter-renderer.js +17 -0
- data/client/src/resource-page/Base.js +36 -0
- data/client/src/resource-page/Edit.jsx +48 -0
- data/client/src/resource-page/Index.jsx +141 -0
- data/client/src/resource-page/New.jsx +48 -0
- data/client/src/resource-page/Show.jsx +116 -0
- data/client/webpack.config.js +26 -0
- data/client/yarn.lock +3816 -0
- data/lib/admin_core/base_controller.rb +114 -0
- data/lib/admin_core/base_resource_manager.rb +24 -0
- data/lib/admin_core/configuration.rb +20 -0
- data/lib/admin_core/engine.rb +6 -0
- data/lib/admin_core/errors.rb +17 -0
- data/lib/admin_core/resource_field/base.rb +69 -0
- data/lib/admin_core/resource_field/belongs_to.rb +38 -0
- data/lib/admin_core/resource_field/boolean.rb +18 -0
- data/lib/admin_core/resource_field/date.rb +18 -0
- data/lib/admin_core/resource_field/date_time.rb +18 -0
- data/lib/admin_core/resource_field/enum.rb +26 -0
- data/lib/admin_core/resource_field/number.rb +18 -0
- data/lib/admin_core/resource_field/string.rb +18 -0
- data/lib/admin_core/resource_field/text.rb +23 -0
- data/lib/admin_core/resource_field_builder.rb +48 -0
- data/lib/admin_core/resource_filter/base.rb +63 -0
- data/lib/admin_core/resource_filter/boolean.rb +17 -0
- data/lib/admin_core/resource_filter/number.rb +25 -0
- data/lib/admin_core/resource_filter/string.rb +27 -0
- data/lib/admin_core/resource_filter_builder.rb +37 -0
- data/lib/admin_core/resource_manager/buildable.rb +42 -0
- data/lib/admin_core/resource_manager/convert.rb +95 -0
- data/lib/admin_core/resource_manager/has_many_fields.rb +71 -0
- data/lib/admin_core/resource_manager/permission.rb +35 -0
- data/lib/admin_core/resource_manager/searchable.rb +57 -0
- data/lib/admin_core/resource_page/base.rb +17 -0
- data/lib/admin_core/resource_page/edit.rb +22 -0
- data/lib/admin_core/resource_page/index.rb +52 -0
- data/lib/admin_core/resource_page/new.rb +26 -0
- data/lib/admin_core/resource_page/show.rb +22 -0
- data/lib/admin_core/resource_router.rb +58 -0
- data/lib/admin_core/resource_search.rb +22 -0
- data/lib/admin_core/rspec/matchers.rb +18 -0
- data/lib/admin_core/rspec/resource_field_spec_helper.rb +54 -0
- data/lib/admin_core/version.rb +11 -0
- data/lib/admin_core/view_object/sidebar_dropdown.rb +21 -0
- data/lib/admin_core/view_object/sidebar_link.rb +24 -0
- data/lib/admin_core/view_object/sidebar_resource_link.rb +23 -0
- data/lib/admin_core/view_object/sidebar_title.rb +18 -0
- data/lib/admin_core.rb +69 -0
- data/lib/generators/admin_core/install_generator.rb +39 -0
- data/lib/generators/admin_core/resource_manager_generator.rb +166 -0
- data/lib/generators/admin_core/templates/admin-core.css +1 -0
- data/lib/generators/admin_core/templates/admin-core.js +38196 -0
- data/lib/generators/admin_core/templates/controller.rb.erb +4 -0
- data/lib/generators/admin_core/templates/initializer.rb.erb +3 -0
- data/lib/generators/admin_core/templates/resource_manager.rb.erb +33 -0
- data/lib/generators/admin_core/templates/view.html.erb +58 -0
- data/sample/.gitignore +21 -0
- data/sample/Gemfile +35 -0
- data/sample/Gemfile.lock +147 -0
- data/sample/README.md +24 -0
- data/sample/Rakefile +6 -0
- data/sample/app/assets/config/manifest.js +2 -0
- data/sample/app/assets/images/.keep +0 -0
- data/sample/app/assets/stylesheets/application.css +15 -0
- data/sample/app/controllers/admin/application_controller.rb +4 -0
- data/sample/app/controllers/admin/tweets_controller.rb +4 -0
- data/sample/app/controllers/admin/users_controller.rb +4 -0
- data/sample/app/controllers/application_controller.rb +3 -0
- data/sample/app/controllers/concerns/.keep +0 -0
- data/sample/app/helpers/application_helper.rb +2 -0
- data/sample/app/jobs/application_job.rb +2 -0
- data/sample/app/models/admin/tweet.rb +35 -0
- data/sample/app/models/admin/user.rb +41 -0
- data/sample/app/models/application_record.rb +3 -0
- data/sample/app/models/concerns/.keep +0 -0
- data/sample/app/models/tweet.rb +3 -0
- data/sample/app/models/user.rb +3 -0
- data/sample/app/views/admin/application.html.erb +64 -0
- data/sample/app/views/layouts/application.html.erb +13 -0
- data/sample/bin/bundle +3 -0
- data/sample/bin/rails +4 -0
- data/sample/bin/rake +4 -0
- data/sample/bin/setup +34 -0
- data/sample/bin/update +29 -0
- data/sample/config/application.rb +25 -0
- data/sample/config/boot.rb +3 -0
- data/sample/config/database.yml +25 -0
- data/sample/config/environment.rb +5 -0
- data/sample/config/environments/development.rb +42 -0
- data/sample/config/environments/production.rb +69 -0
- data/sample/config/environments/test.rb +36 -0
- data/sample/config/initializers/admin_core.rb +8 -0
- data/sample/config/initializers/application_controller_renderer.rb +6 -0
- data/sample/config/initializers/backtrace_silencers.rb +7 -0
- data/sample/config/initializers/cookies_serializer.rb +5 -0
- data/sample/config/initializers/filter_parameter_logging.rb +4 -0
- data/sample/config/initializers/inflections.rb +16 -0
- data/sample/config/initializers/mime_types.rb +4 -0
- data/sample/config/initializers/new_framework_defaults.rb +24 -0
- data/sample/config/initializers/session_store.rb +3 -0
- data/sample/config/initializers/wrap_parameters.rb +14 -0
- data/sample/config/locales/en.yml +23 -0
- data/sample/config/routes.rb +6 -0
- data/sample/config/secrets.yml +22 -0
- data/sample/config.ru +5 -0
- data/sample/db/migrate/20170417055257_create_users.rb +10 -0
- data/sample/db/migrate/20170417055412_create_tweets.rb +9 -0
- data/sample/db/schema.rb +31 -0
- data/sample/db/seeds.rb +7 -0
- data/sample/lib/assets/.keep +0 -0
- data/sample/lib/tasks/.keep +0 -0
- data/sample/log/.keep +0 -0
- data/sample/public/404.html +67 -0
- data/sample/public/422.html +67 -0
- data/sample/public/500.html +66 -0
- data/sample/public/apple-touch-icon-precomposed.png +0 -0
- data/sample/public/apple-touch-icon.png +0 -0
- data/sample/public/bundle.min.js +27 -0
- data/sample/public/bundle.min.js.map +1 -0
- data/sample/public/favicon.ico +0 -0
- data/sample/public/javascripts/admin-core.js +38196 -0
- data/sample/public/robots.txt +5 -0
- data/sample/public/stylesheets/admin-core.css +1 -0
- data/sample/tmp/.keep +0 -0
- data/sample/vendor/assets/stylesheets/.keep +0 -0
- metadata +368 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
// @flow
|
2
|
+
import React from "react";
|
3
|
+
import classNames from "classnames";
|
4
|
+
import omit from "lodash.omit";
|
5
|
+
import reduce from "lodash.reduce";
|
6
|
+
import toPairs from "lodash.topairs";
|
7
|
+
import {Link} from "react-router-dom";
|
8
|
+
import {ButtonGroup, Card, CardBlock, CardHeader, CardTitle} from "reactstrap";
|
9
|
+
|
10
|
+
import Base from "./Base";
|
11
|
+
import Breadcrumb from "../components/Breadcrumb";
|
12
|
+
import ResourceFilters from "../components/ResourceFilters";
|
13
|
+
import ResourcesCollection from "../components/ResourcesCollection";
|
14
|
+
import Pagination from "../components/Pagination";
|
15
|
+
import type {ResourceManager, ResourcePage$Index} from "../decls";
|
16
|
+
|
17
|
+
export default function index(resourceManager: ResourceManager) {
|
18
|
+
class IndexPage extends Base {
|
19
|
+
queries: { [string]: string; };
|
20
|
+
|
21
|
+
state: {
|
22
|
+
page?: ResourcePage$Index;
|
23
|
+
}
|
24
|
+
|
25
|
+
componentDidMount(...args: any) {
|
26
|
+
super.componentDidMount(...args);
|
27
|
+
this.setQueries();
|
28
|
+
}
|
29
|
+
|
30
|
+
componentDidUpdate(...args: any) {
|
31
|
+
super.componentDidUpdate(...args);
|
32
|
+
this.setQueries();
|
33
|
+
}
|
34
|
+
|
35
|
+
setQueries() {
|
36
|
+
this.queries = reduce(this.props.location.search.substring(1).split("&"), (acc, kv) => {
|
37
|
+
const [key, value] = kv.split("=");
|
38
|
+
if (key) {
|
39
|
+
acc[key] = value;
|
40
|
+
}
|
41
|
+
return acc;
|
42
|
+
}, {});
|
43
|
+
}
|
44
|
+
|
45
|
+
renderScopeButton(scope: { name: string; count: number; }, key: number) {
|
46
|
+
const match = scope.name === this.queries.scope;
|
47
|
+
const search = reduce(toPairs(omit(this.queries, "scope")), (acc, kv) => {
|
48
|
+
acc.push(kv.join("="));
|
49
|
+
return acc;
|
50
|
+
}, match ? [] : [`scope=${scope.name}`]).join("&");
|
51
|
+
return (
|
52
|
+
<Link
|
53
|
+
to={{
|
54
|
+
pathname: this.props.location.pathname,
|
55
|
+
search: search ? `?${search}` : "",
|
56
|
+
hash: this.props.location.hash,
|
57
|
+
}}
|
58
|
+
className={classNames("btn", { "btn-primary": match, "btn-secondary": !match })}
|
59
|
+
key={key}
|
60
|
+
>
|
61
|
+
{scope.name} ({scope.count})
|
62
|
+
</Link>
|
63
|
+
);
|
64
|
+
}
|
65
|
+
|
66
|
+
needFilter() {
|
67
|
+
const page = this.state.page;
|
68
|
+
return page && (page.scopes.length > 0 || page.filters.length > 0);
|
69
|
+
}
|
70
|
+
|
71
|
+
render() {
|
72
|
+
const page = this.state.page;
|
73
|
+
return (
|
74
|
+
<main className="main">
|
75
|
+
<Breadcrumb links={[["Home", "/"]]} current={resourceManager.displayName}/>
|
76
|
+
<div className="container-fluid">
|
77
|
+
<div className="animated fadeIn">
|
78
|
+
<div className="row">
|
79
|
+
{this.needFilter() &&
|
80
|
+
<div className="col-sm-12 col-lg-3 push-lg-9">
|
81
|
+
{page && page.scopes.length > 0 &&
|
82
|
+
<Card>
|
83
|
+
<CardHeader>
|
84
|
+
Scopes
|
85
|
+
</CardHeader>
|
86
|
+
<CardBlock>
|
87
|
+
<ButtonGroup vertical className="hidden-md-down d-block">
|
88
|
+
{page.scopes.map((scope, i) =>
|
89
|
+
this.renderScopeButton(scope, i)
|
90
|
+
)}
|
91
|
+
</ButtonGroup>
|
92
|
+
<ButtonGroup className="hidden-lg-up">
|
93
|
+
{page.scopes.map((scope, i) =>
|
94
|
+
this.renderScopeButton(scope, i)
|
95
|
+
)}
|
96
|
+
</ButtonGroup>
|
97
|
+
</CardBlock>
|
98
|
+
</Card>
|
99
|
+
}
|
100
|
+
{page && page.filters.length > 0 &&
|
101
|
+
<ResourceFilters location={this.props.location} filters={page.filters} />
|
102
|
+
}
|
103
|
+
</div>
|
104
|
+
}
|
105
|
+
{ page &&
|
106
|
+
<div className={classNames("col-sm-12", { "col-lg-9 pull-lg-3": this.needFilter() })}>
|
107
|
+
<Card>
|
108
|
+
<CardBlock>
|
109
|
+
<div className="row">
|
110
|
+
<div className="col-sm-5">
|
111
|
+
<CardTitle>{resourceManager.displayName}</CardTitle>
|
112
|
+
</div>
|
113
|
+
<div className="col-sm-7">
|
114
|
+
{resourceManager.newPath &&
|
115
|
+
<Link to={resourceManager.newPath} className="btn btn-primary float-right">
|
116
|
+
<i className="icon-plus" />
|
117
|
+
</Link>
|
118
|
+
}
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
<ResourcesCollection {...page} />
|
122
|
+
{page.pagination.total > 1 &&
|
123
|
+
<Pagination
|
124
|
+
{...page.pagination}
|
125
|
+
location={this.props.location}
|
126
|
+
/>
|
127
|
+
}
|
128
|
+
</CardBlock>
|
129
|
+
</Card>
|
130
|
+
</div>
|
131
|
+
}
|
132
|
+
</div>
|
133
|
+
</div>
|
134
|
+
</div>
|
135
|
+
</main>
|
136
|
+
);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
IndexPage.displayName = `IndexPage(${resourceManager.displayName})`;
|
140
|
+
return IndexPage;
|
141
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
// @flow
|
2
|
+
import React from "react";
|
3
|
+
|
4
|
+
import Base from "./Base";
|
5
|
+
import ResourceForm from "../components/ResourceForm";
|
6
|
+
import Breadcrumb from "../components/Breadcrumb";
|
7
|
+
import type {ResourcePage$New, ResourceManager} from "../decls";
|
8
|
+
|
9
|
+
export default function newPage(resourceManager: ResourceManager) {
|
10
|
+
class NewPage extends Base {
|
11
|
+
state: {
|
12
|
+
page?: ResourcePage$New;
|
13
|
+
}
|
14
|
+
|
15
|
+
render() {
|
16
|
+
const page = this.state.page;
|
17
|
+
return (
|
18
|
+
<main className="main">
|
19
|
+
<Breadcrumb links={[["Home", "/"], [resourceManager.displayName, resourceManager.indexPath]]} current="New" />
|
20
|
+
<div className="container-fluid">
|
21
|
+
<div className="animated fadeIn">
|
22
|
+
<div className="row">
|
23
|
+
{page &&
|
24
|
+
<div className="col-sm-12">
|
25
|
+
<div className="card">
|
26
|
+
<div className="card-block">
|
27
|
+
<h3 className="card-title">
|
28
|
+
New
|
29
|
+
</h3>
|
30
|
+
<ResourceForm
|
31
|
+
action={resourceManager.indexPath}
|
32
|
+
history={this.props.history}
|
33
|
+
resource={page.resource}
|
34
|
+
/>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
}
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</main>
|
43
|
+
);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
NewPage.displayName = `NewPage(${resourceManager.displayName})`;
|
47
|
+
return NewPage;
|
48
|
+
}
|
@@ -0,0 +1,116 @@
|
|
1
|
+
// @flow
|
2
|
+
import React from "react";
|
3
|
+
import {Link, Redirect} from "react-router-dom";
|
4
|
+
import {Modal, ModalBody, ModalFooter, ModalHeader} from "reactstrap";
|
5
|
+
|
6
|
+
import httpClient from "../http-client";
|
7
|
+
import Base from "./Base";
|
8
|
+
import Breadcrumb from "../components/Breadcrumb";
|
9
|
+
import type {ResourceManager, ResourcePage$Show} from "../decls";
|
10
|
+
import {renderShow} from "../resource-field-renderer";
|
11
|
+
|
12
|
+
export default function show(resourceManager: ResourceManager) {
|
13
|
+
class ShowPage extends Base {
|
14
|
+
state: {
|
15
|
+
confirm?: boolean;
|
16
|
+
page?: ResourcePage$Show;
|
17
|
+
redirectTo?: string;
|
18
|
+
}
|
19
|
+
|
20
|
+
toggleModal(e: SyntheticEvent) {
|
21
|
+
e.preventDefault();
|
22
|
+
this.setState({ confirm: !this.state.confirm });
|
23
|
+
}
|
24
|
+
|
25
|
+
deleteResource(e: SyntheticEvent) {
|
26
|
+
e.preventDefault();
|
27
|
+
const page = this.state.page;
|
28
|
+
if (page && page.resource.showPath) {
|
29
|
+
httpClient.delete(page.resource.showPath)
|
30
|
+
.then(r => this.setState({ redirectTo: r.data.redirectTo }));
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
renderPage(page: ResourcePage$Show) {
|
35
|
+
const resource = page.resource;
|
36
|
+
return (
|
37
|
+
<div className="col-sm-12">
|
38
|
+
<div className="card">
|
39
|
+
<div className="card-block">
|
40
|
+
<div className="row">
|
41
|
+
<div className="col-sm-5">
|
42
|
+
<h3 className="card-title">
|
43
|
+
{resource.displayName}
|
44
|
+
</h3>
|
45
|
+
</div>
|
46
|
+
<div className="col-sm-7">
|
47
|
+
{resource.destroyable && [
|
48
|
+
<a
|
49
|
+
href="#"
|
50
|
+
className="btn btn-outline-danger float-right"
|
51
|
+
onClick={this.toggleModal.bind(this)}
|
52
|
+
key="link"
|
53
|
+
>
|
54
|
+
<i className="icon-trash" />
|
55
|
+
</a>,
|
56
|
+
<Modal
|
57
|
+
isOpen={this.state.confirm}
|
58
|
+
toggle={this.toggleModal.bind(this)}
|
59
|
+
className="modal-danger"
|
60
|
+
key="modal"
|
61
|
+
>
|
62
|
+
<ModalHeader toggle={this.toggleModal.bind(this)}></ModalHeader>
|
63
|
+
<ModalBody>Are you sure?</ModalBody>
|
64
|
+
<ModalFooter>
|
65
|
+
<button className="btn btn-danger" onClick={this.deleteResource.bind(this)}>Delete</button>
|
66
|
+
<button className="btn btn-secondary" onClick={this.toggleModal.bind(this)}>Cancel</button>
|
67
|
+
</ModalFooter>
|
68
|
+
</Modal>
|
69
|
+
]}
|
70
|
+
{resource.editPath &&
|
71
|
+
<Link to={resource.editPath} className="btn btn-outline-primary float-right mr-1">
|
72
|
+
<i className="icon-pencil" />
|
73
|
+
</Link>
|
74
|
+
}
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
{resource.fields.map((field, i) =>
|
78
|
+
<div className="row" key={i}>
|
79
|
+
<div className="col-md-3">
|
80
|
+
<strong>
|
81
|
+
{field.displayName}
|
82
|
+
</strong>
|
83
|
+
</div>
|
84
|
+
<div className="col-md-9">
|
85
|
+
{renderShow(field)}
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
)}
|
89
|
+
</div>
|
90
|
+
</div>
|
91
|
+
</div>
|
92
|
+
);
|
93
|
+
}
|
94
|
+
|
95
|
+
render() {
|
96
|
+
if (this.state.redirectTo) {
|
97
|
+
return <Redirect to={this.state.redirectTo} />;
|
98
|
+
}
|
99
|
+
const page = this.state.page;
|
100
|
+
return (
|
101
|
+
<main className="main">
|
102
|
+
<Breadcrumb links={[["Home", "/"], [resourceManager.displayName, resourceManager.indexPath]]} current={page && page.resource.displayName}/>
|
103
|
+
<div className="container-fluid">
|
104
|
+
<div className="animated fadeIn">
|
105
|
+
<div className="row">
|
106
|
+
{page && this.renderPage(page)}
|
107
|
+
</div>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
</main>
|
111
|
+
);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
ShowPage.displayName = `ShowPage(${resourceManager.displayName})`;
|
115
|
+
return ShowPage;
|
116
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/* eslint-env node */
|
2
|
+
|
3
|
+
const path = require("path");
|
4
|
+
|
5
|
+
module.exports = {
|
6
|
+
devtool: "source-map",
|
7
|
+
entry: {
|
8
|
+
main: "./src/main.js",
|
9
|
+
},
|
10
|
+
resolve: {
|
11
|
+
extensions: [".js", ".jsx"]
|
12
|
+
},
|
13
|
+
module: {
|
14
|
+
rules: [
|
15
|
+
{
|
16
|
+
test: /\.jsx?$/,
|
17
|
+
exclude: /node_modules/,
|
18
|
+
use: ["babel-loader"],
|
19
|
+
},
|
20
|
+
],
|
21
|
+
},
|
22
|
+
output: {
|
23
|
+
path: path.join(__dirname, "dist"),
|
24
|
+
filename: "admin-core.js",
|
25
|
+
},
|
26
|
+
};
|