admin_core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,119 @@
1
+ // @flow
2
+
3
+ // Represent a single ActiveRecord::Base
4
+ export type Resource = {
5
+ displayName: string;
6
+ name: string;
7
+ showPath: ?string; // If it is null, the resource is not persisted.
8
+ editPath: ?string; // If it is null, the resource is not updatable.
9
+ destroyable: boolean;
10
+ fields: ResourceField[];
11
+ }
12
+
13
+ // Represent a child class of AdminCore::BaseResourceManager.
14
+ //
15
+ // @see AdminCore::BaseResourceManager.to_hash
16
+ export type ResourceManager = {
17
+ displayName: string;
18
+ indexPath: string;
19
+ newPath: ?string; // If it is null, the resource is not creatable.
20
+ showPath: string;
21
+ editPath: ?string; // If it is null, the resource is not updatable.
22
+ scopes: string[];
23
+ }
24
+
25
+ type $ResourceField<T, V> = {
26
+ displayName: string;
27
+ name: string;
28
+ type: T;
29
+ value: V;
30
+ };
31
+
32
+ export type ResourceField = $ResourceField<*, *>;
33
+
34
+ export type ResourceField$Boolean = $ResourceField<"boolean", boolean>;
35
+ export type ResourceField$BelongsTo = $ResourceField<"belongs_to", { resource: Resource; paramName: string; }>;
36
+ export type ResourceField$Date = $ResourceField<"date", string>;
37
+ export type ResourceField$DateTime = $ResourceField<"date_time", string>;
38
+ export type ResourceField$Enum = $ResourceField<"enum", { value: string; values: string[]; }>;
39
+ export type ResourceField$Number = $ResourceField<"number", number>;
40
+ export type ResourceField$String = $ResourceField<"string", string>;
41
+ export type ResourceField$Text = $ResourceField<"text", string>;
42
+
43
+ // Shape of exports of resource-field/*.js
44
+ export type ResourceFieldModule = {
45
+ Index: (ResourceField) => React$Element<*>;
46
+ New: (ResourceField, onChange: (string, any) => void) => React$Element<*>;
47
+ Show: (ResourceField) => React$Element<*>;
48
+ Edit: (ResourceField, onChange: (string, any) => void) => React$Element<*>;
49
+ getValue: (ResourceField) => any;
50
+ }
51
+
52
+
53
+ type $ResourceFilter<O, V> = {
54
+ type: string;
55
+ name: string;
56
+ displayName: string;
57
+ query: {
58
+ operator: O;
59
+ value: ?V;
60
+ };
61
+ }
62
+
63
+ export type ResourceFilter = $ResourceFilter<*, *>;
64
+
65
+ export type ResourceFilter$Boolean = $ResourceFilter<"is", boolean>;
66
+ export type ResourceFilter$Number = $ResourceFilter<"equals" | "greater_than" | "less_than", number>;
67
+ export type ResourceFilter$String = $ResourceFilter<"contains" | "equals" | "starts_with" | "ends_with", string>;
68
+
69
+ // Shpae of exports of admin-core/resource-filter/*.js
70
+ export type ResourceFilterModule = {
71
+ Filter: (ResourceFilter, _: (string, string, string) => void) => React$Element<*>;
72
+ }
73
+
74
+ // Shape of AdminCore::ResourcePage::Index#to_json
75
+ export type ResourcePage$Index = {
76
+ attributes: string[];
77
+ resources: Resource[];
78
+ filters: ResourceFilter[];
79
+ scopes: { name: string; count: number; }[];
80
+ pagination: {
81
+ current: number;
82
+ total: number;
83
+ };
84
+ }
85
+
86
+ // Shape of AdminCore::ResourcePage::New#to_json
87
+ export type ResourcePage$New = {
88
+ resource: Resource;
89
+ }
90
+
91
+ // Shape of AdminCore::ResourcePage::Show#to_json
92
+ export type ResourcePage$Show = {
93
+ resource: Resource;
94
+ }
95
+
96
+ // Shape of AdminCore::ResourcePage::Edit#to_json
97
+ export type ResourcePage$Edit = {
98
+ resource: Resource;
99
+ }
100
+
101
+ export type SidebarItem = SidebarTitle | SidebarDropdown | SidebarLink;
102
+
103
+ export type SidebarTitle = {
104
+ type: "title";
105
+ displayName: string;
106
+ }
107
+
108
+ export type SidebarDropdown = {
109
+ type: "dropdown";
110
+ displayName: string;
111
+ links: SidebarLink[];
112
+ }
113
+
114
+ export type SidebarLink = {
115
+ type: "link";
116
+ displayName: string;
117
+ link: string;
118
+ external: boolean;
119
+ };
@@ -0,0 +1,18 @@
1
+ // @flow
2
+ import axios from "axios";
3
+
4
+ const httpClient = axios.create({
5
+ headers: {
6
+ "Accept": "application/json",
7
+ },
8
+ });
9
+
10
+ httpClient.interceptors.request.use(config => {
11
+ const csrfToken = document.querySelector("meta[name='csrf-token']");
12
+ if (csrfToken instanceof HTMLMetaElement && config.headers) {
13
+ config.headers["X-CSRF-Token"] = csrfToken.content;
14
+ }
15
+ return config;
16
+ });
17
+
18
+ export default httpClient;
@@ -0,0 +1,9 @@
1
+ import AdminCore from "./AdminCore";
2
+ import React from "react";
3
+ import ReactDOM from "react-dom";
4
+ import ReactRouterDOM from "react-router-dom";
5
+
6
+ global.AdminCore = AdminCore;
7
+ global.React = React;
8
+ global.ReactDOM = ReactDOM;
9
+ global.ReactRouterDOM = ReactRouterDOM;
@@ -0,0 +1,26 @@
1
+ // @flow
2
+ import React from "react";
3
+ import {Link} from "react-router-dom";
4
+
5
+ import type {ResourceField$BelongsTo} from "../decls";
6
+ import {getValue} from "../resource-field-renderer";
7
+
8
+ exports.getValue = function (field: ResourceField$BelongsTo) {
9
+ const fi = field.value.resource.fields.find(f => f.name === field.value.paramName);
10
+ if (fi) {
11
+ return getValue(fi);
12
+ }
13
+ };
14
+
15
+ exports.Index = exports.Show = function (field: ResourceField$BelongsTo) {
16
+ const resource = field.value.resource;
17
+ if (resource.showPath) {
18
+ return <Link to={resource.showPath}>{resource.displayName}</Link>;
19
+ } else {
20
+ return <span>{resource.displayName}</span>;
21
+ }
22
+ };
23
+
24
+ exports.New = exports.Edit = function(_field: ResourceField$BelongsTo, _onChange: (string, any) => void) {
25
+ throw new Error("Not implemented");
26
+ };
@@ -0,0 +1,43 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$Boolean} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$Boolean) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$Boolean) {
10
+ return (
11
+ <span className="switch switch-text switch-primary">
12
+ <input
13
+ checked={field.value}
14
+ className="switch-input"
15
+ disabled={true}
16
+ type="checkbox"
17
+ />
18
+ <span className="switch-label" data-on="On" data-off="Off"/>
19
+ <span className="switch-handle"/>
20
+ </span>
21
+ );
22
+ };
23
+
24
+ exports.New = exports.Edit = function (field: ResourceField$Boolean, onChange: (string, any) => void) {
25
+ return (
26
+ <label className="switch switch-text switch-primary">
27
+ <input
28
+ type="checkbox"
29
+ name={field.name}
30
+ className="switch-input"
31
+ defaultChecked={field.value}
32
+ onChange={(e: SyntheticEvent) => {
33
+ const el = e.target;
34
+ if (el instanceof HTMLInputElement) {
35
+ onChange(field.name, el.checked);
36
+ }
37
+ }}
38
+ />
39
+ <span className="switch-label" data-on="On" data-off="Off"/>
40
+ <span className="switch-handle"/>
41
+ </label>
42
+ );
43
+ };
@@ -0,0 +1,29 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$Date} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$Date) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$Date) {
10
+ const date = new Date(field.value);
11
+ return <span>{date.toLocaleDateString()}</span>;
12
+ };
13
+
14
+ exports.New = exports.Edit = function (field: ResourceField$Date, onChange: (string, any) => void) {
15
+ return (
16
+ <input
17
+ className="form-control"
18
+ defaultValue={field.value}
19
+ name={field.name}
20
+ onChange={(e: SyntheticEvent) => {
21
+ const el = e.target;
22
+ if (el instanceof HTMLInputElement) {
23
+ onChange(field.name, el.value);
24
+ }
25
+ }}
26
+ type="date"
27
+ />
28
+ );
29
+ };
@@ -0,0 +1,29 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$DateTime} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$DateTime) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$DateTime) {
10
+ const date = new Date(field.value);
11
+ return <span>{date.toLocaleString()}</span>;
12
+ };
13
+
14
+ exports.New = exports.Edit = function (field: ResourceField$DateTime, onChange: (string, any) => void) {
15
+ return (
16
+ <input
17
+ className="form-control"
18
+ defaultValue={field.value}
19
+ name={field.name}
20
+ onChange={(e: SyntheticEvent) => {
21
+ const el = e.target;
22
+ if (el instanceof HTMLInputElement) {
23
+ onChange(field.name, el.value);
24
+ }
25
+ }}
26
+ type="datetime"
27
+ />
28
+ );
29
+ };
@@ -0,0 +1,34 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$Enum} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$Enum) {
6
+ return field.value.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$Enum) {
10
+ if (field.value.values.indexOf(field.value.value) !== -1 && typeof field.value.value === "number") {
11
+ return <span>{field.value.values[field.value.value]}</span>;
12
+ } else {
13
+ return <span>{field.value.value}</span>;
14
+ }
15
+ };
16
+
17
+ exports.New = exports.Edit = function (field: ResourceField$Enum, onChange: (string, any) => void) {
18
+ return (
19
+ <select
20
+ className="form-control"
21
+ defaultValue={field.value.value}
22
+ onChange={(e: SyntheticEvent) => {
23
+ const el = e.target;
24
+ if (el instanceof HTMLSelectElement) {
25
+ onChange(field.name, el.value);
26
+ }
27
+ }}
28
+ >
29
+ {field.value.values.map((value, i) =>
30
+ <option value={value} key={i}>{value}</option>
31
+ )}
32
+ </select>
33
+ );
34
+ };
@@ -0,0 +1,28 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$Number} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$Number) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$Number) {
10
+ return <span>{field.value}</span>;
11
+ };
12
+
13
+ exports.New = exports.Edit = function(field: ResourceField$Number, onChange: (string, any) => void) {
14
+ return (
15
+ <input
16
+ className="form-control"
17
+ defaultValue={field.value}
18
+ name={field.name}
19
+ onChange={(e: SyntheticEvent) => {
20
+ const el = e.target;
21
+ if (el instanceof HTMLInputElement) {
22
+ onChange(field.name, el.value);
23
+ }
24
+ }}
25
+ type="number"
26
+ />
27
+ );
28
+ };
@@ -0,0 +1,28 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$String} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$String) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$String) {
10
+ return <span>{field.value}</span>;
11
+ };
12
+
13
+ exports.New = exports.Edit = function(field: ResourceField$String, onChange: (string, any) => void) {
14
+ return (
15
+ <input
16
+ className="form-control"
17
+ defaultValue={field.value}
18
+ name={field.name}
19
+ onChange={(e: SyntheticEvent) => {
20
+ const el = e.target;
21
+ if (el instanceof HTMLInputElement) {
22
+ onChange(field.name, el.value);
23
+ }
24
+ }}
25
+ type="text"
26
+ />
27
+ );
28
+ };
@@ -0,0 +1,27 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceField$Text} from "../decls";
4
+
5
+ exports.getValue = function (field: ResourceField$Text) {
6
+ return field.value;
7
+ };
8
+
9
+ exports.Index = exports.Show = function (field: ResourceField$Text) {
10
+ return <span>{field.value}</span>;
11
+ };
12
+
13
+ exports.New = exports.Edit = function(field: ResourceField$Text, onChange: (string, any) => void) {
14
+ return (
15
+ <textarea
16
+ className="form-control"
17
+ defaultValue={field.value}
18
+ name={field.name}
19
+ onChange={(e: SyntheticEvent) => {
20
+ const el = e.target;
21
+ if (el instanceof HTMLTextAreaElement) {
22
+ onChange(field.name, el.value);
23
+ }
24
+ }}
25
+ />
26
+ );
27
+ };
@@ -0,0 +1,45 @@
1
+ // @flow
2
+ import type {
3
+ ResourceFieldModule,
4
+ ResourceField,
5
+ } from "./decls";
6
+
7
+ const resourceFields: { [string]: ResourceFieldModule; } = {};
8
+
9
+ export function register(name: string, module: ResourceFieldModule) {
10
+ resourceFields[name] = module;
11
+ }
12
+
13
+ export function getValue(field: ResourceField) {
14
+ const module = resourceFields[field.type];
15
+ return module.getValue(field);
16
+ }
17
+
18
+ export function renderIndex(field: ResourceField) {
19
+ const module = resourceFields[field.type];
20
+ return module.Index(field);
21
+ }
22
+
23
+ export function renderNew(field: ResourceField, onChange: (string, any) => void) {
24
+ const module = resourceFields[field.type];
25
+ return module.New(field, onChange);
26
+ }
27
+
28
+ export function renderShow(field: ResourceField) {
29
+ const module = resourceFields[field.type];
30
+ return module.Show(field);
31
+ }
32
+
33
+ export function renderEdit(field: ResourceField, onChange: (string, any) => void) {
34
+ const module = resourceFields[field.type];
35
+ return module.Edit(field, onChange);
36
+ }
37
+
38
+ register("belongs_to", require("./resource-field/BelongsTo"));
39
+ register("boolean", require("./resource-field/Boolean"));
40
+ register("date", require("./resource-field/Date"));
41
+ register("date_time", require("./resource-field/DateTime"));
42
+ register("enum", require("./resource-field/Enum"));
43
+ register("number", require("./resource-field/Number"));
44
+ register("string", require("./resource-field/String"));
45
+ register("text", require("./resource-field/Text"));
@@ -0,0 +1,22 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceFilter$Boolean} from "../decls";
4
+
5
+ export function Filter(filter: ResourceFilter$Boolean, onChange: (string, string, string) => void) {
6
+ return (
7
+ <select
8
+ className="form-control"
9
+ defaultValue={filter.query.value == null ? null : filter.query.value ? "true" : "false"}
10
+ onChange={(e: SyntheticEvent) => {
11
+ const el = e.target;
12
+ if (el instanceof HTMLSelectElement) {
13
+ onChange(filter.name, "is", el.value);
14
+ }
15
+ }}
16
+ >
17
+ <option>Any</option>
18
+ <option value="true">Yes</option>
19
+ <option value="false">No</option>
20
+ </select>
21
+ );
22
+ }
@@ -0,0 +1,45 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceFilter$Number} from "../decls";
4
+
5
+ class ResourceFilterNumber extends React.Component {
6
+ props: {
7
+ filter: ResourceFilter$Number;
8
+ onChange: (string, string, string) => void;
9
+ };
10
+
11
+ handleChange() {
12
+ this.props.onChange(
13
+ this.props.filter.name,
14
+ this.refs.select.value,
15
+ this.refs.input.value
16
+ );
17
+ }
18
+
19
+ render() {
20
+ return (
21
+ <div>
22
+ <select
23
+ ref="select"
24
+ className="form-control"
25
+ defaultValue={this.props.filter.query.operator}
26
+ onChange={this.handleChange.bind(this)}
27
+ >
28
+ <option value="equals">Equals</option>
29
+ <option value="greater_than">Greater than</option>
30
+ <option value="less_than">Less than</option>
31
+ </select>
32
+ <input
33
+ ref="input"
34
+ className="form-control"
35
+ defaultValue={this.props.filter.query.value}
36
+ onChange={this.handleChange.bind(this)}
37
+ />
38
+ </div>
39
+ );
40
+ }
41
+ }
42
+
43
+ export function Filter(filter: ResourceFilter$Number, onChange: (string, string, string) => void) {
44
+ return <ResourceFilterNumber filter={filter} onChange={onChange} />;
45
+ }
@@ -0,0 +1,46 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {ResourceFilter$String} from "../decls";
4
+
5
+ class ResourceFilterString extends React.Component {
6
+ props: {
7
+ filter: ResourceFilter$String;
8
+ onChange: (string, string, string) => void;
9
+ };
10
+
11
+ handleChange() {
12
+ this.props.onChange(
13
+ this.props.filter.name,
14
+ this.refs.select.value,
15
+ this.refs.input.value
16
+ );
17
+ }
18
+
19
+ render() {
20
+ return (
21
+ <div>
22
+ <select
23
+ ref="select"
24
+ className="form-control"
25
+ defaultValue={this.props.filter.query.operator}
26
+ onChange={this.handleChange.bind(this)}
27
+ >
28
+ <option value="contains">Contains</option>
29
+ <option value="equals">Equals</option>
30
+ <option value="starts_with">Starts with</option>
31
+ <option value="ends_with">Ends with</option>
32
+ </select>
33
+ <input
34
+ ref="input"
35
+ className="form-control"
36
+ defaultValue={this.props.filter.query.value}
37
+ onChange={this.handleChange.bind(this)}
38
+ />
39
+ </div>
40
+ );
41
+ }
42
+ }
43
+
44
+ export function Filter(filter: ResourceFilter$String, onChange: (string, string, string) => void) {
45
+ return <ResourceFilterString filter={filter} onChange={onChange} />;
46
+ }
@@ -0,0 +1,17 @@
1
+ // @flow
2
+ import type {ResourceFilter, ResourceFilterModule} from "./decls";
3
+
4
+ const resourceFilters: { [string]: ResourceFilterModule; } = {};
5
+
6
+ export function register(name: string, module: ResourceFilterModule) {
7
+ resourceFilters[name] = module;
8
+ }
9
+
10
+ export function renderFilter(filter: ResourceFilter, onChange: (string, string, any) => void) {
11
+ const module = resourceFilters[filter.type];
12
+ return module.Filter(filter, onChange);
13
+ }
14
+
15
+ register("boolean", require("./resource-filter/Boolean"));
16
+ register("number", require("./resource-filter/Number"));
17
+ register("string", require("./resource-filter/String"));
@@ -0,0 +1,36 @@
1
+ // @flow
2
+ import React from "react";
3
+ import type {Location, RouterHistory} from "react-router-dom";
4
+
5
+ import httpClient from "../http-client";
6
+
7
+ export default class Base extends React.Component {
8
+ props: {
9
+ location: Location;
10
+ history: RouterHistory;
11
+ };
12
+
13
+ state: any;
14
+
15
+ constructor(props: any) {
16
+ super(props);
17
+ this.state = {};
18
+ }
19
+
20
+ componentDidMount() {
21
+ this.loadPage();
22
+ }
23
+
24
+ componentDidUpdate(prevProps: { location: Location; }) {
25
+ if (this.props.location.pathname !== prevProps.location.pathname ||
26
+ this.props.location.search !== prevProps.location.search) {
27
+ this.loadPage();
28
+ }
29
+ }
30
+
31
+ loadPage() {
32
+ const jsonPathname = this.props.location.pathname.replace(/\.[^/.]+$/, "") + ".json";
33
+ httpClient.get(jsonPathname + this.props.location.search)
34
+ .then(({ data }) => this.setState({ page: data.page }));
35
+ }
36
+ }
@@ -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$Edit, ResourceManager} from "../decls";
8
+
9
+ export default function editPage(resourceManager: ResourceManager) {
10
+ class EditPage extends Base {
11
+ state: {
12
+ page?: ResourcePage$Edit;
13
+ }
14
+
15
+ render() {
16
+ const page = this.state.page;
17
+ return (
18
+ <main className="main">
19
+ <Breadcrumb links={[["Home", "/"], [resourceManager.displayName, resourceManager.indexPath], page ? [page.resource.displayName, page.resource.showPath || "#"] : ["...", "#"]]} current="Edit"/>
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={page.resource.showPath || ""}
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
+ EditPage.displayName = `EditPage(${resourceManager.displayName})`;
47
+ return EditPage;
48
+ }