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
data/client/src/decls.js
ADDED
@@ -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;
|
data/client/src/main.js
ADDED
@@ -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
|
+
}
|