admin_core 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.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +53 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +27 -0
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +3 -0
  8. data/README.md +48 -0
  9. data/Rakefile +31 -0
  10. data/admin_core.gemspec +32 -0
  11. data/client/.babelrc +19 -0
  12. data/client/.eslintignore +3 -0
  13. data/client/.eslintrc.yml +20 -0
  14. data/client/.flowconfig +7 -0
  15. data/client/.gitignore +64 -0
  16. data/client/README.md +3 -0
  17. data/client/admin-core.scss +8 -0
  18. data/client/flow-typed/npm/axios_v0.16.x.js +120 -0
  19. data/client/flow-typed/npm/classnames_v2.x.x.js +16 -0
  20. data/client/flow-typed/npm/lodash_v4.x.x.js +514 -0
  21. data/client/flow-typed/npm/react-router-dom_v4.x.x.js +166 -0
  22. data/client/flow-typed/npm/reactstrap_vx.x.x.js +536 -0
  23. data/client/package.json +60 -0
  24. data/client/src/.eslintrc.yml +23 -0
  25. data/client/src/AdminCore.jsx +44 -0
  26. data/client/src/components/Breadcrumb.jsx +18 -0
  27. data/client/src/components/Header.jsx +45 -0
  28. data/client/src/components/Pagination.jsx +72 -0
  29. data/client/src/components/ResourceFilters.jsx +87 -0
  30. data/client/src/components/ResourceForm.jsx +103 -0
  31. data/client/src/components/ResourcesCollection.jsx +41 -0
  32. data/client/src/components/Sidebar.jsx +90 -0
  33. data/client/src/decls.js +119 -0
  34. data/client/src/http-client.js +18 -0
  35. data/client/src/main.js +9 -0
  36. data/client/src/resource-field/BelongsTo.jsx +26 -0
  37. data/client/src/resource-field/Boolean.jsx +43 -0
  38. data/client/src/resource-field/Date.jsx +29 -0
  39. data/client/src/resource-field/DateTime.jsx +29 -0
  40. data/client/src/resource-field/Enum.jsx +34 -0
  41. data/client/src/resource-field/Number.jsx +28 -0
  42. data/client/src/resource-field/String.jsx +28 -0
  43. data/client/src/resource-field/Text.jsx +27 -0
  44. data/client/src/resource-field-renderer.js +45 -0
  45. data/client/src/resource-filter/Boolean.jsx +22 -0
  46. data/client/src/resource-filter/Number.jsx +45 -0
  47. data/client/src/resource-filter/String.jsx +46 -0
  48. data/client/src/resource-filter-renderer.js +17 -0
  49. data/client/src/resource-page/Base.js +36 -0
  50. data/client/src/resource-page/Edit.jsx +48 -0
  51. data/client/src/resource-page/Index.jsx +141 -0
  52. data/client/src/resource-page/New.jsx +48 -0
  53. data/client/src/resource-page/Show.jsx +116 -0
  54. data/client/webpack.config.js +26 -0
  55. data/client/yarn.lock +3816 -0
  56. data/lib/admin_core/base_controller.rb +114 -0
  57. data/lib/admin_core/base_resource_manager.rb +24 -0
  58. data/lib/admin_core/configuration.rb +20 -0
  59. data/lib/admin_core/engine.rb +6 -0
  60. data/lib/admin_core/errors.rb +17 -0
  61. data/lib/admin_core/resource_field/base.rb +69 -0
  62. data/lib/admin_core/resource_field/belongs_to.rb +38 -0
  63. data/lib/admin_core/resource_field/boolean.rb +18 -0
  64. data/lib/admin_core/resource_field/date.rb +18 -0
  65. data/lib/admin_core/resource_field/date_time.rb +18 -0
  66. data/lib/admin_core/resource_field/enum.rb +26 -0
  67. data/lib/admin_core/resource_field/number.rb +18 -0
  68. data/lib/admin_core/resource_field/string.rb +18 -0
  69. data/lib/admin_core/resource_field/text.rb +23 -0
  70. data/lib/admin_core/resource_field_builder.rb +48 -0
  71. data/lib/admin_core/resource_filter/base.rb +63 -0
  72. data/lib/admin_core/resource_filter/boolean.rb +17 -0
  73. data/lib/admin_core/resource_filter/number.rb +25 -0
  74. data/lib/admin_core/resource_filter/string.rb +27 -0
  75. data/lib/admin_core/resource_filter_builder.rb +37 -0
  76. data/lib/admin_core/resource_manager/buildable.rb +42 -0
  77. data/lib/admin_core/resource_manager/convert.rb +95 -0
  78. data/lib/admin_core/resource_manager/has_many_fields.rb +71 -0
  79. data/lib/admin_core/resource_manager/permission.rb +35 -0
  80. data/lib/admin_core/resource_manager/searchable.rb +57 -0
  81. data/lib/admin_core/resource_page/base.rb +17 -0
  82. data/lib/admin_core/resource_page/edit.rb +22 -0
  83. data/lib/admin_core/resource_page/index.rb +52 -0
  84. data/lib/admin_core/resource_page/new.rb +26 -0
  85. data/lib/admin_core/resource_page/show.rb +22 -0
  86. data/lib/admin_core/resource_router.rb +58 -0
  87. data/lib/admin_core/resource_search.rb +22 -0
  88. data/lib/admin_core/rspec/matchers.rb +18 -0
  89. data/lib/admin_core/rspec/resource_field_spec_helper.rb +54 -0
  90. data/lib/admin_core/version.rb +11 -0
  91. data/lib/admin_core/view_object/sidebar_dropdown.rb +21 -0
  92. data/lib/admin_core/view_object/sidebar_link.rb +24 -0
  93. data/lib/admin_core/view_object/sidebar_resource_link.rb +23 -0
  94. data/lib/admin_core/view_object/sidebar_title.rb +18 -0
  95. data/lib/admin_core.rb +69 -0
  96. data/lib/generators/admin_core/install_generator.rb +39 -0
  97. data/lib/generators/admin_core/resource_manager_generator.rb +166 -0
  98. data/lib/generators/admin_core/templates/admin-core.css +1 -0
  99. data/lib/generators/admin_core/templates/admin-core.js +38196 -0
  100. data/lib/generators/admin_core/templates/controller.rb.erb +4 -0
  101. data/lib/generators/admin_core/templates/initializer.rb.erb +3 -0
  102. data/lib/generators/admin_core/templates/resource_manager.rb.erb +33 -0
  103. data/lib/generators/admin_core/templates/view.html.erb +58 -0
  104. data/sample/.gitignore +21 -0
  105. data/sample/Gemfile +35 -0
  106. data/sample/Gemfile.lock +147 -0
  107. data/sample/README.md +24 -0
  108. data/sample/Rakefile +6 -0
  109. data/sample/app/assets/config/manifest.js +2 -0
  110. data/sample/app/assets/images/.keep +0 -0
  111. data/sample/app/assets/stylesheets/application.css +15 -0
  112. data/sample/app/controllers/admin/application_controller.rb +4 -0
  113. data/sample/app/controllers/admin/tweets_controller.rb +4 -0
  114. data/sample/app/controllers/admin/users_controller.rb +4 -0
  115. data/sample/app/controllers/application_controller.rb +3 -0
  116. data/sample/app/controllers/concerns/.keep +0 -0
  117. data/sample/app/helpers/application_helper.rb +2 -0
  118. data/sample/app/jobs/application_job.rb +2 -0
  119. data/sample/app/models/admin/tweet.rb +35 -0
  120. data/sample/app/models/admin/user.rb +41 -0
  121. data/sample/app/models/application_record.rb +3 -0
  122. data/sample/app/models/concerns/.keep +0 -0
  123. data/sample/app/models/tweet.rb +3 -0
  124. data/sample/app/models/user.rb +3 -0
  125. data/sample/app/views/admin/application.html.erb +64 -0
  126. data/sample/app/views/layouts/application.html.erb +13 -0
  127. data/sample/bin/bundle +3 -0
  128. data/sample/bin/rails +4 -0
  129. data/sample/bin/rake +4 -0
  130. data/sample/bin/setup +34 -0
  131. data/sample/bin/update +29 -0
  132. data/sample/config/application.rb +25 -0
  133. data/sample/config/boot.rb +3 -0
  134. data/sample/config/database.yml +25 -0
  135. data/sample/config/environment.rb +5 -0
  136. data/sample/config/environments/development.rb +42 -0
  137. data/sample/config/environments/production.rb +69 -0
  138. data/sample/config/environments/test.rb +36 -0
  139. data/sample/config/initializers/admin_core.rb +8 -0
  140. data/sample/config/initializers/application_controller_renderer.rb +6 -0
  141. data/sample/config/initializers/backtrace_silencers.rb +7 -0
  142. data/sample/config/initializers/cookies_serializer.rb +5 -0
  143. data/sample/config/initializers/filter_parameter_logging.rb +4 -0
  144. data/sample/config/initializers/inflections.rb +16 -0
  145. data/sample/config/initializers/mime_types.rb +4 -0
  146. data/sample/config/initializers/new_framework_defaults.rb +24 -0
  147. data/sample/config/initializers/session_store.rb +3 -0
  148. data/sample/config/initializers/wrap_parameters.rb +14 -0
  149. data/sample/config/locales/en.yml +23 -0
  150. data/sample/config/routes.rb +6 -0
  151. data/sample/config/secrets.yml +22 -0
  152. data/sample/config.ru +5 -0
  153. data/sample/db/migrate/20170417055257_create_users.rb +10 -0
  154. data/sample/db/migrate/20170417055412_create_tweets.rb +9 -0
  155. data/sample/db/schema.rb +31 -0
  156. data/sample/db/seeds.rb +7 -0
  157. data/sample/lib/assets/.keep +0 -0
  158. data/sample/lib/tasks/.keep +0 -0
  159. data/sample/log/.keep +0 -0
  160. data/sample/public/404.html +67 -0
  161. data/sample/public/422.html +67 -0
  162. data/sample/public/500.html +66 -0
  163. data/sample/public/apple-touch-icon-precomposed.png +0 -0
  164. data/sample/public/apple-touch-icon.png +0 -0
  165. data/sample/public/bundle.min.js +27 -0
  166. data/sample/public/bundle.min.js.map +1 -0
  167. data/sample/public/favicon.ico +0 -0
  168. data/sample/public/javascripts/admin-core.js +38196 -0
  169. data/sample/public/robots.txt +5 -0
  170. data/sample/public/stylesheets/admin-core.css +1 -0
  171. data/sample/tmp/.keep +0 -0
  172. data/sample/vendor/assets/stylesheets/.keep +0 -0
  173. 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
+ };