├── .env ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── config.js ├── logs └── .gitignore ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── manifest.json └── robots.txt ├── save-commands.json ├── server └── index.js └── src ├── App.css ├── App.js ├── App.style.js ├── change-pass ├── actions │ ├── onDone.js │ └── post.js ├── components │ └── ChangePass.js ├── fetchers │ └── post.js └── reducer.js ├── colors.js ├── config.js ├── contacts ├── actions │ ├── onDone.js │ └── post.js ├── components │ └── Contacts.js ├── fetchers │ └── post.js └── reducer.js ├── email-confirm ├── actions │ ├── onConfirm.js │ └── post.js ├── components │ └── EmailConfirm.js ├── fetchers │ └── post.js └── reducer.js ├── entries ├── actions │ ├── delete.js │ ├── onAddDialogDone.js │ ├── onAddEntry.js │ ├── onConfirmDialogConfirm.js │ ├── onDeleteEntry.js │ ├── onDialogClose.js │ ├── onDialogExited.js │ ├── onEditDialogDone.js │ ├── onEditEntry.js │ ├── post.js │ └── put.js ├── components │ ├── Entries.js │ ├── basic │ │ └── EntryStringValue.js │ ├── cards │ │ ├── ArrayCard.js │ │ ├── ArrayCard.style.js │ │ ├── ArraySubcard.js │ │ ├── ArraySubcard.style.js │ │ ├── BasicTypeCard.js │ │ ├── BasicTypeSubcard.js │ │ ├── CardFactory.js │ │ ├── ObjectCard.js │ │ ├── ObjectCard.style.js │ │ ├── ObjectSubcard.js │ │ ├── ObjectSubcard.style.js │ │ └── SubcardFactory.js │ └── dialogs │ │ ├── ControlContainer.js │ │ ├── ControlContainer.style.js │ │ ├── ControlFactory.js │ │ ├── EntryDialog.js │ │ ├── EntryDialogInner.js │ │ └── controls │ │ ├── ArrayControl.js │ │ ├── ArrayControl.style.js │ │ ├── AssetControl.js │ │ ├── BooleanControl.js │ │ ├── EnumControl.js │ │ ├── HtmlControl.js │ │ ├── MarkdownControl.js │ │ ├── NumberControl.js │ │ ├── ObjectControl.js │ │ ├── ObjectControl.style.js │ │ ├── ReferenceControl.js │ │ ├── StringLineControl.js │ │ └── StringMultilineControl.js ├── fetchers │ ├── delete.js │ ├── post.js │ ├── postFileFetcher.js │ └── put.js ├── helpers │ └── isEmptyObject.js └── reducer.js ├── explorer └── components │ ├── Explorer.js │ └── config.js ├── global ├── actions │ ├── errorSet.js │ ├── fetchingEnd.js │ ├── fetchingStart.js │ ├── getData.js │ ├── projectIdSet.js │ └── redirectSet.js ├── components │ ├── Container.js │ ├── Content.js │ ├── Content.style.js │ ├── ErrorContainer.js │ ├── ErrorContainer.style.js │ ├── Header.js │ ├── Header.style.js │ ├── Loader.js │ ├── Loader.style.js │ ├── Menu.js │ ├── Menu.style.js │ ├── Redirect.js │ └── RedirectContainer.js ├── fetchers │ ├── getEntries.js │ ├── getModels.js │ └── getProjects.js └── reducer.js ├── index.js ├── index └── components │ ├── Index.js │ └── Index.style.js ├── landing └── components │ ├── GithubCorner.js │ ├── GithubCorner.style.js │ ├── Landing.js │ └── Landing.style.js ├── lib ├── components │ ├── Button.js │ ├── Button.style.js │ ├── Card.js │ ├── Card.style.js │ ├── CircledIcon.js │ ├── CircularProgress.js │ ├── CircularProgress.style.js │ ├── ConfirmDialog.js │ ├── Dialog.js │ ├── Dialog.style.js │ ├── DialogActions.js │ ├── DialogActions.style.js │ ├── DialogContent.js │ ├── DialogContent.style.js │ ├── Icon.js │ ├── LinearProgress.js │ ├── LinearProgress.style.js │ ├── MessageBlock.js │ ├── MessageBlock.style.js │ ├── PageContainer.js │ ├── PageContainer.style.js │ ├── Subcard.js │ ├── Subcard.style.js │ ├── SubcardDivider.js │ ├── SubcardDivider.style.js │ ├── Typography.js │ ├── Typography.style.js │ ├── controls │ │ ├── Checkbox.js │ │ ├── Checkbox.style.js │ │ ├── CodeEditor.js │ │ ├── CodeEditor.style.js │ │ ├── FileInput.js │ │ ├── Select.js │ │ ├── SelectInner.js │ │ ├── SelectInner.style.js │ │ ├── Switch.js │ │ ├── Switch.style.js │ │ ├── TextInput.js │ │ └── TextInput.style.js │ └── fields │ │ ├── Checkbox.js │ │ ├── Checkbox.style.js │ │ ├── Select.js │ │ ├── Switch.js │ │ ├── Switch.style.js │ │ ├── TextField.js │ │ └── TextField.style.js ├── helpers │ ├── createEntry.js │ ├── createModel.js │ ├── createSubentry.js │ ├── createSubmodel.js │ ├── getAssetControlsWithFile.js │ ├── isEntryConsistencyConflict.js │ ├── isEntryRefConflict.js │ ├── isModelRefConflict.js │ ├── isNumber.js │ ├── logout.js │ ├── sortKeys.js │ └── valueAsNumber.js ├── services │ ├── Auth.js │ ├── Fetcher.js │ ├── Jss.js │ ├── Promise.js │ ├── Routes.js │ └── Validator.js ├── styles │ ├── margins.css │ ├── paddings.css │ └── texts.css └── types │ ├── entries │ ├── ArrayEntryType.js │ ├── ArraySubentryType.js │ ├── AssetEntryType.js │ ├── AssetSubentryType.js │ ├── BooleanEntryType.js │ ├── BooleanSubentryType.js │ ├── EntryType.js │ ├── EnumEntryType.js │ ├── EnumSubentryType.js │ ├── NumberEntryType.js │ ├── NumberSubentryType.js │ ├── ObjectEntryType.js │ ├── ObjectSubentryType.js │ ├── ReferenceEntryType.js │ ├── ReferenceSubentryType.js │ ├── StringEntryType.js │ ├── StringSubentryType.js │ └── SubentryType.js │ └── models │ ├── AbstractType.js │ ├── ArrayModelType.js │ ├── ArraySubmodelType.js │ ├── AssetModelType.js │ ├── AssetSubmodelType.js │ ├── BooleanModelType.js │ ├── BooleanSubmodelType.js │ ├── EnumModelType.js │ ├── EnumSubmodelType.js │ ├── ModelType.js │ ├── NumberModelType.js │ ├── NumberSubmodelType.js │ ├── ObjectModelType.js │ ├── ObjectSubmodelType.js │ ├── ReferenceModelType.js │ ├── ReferenceSubmodelType.js │ ├── StringModelType.js │ ├── StringSubmodelType.js │ └── SubmodelType.js ├── login ├── actions │ ├── onDone.js │ └── post.js ├── components │ ├── Login.js │ └── Login.style.js ├── fetchers │ └── post.js └── reducer.js ├── mocks ├── getArray.js ├── getAsset.js ├── getBoolean.js ├── getEnum.js ├── getId.js ├── getNumber.js ├── getObject.js ├── getReference.js ├── getStringHtml.js ├── getStringLine.js ├── getStringMarkdown.js ├── getStringMultiline.js ├── index.js └── stringMarkdownValue.js ├── models ├── actions │ ├── delete.js │ ├── onAddItem.js │ ├── onAddModel.js │ ├── onAddProp.js │ ├── onDeleteItem.js │ ├── onDeleteModel.js │ ├── onDeleteProp.js │ ├── onDialogAddDoneItem.js │ ├── onDialogAddDoneModel.js │ ├── onDialogAddDoneProp.js │ ├── onDialogClose.js │ ├── onDialogConfirmConfirmItem.js │ ├── onDialogConfirmConfirmModel.js │ ├── onDialogConfirmConfirmProp.js │ ├── onDialogEditDoneItem.js │ ├── onDialogEditDoneModel.js │ ├── onDialogEditDoneProp.js │ ├── onDialogExited.js │ ├── onEditItem.js │ ├── onEditModel.js │ ├── onEditProp.js │ ├── post.js │ └── put.js ├── components │ ├── Models.js │ ├── basic │ │ ├── TypeBlock.js │ │ └── TypeBlock.style.js │ ├── cards │ │ ├── ArrayCard.js │ │ ├── BasicTypeCard.js │ │ ├── CardFactory.js │ │ ├── ObjectCard.js │ │ └── ObjectCard.style.js │ └── dialogs │ │ ├── AddDialog.js │ │ ├── ConfirmDialog.js │ │ ├── EditDialog.js │ │ ├── SelectType.js │ │ ├── Step.js │ │ ├── Steps.js │ │ ├── array-type │ │ ├── ArrayItem.js │ │ ├── ArrayModel.js │ │ └── ArrayProp.js │ │ ├── asset-type │ │ ├── AssetItem.js │ │ ├── AssetModel.js │ │ └── AssetProp.js │ │ ├── boolean-type │ │ ├── BooleanItem.js │ │ ├── BooleanModel.js │ │ └── BooleanProp.js │ │ ├── enum-type │ │ ├── EnumControl.js │ │ ├── EnumControl.style.js │ │ ├── EnumControlSmart.js │ │ ├── EnumItem.js │ │ ├── EnumModel.js │ │ └── EnumProp.js │ │ ├── number-type │ │ ├── NumberItem.js │ │ ├── NumberModel.js │ │ └── NumberProp.js │ │ ├── object-type │ │ ├── ObjectItem.js │ │ ├── ObjectModel.js │ │ └── ObjectProp.js │ │ ├── reference-type │ │ ├── ReferenceItem.js │ │ ├── ReferenceItem.style.js │ │ ├── ReferenceModel.js │ │ ├── ReferenceModel.style.js │ │ ├── ReferenceProp.js │ │ └── ReferenceProp.style.js │ │ └── string-type │ │ ├── StringItem.js │ │ ├── StringModel.js │ │ ├── StringProp.js │ │ └── Subtype.js ├── fetchers │ ├── delete.js │ ├── post.js │ └── put.js ├── helpers │ ├── dialogConstructor.js │ ├── dialogTypeOnBlur.js │ ├── dialogTypeOnDone.js │ ├── onBooleanChange.js │ ├── onNumberChange.js │ ├── onStringChange.js │ └── validatePropertyName.js └── reducer.js ├── not-found └── components │ └── NotFound.js ├── projects ├── actions │ ├── deleteAction.js │ ├── getAction.js │ ├── onAddDialogDone.js │ ├── onAddProject.js │ ├── onConfirmDialogConfirm.js │ ├── onDeleteProject.js │ ├── onDialogClose.js │ ├── onDialogExited.js │ ├── onEditDialogDone.js │ ├── onEditProject.js │ ├── onLogoutClick.js │ ├── postAction.js │ └── putAction.js ├── components │ ├── ConfirmDialog.js │ ├── ProjectDialog.js │ ├── ProjectDialogInner.js │ ├── Projects.js │ └── Projects.style.js ├── fetchers │ ├── deleteFetcher.js │ ├── postFetcher.js │ └── putFetcher.js └── reducer.js ├── recover-pass ├── actions │ ├── onDone.js │ └── post.js ├── components │ └── RecoverPass.js ├── fetchers │ └── post.js └── reducer.js ├── reducer.js ├── registration ├── actions │ ├── onDone.js │ └── post.js ├── components │ ├── Registration.js │ └── Registration.style.js ├── fetchers │ └── post.js └── reducer.js ├── serviceWorker.js ├── state.js ├── store.js └── tokens ├── actions ├── deleteAction.js ├── getAction.js ├── onAdd.js ├── onAddDialogDone.js ├── onConfirmDialogConfirm.js ├── onDelete.js ├── onDialogClose.js ├── onDialogExited.js ├── onEdit.js ├── onEditDialogDone.js ├── postAction.js └── putAction.js ├── components ├── ConfirmDialog.js ├── TokenDialog.js ├── TokenDialogInner.js ├── Tokens.js └── basic │ ├── Card.js │ └── Card.style.js ├── fetchers ├── deleteFetcher.js ├── getFetcher.js ├── postFetcher.js └── putFetcher.js └── reducer.js /.env: -------------------------------------------------------------------------------- 1 | NODE_PATH=./ 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app", 3 | "rules": { 4 | # "lines-between-class-members": [1, "always"], # added in eslint 4.9.0 5 | "arrow-parens": [1, "as-needed"], 6 | "comma-dangle": [1,"always-multiline"], 7 | "no-console": 1, 8 | "react/require-default-props": 1, 9 | "react/prop-types": 1, 10 | "react/default-props-match-prop-types": 1, 11 | "react/no-unused-prop-types": 1, 12 | "prefer-template": 1, 13 | "no-useless-concat": 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | /public/vs 24 | /public/cookieconsent 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Any JSON CMS - Admin application server 2 | 3 | If you want to read general information about Any JSON CMS go [here](https://github.com/evmizulin/any-json-cms). 4 | 5 | Any JSON CMS consists of two parts. Admin application server and [API server](https://github.com/evmizulin/cms-api). It is Admin application server. 6 | 7 | ### Installation 8 | 9 | #### Step 1. Install the required dependencies. 10 | - [Node.js](https://nodejs.org/) v8+; 11 | 12 | #### Step 2. Install the Admin application server. 13 | ```sh 14 | git clone git@github.com:evmizulin/cms-admin.git 15 | cd cms-admin 16 | npm install 17 | ``` 18 | 19 | #### Step 3. Update configuration file. 20 | In project root folder there are configuration file ```config.js```. Update it for your needs. 21 | ```js 22 | module.exports = { 23 | config: { 24 | isDemo: true, 25 | prodAppServerHost: 'localhost', 26 | prodAppServerPort: 8081, 27 | devApiProtocol: 'http', 28 | prodApiProtocol: 'https', 29 | devApiHost: 'localhost:8080', 30 | prodApiHost: 'some-domain', 31 | }, 32 | } 33 | ``` 34 | All parameters that have ```dev``` and ```prod``` prefixes, will be used for development and production environments respectively. 35 | 36 | - ```isDemo``` - there are several messages on the landing page informing that it is a demo server, set flag to ```false``` and it will hide messages; 37 | - ```prodAppServerHost``` - this parameter will be passed as ```host``` to run Node.js server; 38 | - ```prodAppServerPort``` - this parameter will be passed as ```port``` to run Node.js server; 39 | - ```apiProtocol``` - protocol of [API server](https://github.com/evmizulin/cms-api); 40 | - ```apiHost``` - host of [API server](https://github.com/evmizulin/cms-api); 41 | 42 | #### Step 4. Run Admin application server. 43 | To run Admin application server in development environment: 44 | ```sh 45 | npm start 46 | ``` 47 | And open ```http://localhost:3000``` in your browser. 48 | 49 | To run Admin application server in production environment: 50 | ```sh 51 | npm run build && npm run start.prod 52 | ``` 53 | You could see the logs in ```/logs``` folder. 54 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | config: { 3 | isDemo: true, 4 | prodAppServerHost: 'localhost', 5 | prodAppServerPort: 8081, 6 | devApiProtocol: 'http', 7 | prodApiProtocol: 'https', 8 | devApiHost: 'localhost:8080', 9 | prodApiHost: 'some-domain', 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cms-admin", 3 | "version": "1.3.2", 4 | "description": "", 5 | "scripts": { 6 | "pre": "npm run pre-monaco && npm run pre-cookie", 7 | "pre-monaco": "rm -rf public/vs && cp -a node_modules/monaco-editor/min/vs public/", 8 | "pre-cookie": "rm -rf public/cookieconsent && cp -a node_modules/cookieconsent/build/. public/cookieconsent", 9 | "build": "npm run pre && react-scripts build", 10 | "start": "npm run pre && react-scripts start", 11 | "test": "react-scripts test --env=jsdom", 12 | "start.prod": "NODE_ENV=production forever start -a -p logs -l console.log server/index.js", 13 | "stop": "forever stop server/index.js" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+ssh://git@bitbucket.org/evmizulin/cms-admin.git" 18 | }, 19 | "author": "Evgeny Mizulin", 20 | "license": "GNU General Public License v3.0", 21 | "engines": { 22 | "node": "8.11.2" 23 | }, 24 | "dependencies": { 25 | "bootstrap-grid": "^2.0.1", 26 | "clone": "2.1.1", 27 | "cookieconsent": "^3.1.0", 28 | "deep-freeze": "0.0.1", 29 | "express": "^4.16.4", 30 | "forever": "^0.15.3", 31 | "jss": "9.3.3", 32 | "jss-preset-default": "4.0.1", 33 | "lodash.uniq": "^4.5.0", 34 | "material-ui": "1.0.0-beta.23", 35 | "monaco-editor": "^0.10.1", 36 | "morgan": "^1.9.1", 37 | "prettier": "^1.14.3", 38 | "react": "16.0.0", 39 | "react-dom": "16.0.0", 40 | "react-icons": "^3.1.0", 41 | "react-redux": "5.0.6", 42 | "react-router-dom": "4.2.2", 43 | "react-scripts": "1.0.14", 44 | "react-select": "^2.1.1", 45 | "react-youtube": "^7.8.0", 46 | "redux": "3.7.2", 47 | "redux-thunk": "2.2.0", 48 | "reset-css": "^2.2.1", 49 | "rotating-file-stream": "^1.3.9", 50 | "showdown": "^1.8.5", 51 | "store": "^2.0.12", 52 | "swagger-ui": "^3.10.0", 53 | "tvalidator": "2.0.3", 54 | "xhr": "^2.5.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evmizulin/cms-admin/16534d05fe187608af4b329ad1e7cb02794d09b0/public/favicon.ico -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Any JSON CMS", 3 | "name": "Any JSON CMS", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | -------------------------------------------------------------------------------- /save-commands.json: -------------------------------------------------------------------------------- 1 | { 2 | "commands": [ 3 | "src/**/*.js : ./node_modules/.bin/prettier --print-width 110 --no-semi --single-quote --trailing-comma es5 --write {absPath}{filename}", 4 | "src/**/*.js : ./node_modules/.bin/eslint --fix {absPath}{filename}", 5 | "server/**/*.js : ./node_modules/.bin/prettier --print-width 110 --no-semi --single-quote --trailing-comma es5 --write {absPath}{filename}", 6 | "server/**/*.js : ./node_modules/.bin/eslint --fix {absPath}{filename}" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const fs = require('fs') 3 | const morgan = require('morgan') 4 | const path = require('path') 5 | const rfs = require('rotating-file-stream') 6 | const indexHtml = fs.readFileSync('./build/index.html', 'utf8') 7 | const { config } = require('../config') 8 | 9 | const accessLogStream = rfs('access.log', { 10 | interval: '1d', 11 | path: path.join(__dirname, '../logs'), 12 | }) 13 | const format = 14 | ':date[iso] :remote-addr :remote-user :method :url :status :res[content-length] - :response-time ms' 15 | 16 | const app = express() 17 | 18 | app.use(morgan(format, { stream: accessLogStream })) 19 | app.use(express.static('build')) 20 | 21 | app.get('*', (req, res) => { 22 | res.status(200).send(indexHtml) 23 | }) 24 | 25 | app.listen(config.prodAppServerPort, config.prodAppServerHost, () => { 26 | // eslint-disable-next-line no-console 27 | console.log( 28 | `${new Date().toISOString()} listening on ${config.prodAppServerHost}:${config.prodAppServerPort}` 29 | ) 30 | // eslint-disable-next-line no-console 31 | console.log(`${new Date().toISOString()} NODE_ENV=${process.env.NODE_ENV}`) 32 | }) 33 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import "./lib/styles/paddings.css"; 2 | @import "./lib/styles/margins.css"; 3 | @import "./lib/styles/texts.css"; 4 | @import "~bootstrap-grid/dist/grid.css"; 5 | @import "~swagger-ui/dist/swagger-ui.css"; 6 | 7 | html, 8 | body { 9 | height: 100%; 10 | margin: 0; 11 | padding: 0; 12 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 13 | font-size: 16px; 14 | } 15 | 16 | #root { 17 | margin: 0; 18 | padding: 0; 19 | height: 100%; 20 | position: relative; } 21 | 22 | .row > div { 23 | box-sizing: border-box; 24 | } 25 | 26 | .github-corner:hover .octo-arm { 27 | animation: octocat-wave 560ms ease-in-out 28 | } 29 | @keyframes octocat-wave { 30 | 0%, 31 | 100% { 32 | transform: rotate(0) 33 | } 34 | 20%, 35 | 60% { 36 | transform: rotate(-25deg) 37 | } 38 | 40%, 39 | 80% { 40 | transform: rotate(10deg) 41 | } 42 | } 43 | @media (max-width:500px) { 44 | .github-corner:hover .octo-arm { 45 | animation: none 46 | } 47 | .github-corner .octo-arm { 48 | animation: octocat-wave 560ms ease-in-out 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/App.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | jss 5 | .createStyleSheet({ 6 | '@global': { 7 | '.text-error': { 8 | color: colors.error.main, 9 | }, 10 | '.text-disabled': { 11 | color: colors.black.t4, 12 | }, 13 | '.loader-page-container': { 14 | background: colors.tertiary.main, 15 | minHeight: 'calc(100% - 50px)', 16 | paddingTop: '50px', 17 | textAlign: 'center', 18 | }, 19 | 'a:link': { 20 | color: colors.primary.main, 21 | }, 22 | 'a:visited': { 23 | color: colors.primary.main, 24 | }, 25 | 'a:hover': { 26 | color: colors.primary.d5, 27 | }, 28 | 'a:active': { 29 | color: colors.primary.l3, 30 | }, 31 | }, 32 | }) 33 | .attach() 34 | -------------------------------------------------------------------------------- /src/change-pass/actions/onDone.js: -------------------------------------------------------------------------------- 1 | export const onDone = creds => ({ 2 | widget: 'changePass', 3 | type: 'onDone', 4 | payload: { creds }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/change-pass/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/change-pass/fetchers/post' 2 | 3 | export function postAction(creds) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'changePass', 7 | type: 'postStart', 8 | }) 9 | postFetcher(creds) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'changePass', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'changePass', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/change-pass/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(creds) { 4 | return fetch({ method: 'POST', url: `change-pass`, body: creds }) 5 | } 6 | -------------------------------------------------------------------------------- /src/change-pass/reducer.js: -------------------------------------------------------------------------------- 1 | import { errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { postAction } from 'src/change-pass/actions/post' 3 | import { dispatch } from 'src/store' 4 | 5 | export function postStart(state) { 6 | fetchingStart(state) 7 | state.changePass.loading.post = true 8 | } 9 | 10 | export function postEnd(state) { 11 | fetchingEnd(state) 12 | state.changePass.loading.post = false 13 | state.changePass.showMessage = true 14 | } 15 | 16 | export function postError(state, error) { 17 | fetchingEnd(state) 18 | state.changePass.loading.post = false 19 | errorSet(state, { show: true, error }) 20 | } 21 | 22 | export function onDone(state, { creds }) { 23 | setTimeout(() => dispatch(postAction(creds)), 0) 24 | } 25 | 26 | const modifiers = { 27 | postStart, 28 | postEnd, 29 | postError, 30 | onDone, 31 | } 32 | 33 | export const changePassReducer = (state, action) => { 34 | const modifier = modifiers[action.type] 35 | modifier(state, action.payload) 36 | } 37 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | import { config as globalConfig } from 'config' 2 | 3 | const IS_DEV = process.env.NODE_ENV === 'development' 4 | 5 | export const config = { 6 | isDemo: globalConfig.isDemo, 7 | apiUrl: IS_DEV 8 | ? `${globalConfig.devApiProtocol}://${globalConfig.devApiHost}` 9 | : `${globalConfig.prodApiProtocol}://${globalConfig.prodApiHost}`, 10 | apiHost: IS_DEV ? globalConfig.devApiHost : globalConfig.prodApiHost, 11 | apiProtocol: IS_DEV ? globalConfig.devApiProtocol : globalConfig.prodApiProtocol, 12 | } 13 | -------------------------------------------------------------------------------- /src/contacts/actions/onDone.js: -------------------------------------------------------------------------------- 1 | export const onDone = contact => ({ 2 | widget: 'contacts', 3 | type: 'onDone', 4 | payload: { contact }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/contacts/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/contacts/fetchers/post' 2 | 3 | export function postAction(contact) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'contacts', 7 | type: 'postStart', 8 | }) 9 | postFetcher(contact) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'contacts', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'contacts', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/contacts/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(contact) { 4 | return fetch({ method: 'POST', url: `tmp/contacts`, body: contact }) 5 | } 6 | -------------------------------------------------------------------------------- /src/contacts/reducer.js: -------------------------------------------------------------------------------- 1 | import { errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { postAction } from 'src/contacts/actions/post' 3 | import { dispatch } from 'src/store' 4 | 5 | export function postStart(state) { 6 | fetchingStart(state) 7 | state.contacts.loading.post = true 8 | } 9 | 10 | export function postEnd(state) { 11 | fetchingEnd(state) 12 | state.contacts.loading.post = false 13 | state.contacts.status = 'success' 14 | } 15 | 16 | export function postError(state, error) { 17 | fetchingEnd(state) 18 | state.contacts.loading.post = false 19 | errorSet(state, { show: true, error }) 20 | } 21 | 22 | export function onDone(state, { contact }) { 23 | setTimeout(() => dispatch(postAction(contact)), 0) 24 | } 25 | 26 | const modifiers = { 27 | postStart, 28 | postEnd, 29 | postError, 30 | onDone, 31 | } 32 | 33 | export const contactsReducer = (state, action) => { 34 | const modifier = modifiers[action.type] 35 | modifier(state, action.payload) 36 | } 37 | -------------------------------------------------------------------------------- /src/email-confirm/actions/onConfirm.js: -------------------------------------------------------------------------------- 1 | export const onConfirm = activationToken => ({ 2 | widget: 'emailConfirm', 3 | type: 'onConfirm', 4 | payload: { activationToken }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/email-confirm/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/email-confirm/fetchers/post' 2 | 3 | export function postAction(activationToken) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'emailConfirm', 7 | type: 'postStart', 8 | }) 9 | postFetcher(activationToken) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'emailConfirm', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'emailConfirm', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/email-confirm/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(activationToken) { 4 | return fetch({ method: 'POST', url: `email-confirm`, body: { activationToken } }) 5 | } 6 | -------------------------------------------------------------------------------- /src/email-confirm/reducer.js: -------------------------------------------------------------------------------- 1 | import { errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { postAction } from 'src/email-confirm/actions/post' 3 | import { dispatch } from 'src/store' 4 | 5 | export function postStart(state) { 6 | fetchingStart(state) 7 | state.emailConfirm.status = 'loading' 8 | } 9 | 10 | export function postEnd(state) { 11 | fetchingEnd(state) 12 | state.emailConfirm.status = 'success' 13 | } 14 | 15 | export function postError(state, error) { 16 | fetchingEnd(state) 17 | state.emailConfirm.status = 'fail' 18 | errorSet(state, { show: true, error }) 19 | } 20 | 21 | export function onConfirm(state, { activationToken }) { 22 | setTimeout(() => dispatch(postAction(activationToken)), 0) 23 | } 24 | 25 | const modifiers = { 26 | postStart, 27 | postEnd, 28 | postError, 29 | onConfirm, 30 | } 31 | 32 | export const emailConfirmReducer = (state, action) => { 33 | const modifier = modifiers[action.type] 34 | modifier(state, action.payload) 35 | } 36 | -------------------------------------------------------------------------------- /src/entries/actions/delete.js: -------------------------------------------------------------------------------- 1 | import { deleteFetcher } from 'src/entries/fetchers/delete' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | 4 | export function deleteAction(entry) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'entries', 9 | type: 'deleteStart', 10 | }) 11 | deleteFetcher(projectId, entry.id) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'entries', 15 | type: 'deleteEnd', 16 | }) 17 | dispatch(getDataAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'entries', 22 | type: 'deleteError', 23 | payload: error, 24 | }) 25 | dispatch(getDataAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/entries/actions/onAddDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onAddDialogDone = entry => ({ 2 | widget: 'entries', 3 | type: 'onAddDialogDone', 4 | payload: { entry }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onAddEntry.js: -------------------------------------------------------------------------------- 1 | export const onAddEntry = model => ({ 2 | widget: 'entries', 3 | type: 'onAddEntry', 4 | payload: { model }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onConfirmDialogConfirm.js: -------------------------------------------------------------------------------- 1 | export const onConfirmDialogConfirm = () => ({ 2 | widget: 'entries', 3 | type: 'onConfirmDialogConfirm', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onDeleteEntry.js: -------------------------------------------------------------------------------- 1 | export const onDeleteEntry = id => ({ 2 | widget: 'entries', 3 | type: 'onDeleteEntry', 4 | payload: { id }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onDialogClose.js: -------------------------------------------------------------------------------- 1 | export const onDialogClose = dialogType => ({ 2 | widget: 'entries', 3 | type: 'onDialogClose', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onDialogExited.js: -------------------------------------------------------------------------------- 1 | export const onDialogExited = dialogType => ({ 2 | widget: 'entries', 3 | type: 'onDialogExited', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onEditDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onEditDialogDone = entry => ({ 2 | widget: 'entries', 3 | type: 'onEditDialogDone', 4 | payload: { entry }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/onEditEntry.js: -------------------------------------------------------------------------------- 1 | export const onEditEntry = (entry, model) => ({ 2 | widget: 'entries', 3 | type: 'onEditEntry', 4 | payload: { entry, model }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/entries/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/entries/fetchers/post' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | import { getAssetControlsWithFile } from 'src/lib/helpers/getAssetControlsWithFile' 4 | import { postFileFetcher } from 'src/entries/fetchers/postFileFetcher' 5 | 6 | export function postAction(entry) { 7 | return function(dispatch, getState) { 8 | const { projectId } = getState().global 9 | const errorHandler = error => { 10 | dispatch({ 11 | widget: 'entries', 12 | type: 'postError', 13 | payload: error, 14 | }) 15 | dispatch(getDataAction()) 16 | } 17 | dispatch({ 18 | widget: 'entries', 19 | type: 'postStart', 20 | }) 21 | Promise.all( 22 | getAssetControlsWithFile(entry).map(assetControl => { 23 | return postFileFetcher(projectId, assetControl.value).then(fileUrl => { 24 | assetControl.value = fileUrl 25 | }) 26 | }) 27 | ) 28 | .then(() => { 29 | postFetcher(projectId, entry) 30 | .then(() => { 31 | dispatch({ 32 | widget: 'entries', 33 | type: 'postEnd', 34 | }) 35 | dispatch(getDataAction()) 36 | }) 37 | .catch(errorHandler) 38 | }) 39 | .catch(errorHandler) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/entries/actions/put.js: -------------------------------------------------------------------------------- 1 | import { putFetcher } from 'src/entries/fetchers/put' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | import { getAssetControlsWithFile } from 'src/lib/helpers/getAssetControlsWithFile' 4 | import { postFileFetcher } from 'src/entries/fetchers/postFileFetcher' 5 | 6 | export function putAction(oldEntry, newEntry) { 7 | return function(dispatch, getState) { 8 | const { projectId } = getState().global 9 | const errorHandler = error => { 10 | dispatch({ 11 | widget: 'entries', 12 | type: 'putError', 13 | payload: error, 14 | }) 15 | dispatch(getDataAction()) 16 | } 17 | dispatch({ 18 | widget: 'entries', 19 | type: 'putStart', 20 | }) 21 | Promise.all( 22 | getAssetControlsWithFile(newEntry).map(assetControl => { 23 | return postFileFetcher(projectId, assetControl.value).then(fileUrl => { 24 | assetControl.value = fileUrl 25 | }) 26 | }) 27 | ) 28 | .then(() => { 29 | putFetcher(projectId, newEntry) 30 | .then(() => { 31 | dispatch({ 32 | widget: 'entries', 33 | type: 'putEnd', 34 | }) 35 | dispatch(getDataAction()) 36 | }) 37 | .catch(errorHandler) 38 | }) 39 | .catch(errorHandler) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/entries/components/basic/EntryStringValue.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string } from 'prop-types' 3 | 4 | export class EntryStringValue extends Component { 5 | static propTypes = { 6 | value: string, 7 | } 8 | 9 | static defaultProps = { 10 | value: '', 11 | } 12 | 13 | render() { 14 | const { value } = this.props 15 | return !value ? ( 16 |
Empty string
17 | ) : ( 18 |
{`${value.slice(0, 350)}${value.length > 350 ? '...' : ''}`}
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/entries/components/cards/ArrayCard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | dividerContainer: { 7 | '&:last-child > $divider': { 8 | display: 'none', 9 | }, 10 | }, 11 | divider: {}, 12 | error: { 13 | color: colors.error.main, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/entries/components/cards/ArraySubcard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Icon } from 'src/lib/components/Icon' 3 | import { instanceOf, arrayOf } from 'prop-types' 4 | import { Subcard } from 'src/lib/components/Subcard' 5 | import { colors } from 'src/colors' 6 | import { ArraySubmodelType } from 'src/lib/types/models/ArraySubmodelType' 7 | import { SubcardFactory } from 'src/entries/components/cards/SubcardFactory' 8 | import { SubcardDivider } from 'src/lib/components/SubcardDivider' 9 | import { EntryType } from 'src/lib/types/entries/EntryType' 10 | import { ArraySubentryType } from 'src/lib/types/entries/ArraySubentryType' 11 | 12 | import { cn } from './ArraySubcard.style' 13 | 14 | export class ArraySubcard extends Component { 15 | static propTypes = { 16 | model: instanceOf(ArraySubmodelType).isRequired, 17 | entries: arrayOf(instanceOf(EntryType)).isRequired, 18 | value: instanceOf(ArraySubentryType).isRequired, 19 | } 20 | 21 | render() { 22 | const { model, value, entries } = this.props 23 | return ( 24 | } 29 | labels={[model.type]} 30 | noDivider={!value.value.length} 31 | > 32 | {value.value.length ? null :
Empty array
} 33 | {value.value.map((item, index) => ( 34 |
35 | 36 | 37 |
38 | ))} 39 |
40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/entries/components/cards/ArraySubcard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | dividerContainer: { 6 | '&:last-child > $divider': { 7 | display: 'none', 8 | }, 9 | }, 10 | divider: {}, 11 | }) 12 | .attach() 13 | 14 | export const cn = classes 15 | -------------------------------------------------------------------------------- /src/entries/components/cards/CardFactory.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { instanceOf } from 'prop-types' 3 | import { ObjectCard } from 'src/entries/components/cards/ObjectCard' 4 | import { ModelType } from 'src/lib/types/models/ModelType' 5 | import { BasicTypeCard } from 'src/entries/components/cards/BasicTypeCard' 6 | import { ArrayCard } from 'src/entries/components/cards/ArrayCard' 7 | 8 | export class CardFactory extends Component { 9 | static propTypes = { 10 | model: instanceOf(ModelType).isRequired, 11 | } 12 | 13 | render() { 14 | const { model, ...props } = this.props 15 | const MAP = { 16 | 'string-line': BasicTypeCard, 17 | 'string-multiline': BasicTypeCard, 18 | 'string-markdown': BasicTypeCard, 19 | 'string-html': BasicTypeCard, 20 | number: BasicTypeCard, 21 | boolean: BasicTypeCard, 22 | object: ObjectCard, 23 | array: ArrayCard, 24 | enum: BasicTypeCard, 25 | reference: BasicTypeCard, 26 | asset: BasicTypeCard, 27 | } 28 | const Component = MAP[model.type] || (() =>
unknown type
) 29 | return 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/entries/components/cards/ObjectCard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | dividerContainer: { 7 | '&:last-child > $divider': { 8 | display: 'none', 9 | }, 10 | }, 11 | divider: {}, 12 | error: { 13 | color: colors.error.main, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/entries/components/cards/ObjectSubcard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Icon } from 'src/lib/components/Icon' 3 | import { instanceOf, arrayOf } from 'prop-types' 4 | import { Subcard } from 'src/lib/components/Subcard' 5 | import { colors } from 'src/colors' 6 | import { ObjectSubmodelType } from 'src/lib/types/models/ObjectSubmodelType' 7 | import { SubcardFactory } from 'src/entries/components/cards/SubcardFactory' 8 | import { SubcardDivider } from 'src/lib/components/SubcardDivider' 9 | import { cn } from './ObjectSubcard.style' 10 | import { isEmptyObject } from 'src/entries/helpers/isEmptyObject' 11 | import { sortKeys } from 'src/lib/helpers/sortKeys' 12 | import { EntryType } from 'src/lib/types/entries/EntryType' 13 | import { ObjectSubentryType } from 'src/lib/types/entries/ObjectSubentryType' 14 | 15 | export class ObjectSubcard extends Component { 16 | static propTypes = { 17 | model: instanceOf(ObjectSubmodelType).isRequired, 18 | entries: arrayOf(instanceOf(EntryType)).isRequired, 19 | value: instanceOf(ObjectSubentryType).isRequired, 20 | } 21 | 22 | render() { 23 | const { model, value, entries } = this.props 24 | return ( 25 | } 30 | labels={[model.type]} 31 | noDivider={isEmptyObject(model, value.value)} 32 | > 33 | {!isEmptyObject(model, value.value) ? null :
Empty object
} 34 | {sortKeys(model.properties).map( 35 | (key, i, keys) => 36 | !value.value.hasOwnProperty(key) ? null : ( 37 |
38 | 39 | 40 |
41 | ) 42 | )} 43 |
44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/entries/components/cards/ObjectSubcard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | dividerContainer: { 6 | '&:last-child > $divider': { 7 | display: 'none', 8 | }, 9 | }, 10 | divider: {}, 11 | }) 12 | .attach() 13 | 14 | export const cn = classes 15 | -------------------------------------------------------------------------------- /src/entries/components/cards/SubcardFactory.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { instanceOf } from 'prop-types' 3 | import { ObjectSubcard } from 'src/entries/components/cards/ObjectSubcard' 4 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 5 | import { BasicTypeSubcard } from 'src/entries/components/cards/BasicTypeSubcard' 6 | import { ArraySubcard } from 'src/entries/components/cards/ArraySubcard' 7 | 8 | export class SubcardFactory extends Component { 9 | static propTypes = { 10 | model: instanceOf(SubmodelType).isRequired, 11 | } 12 | 13 | render() { 14 | const { model, ...props } = this.props 15 | const MAP = { 16 | 'string-line': BasicTypeSubcard, 17 | 'string-multiline': BasicTypeSubcard, 18 | 'string-markdown': BasicTypeSubcard, 19 | 'string-html': BasicTypeSubcard, 20 | number: BasicTypeSubcard, 21 | boolean: BasicTypeSubcard, 22 | object: ObjectSubcard, 23 | array: ArraySubcard, 24 | enum: BasicTypeSubcard, 25 | reference: BasicTypeSubcard, 26 | asset: BasicTypeSubcard, 27 | } 28 | const Component = MAP[model.type] || (() =>
unknown subtype
) 29 | return 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/ControlContainer.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | container: { 7 | borderLeft: `3px solid`, 8 | borderLeftColor: colors.secondary.main, 9 | '&.error': { 10 | borderLeftColor: colors.error.main, 11 | }, 12 | '&.disabled': { 13 | borderLeftColor: `${colors.black.t4} !important`, 14 | }, 15 | }, 16 | head: { 17 | position: 'relative', 18 | minHeight: 40, 19 | }, 20 | left: { 21 | position: 'absolute', 22 | left: '0px', 23 | top: '0px', 24 | bottom: '0px', 25 | width: '60px', 26 | display: 'flex', 27 | alignItems: 'center', 28 | '& > div': { 29 | margin: 'auto', 30 | }, 31 | }, 32 | right: { 33 | marginLeft: '60px', 34 | }, 35 | buttons: { 36 | display: 'inline-block', 37 | }, 38 | body: { 39 | padding: '30px 0 0 32px', 40 | '&.noBodyPedding': { 41 | padding: '20px 0 0 10px', 42 | }, 43 | }, 44 | label: { 45 | minWidth: 50, 46 | minHeight: 20, 47 | fontSize: '0.62rem', 48 | }, 49 | errorMsg: { 50 | color: colors.error.main, 51 | }, 52 | }) 53 | .attach() 54 | 55 | export const cn = classes 56 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/ControlFactory.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { instanceOf, oneOfType } from 'prop-types' 3 | import { ModelType } from 'src/lib/types/models/ModelType' 4 | import { StringLineControl } from 'src/entries/components/dialogs/controls/StringLineControl' 5 | import { BooleanControl } from 'src/entries/components/dialogs/controls/BooleanControl' 6 | import { NumberControl } from 'src/entries/components/dialogs/controls/NumberControl' 7 | import { StringMultilineControl } from 'src/entries/components/dialogs/controls/StringMultilineControl' 8 | import { HtmlControl } from 'src/entries/components/dialogs/controls/HtmlControl' 9 | import { MarkdownControl } from 'src/entries/components/dialogs/controls/MarkdownControl' 10 | import { ObjectControl } from 'src/entries/components/dialogs/controls/ObjectControl' 11 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 12 | import { ArrayControl } from 'src/entries/components/dialogs/controls/ArrayControl' 13 | import { EnumControl } from 'src/entries/components/dialogs/controls/EnumControl' 14 | import { ReferenceControl } from 'src/entries/components/dialogs/controls/ReferenceControl' 15 | import { AssetControl } from 'src/entries/components/dialogs/controls/AssetControl' 16 | 17 | export class ControlFactory extends Component { 18 | static propTypes = { 19 | model: oneOfType([instanceOf(ModelType), instanceOf(SubmodelType)]).isRequired, 20 | } 21 | 22 | render() { 23 | const { model, ...props } = this.props 24 | const MAP = { 25 | 'string-line': StringLineControl, 26 | 'string-multiline': StringMultilineControl, 27 | 'string-html': HtmlControl, 28 | 'string-markdown': MarkdownControl, 29 | boolean: BooleanControl, 30 | number: NumberControl, 31 | object: ObjectControl, 32 | array: ArrayControl, 33 | enum: EnumControl, 34 | reference: ReferenceControl, 35 | asset: AssetControl, 36 | } 37 | const Component = MAP[model.type] 38 | return 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/EntryDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, bool, string, instanceOf, arrayOf } from 'prop-types' 3 | import { ModelType } from 'src/lib/types/models/ModelType' 4 | import { EntryType } from 'src/lib/types/entries/EntryType' 5 | import { Dialog } from 'src/lib/components/Dialog' 6 | import { EntryDialogInner } from 'src/entries/components/dialogs/EntryDialogInner' 7 | 8 | export class EntryDialog extends Component { 9 | static propTypes = { 10 | open: bool.isRequired, 11 | onDone: func.isRequired, 12 | onClose: func.isRequired, 13 | model: instanceOf(ModelType), 14 | entry: instanceOf(EntryType), 15 | title: string.isRequired, 16 | onExited: func.isRequired, 17 | entries: arrayOf(instanceOf(EntryType)).isRequired, 18 | } 19 | 20 | static defaultProps = { 21 | model: null, 22 | entry: null, 23 | } 24 | 25 | render() { 26 | const { open, onClose, onDone, model, entry, title, onExited, entries } = this.props 27 | if (!model) return null 28 | return ( 29 | 30 | 31 | 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/controls/ArrayControl.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | controlContainer: { 7 | marginBottom: '24px', 8 | '&:last-child': { 9 | marginBottom: '0', 10 | }, 11 | }, 12 | disabled: { 13 | color: `${colors.black.t4}`, 14 | }, 15 | addBtnContainer: { 16 | marginTop: '24px', 17 | borderLeft: `3px solid ${colors.secondary.main}`, 18 | '&.disabled': { 19 | borderLeftColor: `${colors.black.t4} !important`, 20 | }, 21 | }, 22 | }) 23 | .attach() 24 | 25 | export const cn = classes 26 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/controls/BooleanControl.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, bool, oneOfType, instanceOf } from 'prop-types' 3 | import { Switch } from 'src/lib/components/controls/Switch' 4 | import { ControlContainer } from 'src/entries/components/dialogs/ControlContainer' 5 | import { BooleanModelType } from 'src/lib/types/models/BooleanModelType' 6 | import { BooleanSubmodelType } from 'src/lib/types/models/BooleanSubmodelType' 7 | 8 | export class BooleanControl extends Component { 9 | static propTypes = { 10 | model: oneOfType([instanceOf(BooleanModelType), instanceOf(BooleanSubmodelType)]).isRequired, 11 | value: bool.isRequired, 12 | onBooleanChange: func.isRequired, 13 | disabled: bool, 14 | propBtnStatus: string, 15 | onPropBtnStatusChange: func, 16 | onItemDelete: func, 17 | onItemUp: func, 18 | onItemDown: func, 19 | } 20 | 21 | static defaultProps = { 22 | /* if it is object prop */ 23 | disabled: false, 24 | onPropBtnStatusChange: null, 25 | propBtnStatus: null, 26 | /* if it is array item */ 27 | onItemDelete: null, 28 | onItemUp: null, 29 | onItemDown: null, 30 | } 31 | 32 | render() { 33 | const { 34 | model, 35 | value, 36 | onBooleanChange, 37 | disabled, 38 | propBtnStatus, 39 | onPropBtnStatusChange, 40 | onItemDelete, 41 | onItemUp, 42 | onItemDown, 43 | } = this.props 44 | return ( 45 | 58 | 59 | 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/controls/ObjectControl.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | controlContainer: { 7 | marginBottom: '24px', 8 | '&:last-child': { 9 | marginBottom: '0', 10 | }, 11 | }, 12 | disabled: { 13 | color: `${colors.black.t4}`, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/entries/components/dialogs/controls/StringLineControl.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, bool, oneOfType, instanceOf } from 'prop-types' 3 | import { ControlContainer } from 'src/entries/components/dialogs/ControlContainer' 4 | import { StringModelType } from 'src/lib/types/models/StringModelType' 5 | import { StringSubmodelType } from 'src/lib/types/models/StringSubmodelType' 6 | import { TextInput } from 'src/lib/components/controls/TextInput' 7 | 8 | export class StringLineControl extends Component { 9 | static propTypes = { 10 | model: oneOfType([instanceOf(StringModelType), instanceOf(StringSubmodelType)]).isRequired, 11 | value: string.isRequired, 12 | error: string.isRequired, 13 | onStringChange: func.isRequired, 14 | onBlur: func.isRequired, 15 | disabled: bool, 16 | propBtnStatus: string, 17 | onPropBtnStatusChange: func, 18 | onItemDelete: func, 19 | onItemUp: func, 20 | onItemDown: func, 21 | } 22 | 23 | static defaultProps = { 24 | /* if it is object prop */ 25 | disabled: false, 26 | onPropBtnStatusChange: null, 27 | propBtnStatus: null, 28 | /* if it is array item */ 29 | onItemDelete: null, 30 | onItemUp: null, 31 | onItemDown: null, 32 | } 33 | 34 | render() { 35 | const { 36 | model, 37 | value, 38 | error, 39 | onStringChange, 40 | onBlur, 41 | disabled, 42 | propBtnStatus, 43 | onPropBtnStatusChange, 44 | onItemDelete, 45 | onItemUp, 46 | onItemDown, 47 | } = this.props 48 | return ( 49 | 63 | 64 | 65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/entries/fetchers/delete.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function deleteFetcher(projectId, id) { 4 | return fetch({ method: 'DELETE', url: `projects/${projectId}/entries/${id}` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/entries/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(projectId, entry) { 4 | return fetch({ method: 'POST', url: `projects/${projectId}/entries`, body: entry }) 5 | } 6 | -------------------------------------------------------------------------------- /src/entries/fetchers/postFileFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFileFetcher(projectId, file) { 4 | return fetch({ method: 'POST', url: `projects/${projectId}/files`, file }).then(({ name, id }) => { 5 | return `/files/${id}/${name}` 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /src/entries/fetchers/put.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function putFetcher(projectId, entry) { 4 | return fetch({ method: 'PUT', url: `projects/${projectId}/entries/${entry.id}`, body: entry }) 5 | } 6 | -------------------------------------------------------------------------------- /src/entries/helpers/isEmptyObject.js: -------------------------------------------------------------------------------- 1 | export const isEmptyObject = (model, value) => 2 | !Object.keys(model.properties).some(key => Object.keys(value).some(item => item === key)) 3 | -------------------------------------------------------------------------------- /src/explorer/components/Explorer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | // import { arrayOf, func, bool, object } from 'prop-types' 3 | import { connect } from 'react-redux' 4 | import SwaggerUI from 'swagger-ui' 5 | // import { exampleConfig } from './exampleConfig' 6 | import { config } from './config' 7 | 8 | class AExplorer extends Component { 9 | static propTypes = {} 10 | 11 | id = `swagger-id-${Math.round(Math.random() * 1e9)}` 12 | 13 | componentDidMount() { 14 | const { id } = this 15 | SwaggerUI({ 16 | dom_id: `#${id}`, 17 | spec: config, 18 | }) 19 | } 20 | 21 | render() { 22 | const { id } = this 23 | return ( 24 |
25 |
26 |
27 | ) 28 | } 29 | } 30 | 31 | export const Explorer = connect( 32 | state => ({}), 33 | dispatch => ({}) 34 | )(AExplorer) 35 | -------------------------------------------------------------------------------- /src/global/actions/errorSet.js: -------------------------------------------------------------------------------- 1 | export const setErrorAction = (show, error) => ({ 2 | widget: 'global', 3 | type: 'errorSet', 4 | payload: { 5 | show, 6 | error, 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /src/global/actions/fetchingEnd.js: -------------------------------------------------------------------------------- 1 | export const fetchingEndAction = () => ({ 2 | widget: 'global', 3 | type: 'fetchingEnd', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/global/actions/fetchingStart.js: -------------------------------------------------------------------------------- 1 | export const fetchingStartAction = () => ({ 2 | widget: 'global', 3 | type: 'fetchingStart', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/global/actions/getData.js: -------------------------------------------------------------------------------- 1 | import { getEntriesFetcher } from 'src/global/fetchers/getEntries' 2 | import { getModelsFetcher } from 'src/global/fetchers/getModels' 3 | import { getProjectsFetcher } from 'src/global/fetchers/getProjects' 4 | import Promise from 'src/lib/services/Promise' 5 | import { createEntry } from 'src/lib/helpers/createEntry' 6 | import { createModel } from 'src/lib/helpers/createModel' 7 | 8 | export const getDataAction = function(projectId) { 9 | return function(dispatch, getState) { 10 | const state = getState() 11 | const { projectId: stateProjectId } = state.global 12 | if (state.models.loading.get && state.entries.loading.get) return 13 | dispatch({ 14 | widget: 'global', 15 | type: 'getStart', 16 | }) 17 | Promise.all([ 18 | getProjectsFetcher(), 19 | getEntriesFetcher(projectId || stateProjectId), 20 | getModelsFetcher(projectId || stateProjectId), 21 | ]) 22 | .then(([projects, entries, models]) => { 23 | dispatch({ 24 | widget: 'global', 25 | type: 'getEnd', 26 | payload: { 27 | projects: projects, 28 | models: models.map(model => createModel(model)), 29 | entries: entries.map(entry => createEntry(entry)), 30 | }, 31 | }) 32 | }) 33 | .catch(error => { 34 | dispatch({ 35 | widget: 'global', 36 | type: 'getError', 37 | payload: error, 38 | }) 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/global/actions/projectIdSet.js: -------------------------------------------------------------------------------- 1 | export const projectIdSet = projectId => ({ 2 | widget: 'global', 3 | type: 'projectIdSet', 4 | payload: { projectId }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/global/actions/redirectSet.js: -------------------------------------------------------------------------------- 1 | export const redirectSetAction = url => ({ 2 | widget: 'global', 3 | type: 'redirectSet', 4 | payload: url, 5 | }) 6 | -------------------------------------------------------------------------------- /src/global/components/Content.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { node } from 'prop-types' 3 | 4 | import { cn } from './Content.style.js' 5 | 6 | export class Content extends React.Component { 7 | static propTypes = { 8 | children: node.isRequired, 9 | } 10 | 11 | render() { 12 | const { children } = this.props 13 | return
{children}
14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/global/components/Content.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | content: { 6 | padding: '20px', 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/global/components/ErrorContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, shape, bool } from 'prop-types' 3 | import Snackbar from 'material-ui/Snackbar' 4 | import { connect } from 'react-redux' 5 | import { setErrorAction } from 'src/global/actions/errorSet' 6 | 7 | import { cn } from './ErrorContainer.style.js' 8 | 9 | class AErrorContainer extends Component { 10 | static propTypes = { 11 | errorSet: func.isRequired, 12 | error: shape({ 13 | show: bool.isRequired, 14 | message: string.isRequired, 15 | }).isRequired, 16 | } 17 | 18 | render() { 19 | const { error, errorSet } = this.props 20 | return ( 21 | errorSet(false)} 26 | message={{error.message}} 27 | /> 28 | ) 29 | } 30 | } 31 | 32 | export const ErrorContainer = connect( 33 | state => ({ 34 | error: state.global.error, 35 | }), 36 | dispatch => ({ 37 | errorSet: (...props) => dispatch(setErrorAction(...props)), 38 | }) 39 | )(AErrorContainer) 40 | -------------------------------------------------------------------------------- /src/global/components/ErrorContainer.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | errorAlert: { 7 | '& > div': { 8 | 'background-color': colors.error.main, 9 | }, 10 | }, 11 | }) 12 | .attach() 13 | 14 | export const cn = classes 15 | -------------------------------------------------------------------------------- /src/global/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { node } from 'prop-types' 3 | import { cn } from './Header.style' 4 | 5 | export class Header extends Component { 6 | static propTypes = { 7 | children: node.isRequired, 8 | } 9 | 10 | render() { 11 | const { children } = this.props 12 | return
{children}
13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/global/components/Header.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | header: { 7 | height: '36px', 8 | background: colors.tertiary.d2, 9 | display: 'flex', 10 | alignItems: 'center', 11 | justifyContent: 'flex-end', 12 | padding: '20px', 13 | borderBottom: `1px solid ${colors.grey.l3t5}`, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/global/components/Loader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { cn } from 'src/global/components/Loader.style' 3 | import { LinearProgress } from 'src/lib/components/LinearProgress' 4 | import { connect } from 'react-redux' 5 | import { arrayOf, bool } from 'prop-types' 6 | 7 | export class ALoader extends Component { 8 | static propTypes = { 9 | loading: arrayOf(bool).isRequired, 10 | } 11 | 12 | render() { 13 | const { loading } = this.props 14 | return loading.some(bool => bool) ? ( 15 |
16 | 17 |
18 | ) : null 19 | } 20 | } 21 | 22 | export const Loader = connect( 23 | state => ({ 24 | loading: state.global.loading, 25 | }), 26 | dispatch => ({}) 27 | )(ALoader) 28 | -------------------------------------------------------------------------------- /src/global/components/Loader.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | loader: { 6 | width: '100%', 7 | background: 'white', 8 | position: 'fixed', 9 | top: '0', 10 | zIndex: 1, 11 | }, 12 | }) 13 | .attach() 14 | 15 | export const cn = classes 16 | -------------------------------------------------------------------------------- /src/global/components/Redirect.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func } from 'prop-types' 3 | import { Redirect as ReactRedirect } from 'react-router' 4 | import { connect } from 'react-redux' 5 | import { redirectSetAction } from 'src/global/actions/redirectSet' 6 | 7 | class ARedirect extends Component { 8 | static propTypes = { 9 | redirectSet: func.isRequired, 10 | } 11 | 12 | componentDidMount() { 13 | const { redirectSet } = this.props 14 | redirectSet('') 15 | } 16 | 17 | render() { 18 | const { redirectSet, ...props } = this.props 19 | return 20 | } 21 | } 22 | 23 | export const Redirect = connect( 24 | state => ({}), 25 | dispatch => ({ 26 | redirectSet: (...props) => dispatch(redirectSetAction(...props)), 27 | }) 28 | )(ARedirect) 29 | -------------------------------------------------------------------------------- /src/global/components/RedirectContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string } from 'prop-types' 3 | import { Redirect } from 'src/global/components/Redirect' 4 | import { connect } from 'react-redux' 5 | 6 | class ARedirectContainer extends Component { 7 | static propTypes = { 8 | redirect: string.isRequired, 9 | } 10 | 11 | render() { 12 | const { redirect } = this.props 13 | return !redirect ? null : 14 | } 15 | } 16 | 17 | export const RedirectContainer = connect( 18 | state => ({ 19 | redirect: state.global.redirect, 20 | }), 21 | dispatch => ({}) 22 | )(ARedirectContainer) 23 | -------------------------------------------------------------------------------- /src/global/fetchers/getEntries.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const getEntriesFetcher = projectId => { 4 | return fetch({ method: 'GET', url: `projects/${projectId}/entries` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/global/fetchers/getModels.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const getModelsFetcher = projectId => { 4 | return fetch({ method: 'GET', url: `projects/${projectId}/models` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/global/fetchers/getProjects.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const getProjectsFetcher = () => { 4 | return fetch({ method: 'GET', url: `projects` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { App } from './App' 4 | import * as serviceWorker from './serviceWorker' 5 | 6 | ReactDOM.render(, document.getElementById('root')) 7 | 8 | // If you want your app to work offline and load faster, you can change 9 | // unregister() to register() below. Note this comes with some pitfalls. 10 | // Learn more about service workers: https://bit.ly/CRA-PWA 11 | serviceWorker.unregister() 12 | -------------------------------------------------------------------------------- /src/index/components/Index.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | card: { 7 | padding: '20px', 8 | background: colors.white.main, 9 | boxShadow: 10 | '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)', 11 | borderRadius: '2px', 12 | }, 13 | container: { 14 | position: 'relative', 15 | }, 16 | left: { 17 | position: 'absolute', 18 | }, 19 | right: { 20 | marginLeft: '68px', 21 | }, 22 | }) 23 | .attach() 24 | 25 | export const cn = classes 26 | -------------------------------------------------------------------------------- /src/landing/components/GithubCorner.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { cn } from './GithubCorner.style' 4 | 5 | export class GithubCorner extends React.Component { 6 | render() { 7 | return ( 8 | 13 | 39 | 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/landing/components/GithubCorner.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | svg: { 6 | fill: '#151513', 7 | color: '#fff', 8 | position: 'absolute', 9 | top: 0, 10 | border: 0, 11 | left: 0, 12 | transform: 'scale(-1, 1)', 13 | }, 14 | path: { 15 | transformOrigin: '130px 106px', 16 | }, 17 | }) 18 | .attach() 19 | 20 | export const cn = classes 21 | -------------------------------------------------------------------------------- /src/landing/components/Landing.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | header: { 7 | background: colors.white.main, 8 | boxShadow: '0px 1px 4px 0px rgba(13,26,44,.23)', 9 | padding: 22, 10 | }, 11 | body: { 12 | width: 760, 13 | margin: 'auto', 14 | padding: '0 4px', 15 | }, 16 | messages: { 17 | marginTop: 60, 18 | }, 19 | message: { 20 | background: colors.primary.l5t5, 21 | padding: 15, 22 | borderRadius: 2, 23 | marginTop: 15, 24 | }, 25 | title: { 26 | paddingTop: 40, 27 | fontSize: 40, 28 | fontWeight: 300, 29 | lineHeight: '52px', 30 | }, 31 | explanation: { 32 | fontSize: 14, 33 | lineHeight: 1.7, 34 | }, 35 | }) 36 | .attach() 37 | 38 | export const cn = classes 39 | -------------------------------------------------------------------------------- /src/lib/components/Button.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { node, bool, oneOf, func } from 'prop-types' 3 | import { Button as AButton } from 'material-ui' 4 | 5 | import { cn } from './Button.style' 6 | 7 | export class Button extends Component { 8 | static propTypes = { 9 | onClick: func.isRequired, 10 | children: node.isRequired, 11 | color: oneOf(['primary', 'secondary', 'accent']), 12 | size: oneOf(['lg', 'md', 'sm', 'xs']), 13 | filled: bool, 14 | outlined: bool, 15 | disabled: bool, 16 | } 17 | 18 | static defaultProps = { 19 | color: 'primary', 20 | size: 'lg', 21 | filled: false, 22 | outlined: false, 23 | disabled: false, 24 | } 25 | 26 | render() { 27 | const { onClick, children, size, color, filled, outlined, disabled } = this.props 28 | const MAP_COLORS = { 29 | primary: cn.primary, 30 | secondary: cn.secondary, 31 | accent: cn.accent, 32 | } 33 | const MAP_SIZE = { 34 | lg: cn.lg, 35 | md: cn.md, 36 | sm: cn.sm, 37 | xs: cn.xs, 38 | } 39 | const colorClasses = disabled ? cn.disabled : `${MAP_COLORS[color]} ${filled ? 'filled' : ''}` 40 | const sizeClasses = MAP_SIZE[size] 41 | const outlineClasses = outlined ? 'outlined' : '' 42 | return ( 43 | 50 | {children} 51 | 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/lib/components/Card.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | container: { 6 | background: '#fff', 7 | boxShadow: 8 | '0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12)', 9 | borderRadius: '2px', 10 | marginBottom: '15px', 11 | position: 'relative', 12 | '&:hover $cardButtons': { 13 | visibility: 'visible !important', 14 | }, 15 | }, 16 | cardButtons: { 17 | display: 'inline-block', 18 | visibility: 'hidden', 19 | }, 20 | left: { 21 | borderRadius: '2px 0 0 2px', 22 | position: 'absolute', 23 | left: '0px', 24 | top: '0px', 25 | bottom: '0px', 26 | width: '80px', 27 | display: 'flex', 28 | alignItems: 'center', 29 | '& > div': { 30 | margin: 'auto', 31 | }, 32 | }, 33 | right: { 34 | marginLeft: '80px', 35 | }, 36 | head: { 37 | padding: '10px', 38 | }, 39 | body: { 40 | padding: '10px', 41 | }, 42 | exclamationIcon: { 43 | display: 'inline-block', 44 | position: 'relative', 45 | top: '4px', 46 | }, 47 | }) 48 | .attach() 49 | 50 | export const cn = classes 51 | -------------------------------------------------------------------------------- /src/lib/components/CircledIcon.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string, node, number } from 'prop-types' 3 | import { Avatar } from 'material-ui' 4 | 5 | export class CircledIcon extends Component { 6 | static propTypes = { 7 | children: node.isRequired, 8 | size: number, 9 | color: string, 10 | className: string, 11 | } 12 | 13 | static defaultProps = { 14 | size: 50, 15 | color: '#fff', 16 | className: '', 17 | } 18 | 19 | render() { 20 | const { children, size, color, className } = this.props 21 | return ( 22 | 23 | {children} 24 | 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/components/CircularProgress.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { number } from 'prop-types' 3 | import { CircularProgress as ACircularProgress } from 'material-ui/Progress' 4 | 5 | import { cn } from './CircularProgress.style' 6 | 7 | export class CircularProgress extends Component { 8 | static propTypes = { 9 | size: number, 10 | } 11 | 12 | static defaultProps = { 13 | size: 100, 14 | } 15 | 16 | render() { 17 | const { size } = this.props 18 | return 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/components/CircularProgress.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | container: { 7 | color: colors.primary.main, 8 | }, 9 | }) 10 | .attach() 11 | 12 | export const cn = classes 13 | -------------------------------------------------------------------------------- /src/lib/components/ConfirmDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, bool, string } from 'prop-types' 3 | import { Dialog } from 'src/lib/components/Dialog' 4 | import { DialogActions } from 'src/lib/components/DialogActions' 5 | import { DialogContent } from 'src/lib/components/DialogContent' 6 | import { Typography } from 'src/lib/components/Typography' 7 | import { Button } from 'src/lib/components/Button' 8 | 9 | export class ConfirmDialog extends Component { 10 | static propTypes = { 11 | title: string.isRequired, 12 | text: string.isRequired, 13 | open: bool.isRequired, 14 | onClose: func.isRequired, 15 | onDone: func.isRequired, 16 | onExited: func, 17 | } 18 | 19 | static defaultProps = { 20 | onExited: () => {}, 21 | } 22 | 23 | render() { 24 | const { title, text, onClose, onDone, onExited, open } = this.props 25 | return ( 26 | 27 | 28 | {text} 29 | 30 | 31 | 34 | 35 | 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/components/Dialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ADialog from 'material-ui/Dialog' 3 | import { Divider } from 'material-ui' 4 | import { DialogTitle } from 'material-ui/Dialog' 5 | import { func, node, bool, number, string } from 'prop-types' 6 | import { LinearProgress } from 'src/lib/components/LinearProgress' 7 | 8 | import { cn } from './Dialog.style' 9 | 10 | export class Dialog extends Component { 11 | static propTypes = { 12 | open: bool.isRequired, 13 | onClose: func.isRequired, 14 | onExited: func, 15 | onEnter: func, 16 | children: node.isRequired, 17 | progress: number, 18 | title: string.isRequired, 19 | maxWidth: string, 20 | } 21 | 22 | static defaultProps = { 23 | progress: null, 24 | onEnter: () => {}, 25 | onExited: () => {}, 26 | maxWidth: 'md', 27 | } 28 | 29 | render() { 30 | const { onEnter, open, onClose, children, onExited, progress, title, maxWidth } = this.props 31 | return ( 32 | 45 | {title} 46 | {progress === null ? ( 47 | 48 | ) : ( 49 |
50 | 51 |
52 | )} 53 | {children} 54 |
55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/lib/components/Dialog.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | dialog: { 6 | 'align-items': 'flex-start !important', 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/lib/components/DialogActions.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Grid } from 'material-ui' 3 | import { func, node } from 'prop-types' 4 | import { Button } from 'src/lib/components/Button' 5 | import { DialogActions as ADialogActions } from 'material-ui/Dialog' 6 | 7 | import { cn } from './DialogActions.style' 8 | 9 | export class DialogActions extends Component { 10 | static propTypes = { 11 | onClose: func.isRequired, 12 | children: node, 13 | } 14 | 15 | static defaultProps = { 16 | children: null, 17 | } 18 | 19 | render() { 20 | const { onClose, children } = this.props 21 | return ( 22 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | {children} 33 | 34 | 35 |
36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/components/DialogActions.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | dialogActions: { 6 | padding: '5px', 7 | }, 8 | actionsLeft: { 9 | 'justify-content': 'flex-start', 10 | }, 11 | }) 12 | .attach() 13 | 14 | export const cn = classes 15 | -------------------------------------------------------------------------------- /src/lib/components/DialogContent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { node } from 'prop-types' 3 | import { DialogContent as ADialogContent } from 'material-ui/Dialog' 4 | 5 | import { cn } from './DialogContent.style' 6 | 7 | export class DialogContent extends Component { 8 | static propTypes = { 9 | children: node.isRequired, 10 | } 11 | 12 | render() { 13 | const { children } = this.props 14 | return {children} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/components/DialogContent.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | dialogContent: { 6 | 'padding-top': '24px !important', 7 | overflowY: 'inherit', 8 | }, 9 | }) 10 | .attach() 11 | 12 | export const cn = classes 13 | -------------------------------------------------------------------------------- /src/lib/components/LinearProgress.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bool } from 'prop-types' 3 | import { LinearProgress as ALinearProgress } from 'material-ui/Progress' 4 | import { cn } from './LinearProgress.style' 5 | 6 | export class LinearProgress extends Component { 7 | static propTypes = { 8 | noHeight: bool, 9 | light: bool, 10 | } 11 | 12 | static defaultProps = { 13 | noHeight: false, 14 | light: false, 15 | } 16 | 17 | render() { 18 | const { noHeight, light, ...props } = this.props 19 | return ( 20 | 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/components/LinearProgress.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | progressBg: { 7 | backgroundColor: colors.primary.d2t4, 8 | }, 9 | progressColor: { 10 | backgroundColor: colors.primary.d2, 11 | }, 12 | progressColorLight: { 13 | backgroundColor: colors.secondary.d5t2, 14 | }, 15 | progressNoHeight: { 16 | marginBottom: -5, 17 | }, 18 | }) 19 | .attach() 20 | 21 | export const cn = classes 22 | -------------------------------------------------------------------------------- /src/lib/components/MessageBlock.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { node, number } from 'prop-types' 3 | 4 | import { cn } from './MessageBlock.style' 5 | 6 | export class MessageBlock extends Component { 7 | static propTypes = { 8 | children: node.isRequired, 9 | minWidth: number, 10 | maxWidth: number, 11 | } 12 | 13 | static defaultProps = { 14 | minWidth: 300, 15 | maxWidth: 300, 16 | } 17 | 18 | render() { 19 | const { children, minWidth, maxWidth } = this.props 20 | return ( 21 |
22 | {children} 23 |
24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/components/MessageBlock.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | card: { 7 | padding: '20px', 8 | display: 'inline-block', 9 | background: colors.white.main, 10 | boxShadow: 11 | '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)', 12 | borderRadius: '2px', 13 | }, 14 | }) 15 | .attach() 16 | 17 | export const cn = classes 18 | -------------------------------------------------------------------------------- /src/lib/components/PageContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { node } from 'prop-types' 3 | 4 | import { cn } from './PageContainer.style' 5 | 6 | export class PageContainer extends Component { 7 | static propTypes = { 8 | children: node.isRequired, 9 | } 10 | 11 | render() { 12 | const { children } = this.props 13 | return
{children}
14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/components/PageContainer.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | container: { 7 | minHeight: '100%', 8 | background: colors.tertiary.main, 9 | }, 10 | }) 11 | .attach() 12 | 13 | export const cn = classes 14 | -------------------------------------------------------------------------------- /src/lib/components/Subcard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | container: { 6 | background: '#fff', 7 | borderRadius: '2px', 8 | position: 'relative', 9 | minHeight: 40, 10 | }, 11 | left: { 12 | borderLeft: `4px solid`, 13 | position: 'absolute', 14 | left: '0px', 15 | top: '0px', 16 | bottom: '0px', 17 | width: '60px', 18 | display: 'flex', 19 | alignItems: 'center', 20 | '& > div': { 21 | margin: 'auto', 22 | }, 23 | }, 24 | right: { 25 | marginLeft: '64px', 26 | }, 27 | head: { 28 | padding: '0', 29 | }, 30 | body: { 31 | padding: '10px 0 0', 32 | fontSize: '12px', 33 | }, 34 | label: { 35 | minWidth: 50, 36 | minHeight: 20, 37 | fontSize: '0.62rem', 38 | }, 39 | exclamationIcon: { 40 | display: 'inline-block', 41 | position: 'relative', 42 | top: '3px', 43 | }, 44 | }) 45 | .attach() 46 | 47 | export const cnHoverTrick = () => { 48 | const { classes } = jss 49 | .createStyleSheet({ 50 | hoverContainer: { 51 | '&:hover $cardButtons': { 52 | visibility: 'visible !important', 53 | }, 54 | }, 55 | cardButtons: { 56 | display: 'inline-block', 57 | visibility: 'hidden', 58 | }, 59 | }) 60 | .attach() 61 | 62 | return classes 63 | } 64 | 65 | export const cn = classes 66 | -------------------------------------------------------------------------------- /src/lib/components/SubcardDivider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string } from 'prop-types' 3 | import { Divider } from 'material-ui' 4 | import { cn } from './SubcardDivider.style' 5 | 6 | export class SubcardDivider extends Component { 7 | static propTypes = { 8 | className: string, 9 | } 10 | 11 | static defaultProps = { 12 | className: '', 13 | } 14 | 15 | render() { 16 | const { className } = this.props 17 | return 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/components/SubcardDivider.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | divider: { 6 | margin: '8px 0', 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/lib/components/Typography.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string, node, bool } from 'prop-types' 3 | 4 | import { cn } from './Typography.style' 5 | 6 | export class Typography extends Component { 7 | static propTypes = { 8 | className: string, 9 | type: string.isRequired, 10 | children: node.isRequired, 11 | inline: bool, 12 | disabled: bool, 13 | } 14 | 15 | static defaultProps = { 16 | className: '', 17 | inline: false, 18 | disabled: false, 19 | } 20 | 21 | render() { 22 | const { type, children, className, inline, disabled } = this.props 23 | const MAP = { 24 | bigTransperent: cn.bigTransperent, 25 | light: cn.light, 26 | lg: cn.lg, 27 | md: cn.md, 28 | sm: cn.sm, 29 | xs: cn.xs, 30 | label: cn.label, 31 | } 32 | return ( 33 |
37 | {children} 38 |
39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lib/components/Typography.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | bigTransperent: { 7 | color: 'rgba(0, 0, 0, 0.54)', 8 | fontSize: '34px', 9 | }, 10 | light: { 11 | color: 'rgba(0, 0, 0, 0.87)', 12 | fontSize: '14px', 13 | }, 14 | lg: { 15 | color: 'rgba(0, 0, 0, 0.87)', 16 | fontSize: '22px', 17 | }, 18 | md: { 19 | color: 'rgba(0, 0, 0, 0.87)', 20 | fontSize: '18px', 21 | }, 22 | sm: { 23 | color: 'rgba(0, 0, 0, 0.87)', 24 | fontSize: '14px', 25 | }, 26 | xs: { 27 | color: 'rgba(0, 0, 0, 0.54)', 28 | fontSize: '12px', 29 | }, 30 | label: { 31 | boxSizing: 'border-box', 32 | color: colors.black.l2t2, 33 | fontSize: '0.8125rem', 34 | lineHeight: '1.4em', 35 | background: colors.grey.l5t3, 36 | borderRadius: '2px', 37 | padding: '3px 4px', 38 | minWidth: 56, 39 | minHeight: 24, 40 | textAlign: 'center', 41 | '&$disabled': { 42 | background: colors.white.d5t2, 43 | }, 44 | }, 45 | disabled: { 46 | color: colors.black.t3, 47 | }, 48 | }) 49 | .attach() 50 | 51 | export const cn = classes 52 | -------------------------------------------------------------------------------- /src/lib/components/controls/Checkbox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bool, func } from 'prop-types' 3 | import ACheckbox from 'material-ui/Checkbox' 4 | 5 | import { cn } from './Checkbox.style' 6 | 7 | export class Checkbox extends Component { 8 | static propTypes = { 9 | checked: bool.isRequired, 10 | onChange: func.isRequired, 11 | disabled: bool, 12 | } 13 | 14 | static defaultProps = { 15 | disabled: false, 16 | } 17 | 18 | render() { 19 | const { checked, onChange, disabled } = this.props 20 | return ( 21 | onChange(!checked)} 25 | value="" 26 | classes={{ checked: cn.checked }} 27 | /> 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/components/controls/Checkbox.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | checked: { 7 | color: colors.primary.main, 8 | }, 9 | }) 10 | .attach() 11 | 12 | export const cn = classes 13 | -------------------------------------------------------------------------------- /src/lib/components/controls/CodeEditor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, oneOf, bool } from 'prop-types' 3 | import { cn } from './CodeEditor.style' 4 | const monaco = window.monaco 5 | 6 | class CodeEditorInner extends Component { 7 | static propTypes = { 8 | language: oneOf(['html', 'markdown']).isRequired, 9 | initialValue: string.isRequired, 10 | onChange: func.isRequired, 11 | onBlur: func.isRequired, 12 | } 13 | 14 | id = `editor-container-${Math.round(Math.random() * 1e9)}` 15 | 16 | componentDidMount() { 17 | const { initialValue, onChange, language } = this.props 18 | const { id } = this 19 | const editor = monaco.editor.create(document.getElementById(id), { 20 | value: initialValue, 21 | language: language, 22 | }) 23 | editor.onDidChangeModelContent(event => { 24 | const value = editor.getValue() 25 | onChange(value) 26 | }) 27 | } 28 | 29 | render() { 30 | const { onBlur } = this.props 31 | const { id } = this 32 | return
33 | } 34 | } 35 | 36 | export class CodeEditor extends Component { 37 | static propTypes = { 38 | disabled: bool, 39 | } 40 | 41 | static defaultProps = { 42 | disabled: false, 43 | } 44 | 45 | render() { 46 | const { disabled, ...props } = this.props 47 | return disabled ?
: 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/lib/components/controls/CodeEditor.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | container: { 6 | height: 150, 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/lib/components/controls/Select.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, arrayOf, bool, shape, oneOfType, number } from 'prop-types' 3 | import SelectInner from 'src/lib/components/controls/SelectInner' 4 | 5 | export class Select extends Component { 6 | static propTypes = { 7 | options: arrayOf( 8 | shape({ 9 | label: string.isRequired, 10 | value: oneOfType([number, string, bool]).isRequired, 11 | }) 12 | ).isRequired, 13 | onChange: func.isRequired, 14 | onMenuOpen: func, 15 | onMenuClose: func, 16 | onBlur: func.isRequired, 17 | value: shape({ 18 | label: string.isRequired, 19 | value: oneOfType([number, string, bool]).isRequired, 20 | }), 21 | placeholder: string, 22 | isField: bool, 23 | disabled: bool, 24 | isError: bool, 25 | } 26 | 27 | static defaultProps = { 28 | value: null, 29 | placeholder: '', 30 | isField: false, 31 | disabled: false, 32 | isError: false, 33 | onMenuOpen: () => {}, 34 | onMenuClose: () => {}, 35 | } 36 | 37 | render() { 38 | const { ...props } = this.props 39 | return 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lib/components/controls/SelectInner.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | inkbar: { 7 | '&:after': { 8 | backgroundColor: colors.primary.main, 9 | }, 10 | }, 11 | inkbarError: { 12 | '&:after': { 13 | backgroundColor: colors.error.main, 14 | }, 15 | }, 16 | }) 17 | .attach() 18 | 19 | export const cn = classes 20 | -------------------------------------------------------------------------------- /src/lib/components/controls/Switch.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ASwitch from 'material-ui/Switch' 3 | import { bool, func } from 'prop-types' 4 | 5 | import { cn } from './Switch.style' 6 | 7 | export class Switch extends Component { 8 | static propTypes = { 9 | checked: bool.isRequired, 10 | onChange: func.isRequired, 11 | disabled: bool, 12 | } 13 | 14 | static defaultProps = { 15 | disabled: false, 16 | } 17 | 18 | render() { 19 | const { checked, onChange, disabled } = this.props 20 | return ( 21 | onChange(!checked)} 25 | value="" 26 | classes={{ 27 | checked: cn.checked, 28 | disabled: cn.disabled, 29 | bar: cn.bar, 30 | }} 31 | /> 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/components/controls/Switch.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | checked: { 7 | color: colors.primary.main, 8 | }, 9 | disabled: { 10 | color: colors.grey.l5t2, 11 | }, 12 | bar: { 13 | 'background-color': `${colors.primary.main} !important`, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/lib/components/controls/TextInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, bool, oneOf } from 'prop-types' 3 | import Input from 'material-ui/Input' 4 | 5 | import { cn } from './TextInput.style' 6 | 7 | export class TextInput extends Component { 8 | static propTypes = { 9 | onChange: func.isRequired, 10 | onBlur: func.isRequired, 11 | value: string.isRequired, 12 | type: oneOf(['text', 'password']), 13 | placeholder: string, 14 | multiline: bool, 15 | disabled: bool, 16 | } 17 | 18 | static defaultProps = { 19 | type: 'text', 20 | placeholder: '', 21 | multiline: false, 22 | disabled: false, 23 | } 24 | 25 | render() { 26 | const { type, disabled, value, onChange, onBlur, placeholder, multiline } = this.props 27 | return ( 28 | onChange(event.target.value)} 36 | onBlur={onBlur} 37 | value={disabled ? '' : value} 38 | classes={{ inkbar: cn.inkbar, error: cn.inkbarError }} 39 | /> 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/components/controls/TextInput.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | inkbar: { 7 | '&:after': { 8 | backgroundColor: colors.primary.main, 9 | }, 10 | }, 11 | inkbarError: { 12 | '&:after': { 13 | backgroundColor: colors.error.main, 14 | }, 15 | }, 16 | }) 17 | .attach() 18 | 19 | export const cn = classes 20 | -------------------------------------------------------------------------------- /src/lib/components/fields/Checkbox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bool, func, string } from 'prop-types' 3 | import { FormControlLabel } from 'material-ui/Form' 4 | import { FormControl, FormHelperText } from 'material-ui/Form' 5 | import { Checkbox as ACheckbox } from 'src/lib/components/controls/Checkbox' 6 | 7 | import { cn } from './Checkbox.style' 8 | 9 | export class Checkbox extends Component { 10 | static propTypes = { 11 | checked: bool.isRequired, 12 | onChange: func.isRequired, 13 | label: string.isRequired, 14 | disabled: bool, 15 | helperText: string, 16 | dirtyAlign: bool, 17 | } 18 | 19 | static defaultProps = { 20 | disabled: false, 21 | dirtyAlign: false, 22 | helperText: '', 23 | } 24 | 25 | render() { 26 | const { checked, onChange, label, disabled, helperText, dirtyAlign } = this.props 27 | return ( 28 | 29 | } 36 | label={label} 37 | /> 38 | {helperText} 39 | 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/components/fields/Checkbox.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | label: { 7 | 'font-size': '1rem', 8 | }, 9 | dirtyAlign: { 10 | marginTop: 6, 11 | marginBottom: -6, 12 | }, 13 | disabled: { 14 | '& $label': { 15 | color: colors.black.l2t3, 16 | }, 17 | }, 18 | }) 19 | .attach() 20 | 21 | export const cn = classes 22 | -------------------------------------------------------------------------------- /src/lib/components/fields/Select.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string, bool, shape, oneOfType, number } from 'prop-types' 3 | import { FormControl, FormHelperText } from 'material-ui/Form' 4 | import { InputLabel } from 'material-ui/Input' 5 | import { Select as ASelect } from 'src/lib/components/controls/Select' 6 | 7 | import { cn } from './TextField.style' 8 | 9 | export class Select extends Component { 10 | static propTypes = { 11 | label: string.isRequired, 12 | required: bool, 13 | error: bool.isRequired, 14 | helperText: string.isRequired, 15 | disabled: bool, 16 | value: shape({ 17 | label: string.isRequired, 18 | value: oneOfType([number, string, bool]).isRequired, 19 | }), 20 | } 21 | 22 | state = { 23 | open: false, 24 | } 25 | 26 | onMenuOpen() { 27 | this.setState({ open: true }) 28 | } 29 | 30 | onMenuClose() { 31 | this.setState({ open: false }) 32 | } 33 | 34 | static defaultProps = { 35 | required: false, 36 | disabled: false, 37 | value: null, 38 | } 39 | 40 | render() { 41 | const { disabled, label, error, helperText, required, value, ...props } = this.props 42 | const { open } = this.state 43 | return ( 44 | 45 | 50 | {label} 51 | 52 | this.onMenuOpen()} 59 | onMenuClose={() => this.onMenuClose()} 60 | /> 61 | {helperText} 62 | 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/lib/components/fields/Switch.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Switch as ASwitch } from 'src/lib/components/controls/Switch' 3 | import { bool, func, string } from 'prop-types' 4 | import { FormControlLabel, FormHelperText, FormControl } from 'material-ui/Form' 5 | 6 | import { cn } from './Switch.style' 7 | 8 | export class Switch extends Component { 9 | static propTypes = { 10 | checked: bool.isRequired, 11 | onChange: func.isRequired, 12 | label: string.isRequired, 13 | helperText: string, 14 | disabled: bool, 15 | dirtyAlign: bool, 16 | } 17 | 18 | static defaultProps = { 19 | helperText: ' ', 20 | disabled: false, 21 | dirtyAlign: false, 22 | } 23 | 24 | render() { 25 | const { checked, onChange, label, helperText, disabled, dirtyAlign } = this.props 26 | return ( 27 | 28 | } 35 | label={label} 36 | /> 37 | {helperText} 38 | 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lib/components/fields/Switch.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | label: { 7 | 'font-size': '1rem', 8 | }, 9 | dirtyAlign: { 10 | marginTop: 6, 11 | marginBottom: -6, 12 | }, 13 | disabled: { 14 | '& $label': { 15 | color: colors.black.l2t3, 16 | }, 17 | }, 18 | }) 19 | .attach() 20 | 21 | export const cn = classes 22 | -------------------------------------------------------------------------------- /src/lib/components/fields/TextField.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, string, bool, oneOf } from 'prop-types' 3 | import { FormControl, FormHelperText } from 'material-ui/Form' 4 | import { InputLabel } from 'material-ui/Input' 5 | import { TextInput } from 'src/lib/components/controls/TextInput' 6 | 7 | import { cn } from './TextField.style' 8 | 9 | export class TextField extends Component { 10 | static propTypes = { 11 | label: string.isRequired, 12 | required: bool, 13 | shrink: bool, 14 | onChange: func.isRequired, 15 | onBlur: func.isRequired, 16 | value: string.isRequired, 17 | error: bool.isRequired, 18 | helperText: string.isRequired, 19 | placeholder: string, 20 | multiline: bool, 21 | disabled: bool, 22 | type: oneOf(['text', 'password']), 23 | } 24 | 25 | static defaultProps = { 26 | required: false, 27 | shrink: undefined, 28 | placeholder: '', 29 | multiline: false, 30 | disabled: false, 31 | type: 'text', 32 | } 33 | 34 | render() { 35 | const { 36 | disabled, 37 | label, 38 | value, 39 | error, 40 | onChange, 41 | onBlur, 42 | helperText, 43 | required, 44 | shrink, 45 | placeholder, 46 | multiline, 47 | type, 48 | } = this.props 49 | return ( 50 | 51 | 56 | {label} 57 | 58 | 67 | {helperText} 68 | 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/lib/components/fields/TextField.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | label: { 7 | color: colors.primary.main, 8 | }, 9 | labelError: { 10 | color: colors.error.main, 11 | }, 12 | textError: { 13 | color: colors.error.main, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/lib/helpers/createEntry.js: -------------------------------------------------------------------------------- 1 | import { NumberEntryType } from 'src/lib/types/entries/NumberEntryType' 2 | import { StringEntryType } from 'src/lib/types/entries/StringEntryType' 3 | import { BooleanEntryType } from 'src/lib/types/entries/BooleanEntryType' 4 | import { ObjectEntryType } from 'src/lib/types/entries/ObjectEntryType' 5 | import { ArrayEntryType } from 'src/lib/types/entries/ArrayEntryType' 6 | import { EnumEntryType } from 'src/lib/types/entries/EnumEntryType' 7 | import { ReferenceEntryType } from 'src/lib/types/entries/ReferenceEntryType' 8 | import { AssetEntryType } from 'src/lib/types/entries/AssetEntryType' 9 | 10 | export function createEntry(entry) { 11 | const map = { 12 | 'string-line': StringEntryType, 13 | 'string-html': StringEntryType, 14 | 'string-markdown': StringEntryType, 15 | 'string-multiline': StringEntryType, 16 | number: NumberEntryType, 17 | boolean: BooleanEntryType, 18 | object: ObjectEntryType, 19 | array: ArrayEntryType, 20 | enum: EnumEntryType, 21 | reference: ReferenceEntryType, 22 | asset: AssetEntryType, 23 | } 24 | 25 | if (!entry || !entry.value || !entry.value.type || !map[entry.value.type]) { 26 | throw new Error('Unvalid entry: unknown type of entry') 27 | } 28 | 29 | return new map[entry.value.type](entry) 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/helpers/createModel.js: -------------------------------------------------------------------------------- 1 | import { NumberModelType } from 'src/lib/types/models/NumberModelType' 2 | import { StringModelType } from 'src/lib/types/models/StringModelType' 3 | import { BooleanModelType } from 'src/lib/types/models/BooleanModelType' 4 | import { ObjectModelType } from 'src/lib/types/models/ObjectModelType' 5 | import { ArrayModelType } from 'src/lib/types/models/ArrayModelType' 6 | import { EnumModelType } from 'src/lib/types/models/EnumModelType' 7 | import { ReferenceModelType } from 'src/lib/types/models/ReferenceModelType' 8 | import { AssetModelType } from 'src/lib/types/models/AssetModelType' 9 | 10 | export function createModel(model) { 11 | const map = { 12 | 'string-line': StringModelType, 13 | 'string-html': StringModelType, 14 | 'string-markdown': StringModelType, 15 | 'string-multiline': StringModelType, 16 | number: NumberModelType, 17 | boolean: BooleanModelType, 18 | object: ObjectModelType, 19 | array: ArrayModelType, 20 | enum: EnumModelType, 21 | reference: ReferenceModelType, 22 | asset: AssetModelType, 23 | } 24 | 25 | if (!model || !model.type || !map[model.type]) { 26 | throw new Error('unknown type of model') 27 | } 28 | 29 | return new map[model.type](model) 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/helpers/createSubentry.js: -------------------------------------------------------------------------------- 1 | import { NumberSubentryType } from 'src/lib/types/entries/NumberSubentryType' 2 | import { StringSubentryType } from 'src/lib/types/entries/StringSubentryType' 3 | import { BooleanSubentryType } from 'src/lib/types/entries/BooleanSubentryType' 4 | import { ObjectSubentryType } from 'src/lib/types/entries/ObjectSubentryType' 5 | import { ArraySubentryType } from 'src/lib/types/entries/ArraySubentryType' 6 | import { EnumSubentryType } from 'src/lib/types/entries/EnumSubentryType' 7 | import { ReferenceSubentryType } from 'src/lib/types/entries/ReferenceSubentryType' 8 | import { AssetSubentryType } from 'src/lib/types/entries/AssetSubentryType' 9 | 10 | export function createSubentry(subentry) { 11 | const map = { 12 | 'string-line': StringSubentryType, 13 | 'string-html': StringSubentryType, 14 | 'string-markdown': StringSubentryType, 15 | 'string-multiline': StringSubentryType, 16 | number: NumberSubentryType, 17 | boolean: BooleanSubentryType, 18 | object: ObjectSubentryType, 19 | array: ArraySubentryType, 20 | enum: EnumSubentryType, 21 | reference: ReferenceSubentryType, 22 | asset: AssetSubentryType, 23 | } 24 | 25 | if (!subentry || !subentry.type || !map[subentry.type]) { 26 | throw new Error('Unvalid subentry: unknown type of subentry') 27 | } 28 | 29 | return new map[subentry.type](subentry) 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/helpers/createSubmodel.js: -------------------------------------------------------------------------------- 1 | import { StringSubmodelType } from 'src/lib/types/models/StringSubmodelType' 2 | import { NumberSubmodelType } from 'src/lib/types/models/NumberSubmodelType' 3 | import { BooleanSubmodelType } from 'src/lib/types/models/BooleanSubmodelType' 4 | import { ObjectSubmodelType } from 'src/lib/types/models/ObjectSubmodelType' 5 | import { ArraySubmodelType } from 'src/lib/types/models/ArraySubmodelType' 6 | import { EnumSubmodelType } from 'src/lib/types/models/EnumSubmodelType' 7 | import { ReferenceSubmodelType } from 'src/lib/types/models/ReferenceSubmodelType' 8 | import { AssetSubmodelType } from 'src/lib/types/models/AssetSubmodelType' 9 | 10 | export function createSubmodel(submodel) { 11 | const map = { 12 | 'string-line': StringSubmodelType, 13 | 'string-html': StringSubmodelType, 14 | 'string-markdown': StringSubmodelType, 15 | 'string-multiline': StringSubmodelType, 16 | number: NumberSubmodelType, 17 | boolean: BooleanSubmodelType, 18 | object: ObjectSubmodelType, 19 | array: ArraySubmodelType, 20 | enum: EnumSubmodelType, 21 | reference: ReferenceSubmodelType, 22 | asset: AssetSubmodelType, 23 | } 24 | 25 | if (!submodel || !submodel.type || !map[submodel.type]) { 26 | throw new Error('unknown type of submodel') 27 | } 28 | 29 | return new map[submodel.type](submodel) 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/helpers/getAssetControlsWithFile.js: -------------------------------------------------------------------------------- 1 | export const getAssetControlsWithFile = entry => { 2 | const res = [] 3 | inAny(res, entry.value) 4 | return res 5 | } 6 | 7 | const inSimple = () => [] 8 | 9 | const inAny = (res, value) => { 10 | const MAP = { 11 | 'string-line': inSimple, 12 | 'string-multiline': inSimple, 13 | 'string-html': inSimple, 14 | 'string-markdown': inSimple, 15 | boolean: inSimple, 16 | number: inSimple, 17 | object: isObject, 18 | array: inArray, 19 | enum: inSimple, 20 | reference: inSimple, 21 | asset: inAsset, 22 | } 23 | MAP[value.type](res, value) 24 | } 25 | 26 | const isObject = (res, value) => { 27 | Object.keys(value.value).forEach(propName => { 28 | inAny(res, value.value[propName]) 29 | }) 30 | } 31 | 32 | const inArray = (res, value) => { 33 | value.value.forEach(item => inAny(res, item)) 34 | } 35 | 36 | const inAsset = (res, value) => { 37 | if (value.value instanceof File) { 38 | res.push(value) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/lib/helpers/isEntryConsistencyConflict.js: -------------------------------------------------------------------------------- 1 | export const isEntryConsistencyConflict = (entry, model) => { 2 | return !validate(model, entry.value) 3 | } 4 | 5 | const isValid = (model, value) => model.type === value.type 6 | 7 | const isValidObject = (model, value) => { 8 | if (!isValid(model, value)) return false 9 | return Object.keys(model.properties).every(propName => { 10 | const subModel = model.properties[propName] 11 | const isValue = value.value.hasOwnProperty(propName) 12 | return isValue ? validate(subModel, value.value[propName]) : true 13 | }) 14 | } 15 | 16 | const isValidArray = (model, value) => { 17 | if (!isValid(model, value)) return false 18 | return !model.items ? true : value.value.every(item => validate(model.items, item)) 19 | } 20 | 21 | const validate = (model, value, entries) => { 22 | const MAP = { 23 | 'string-line': isValid, 24 | 'string-multiline': isValid, 25 | 'string-html': isValid, 26 | 'string-markdown': isValid, 27 | boolean: isValid, 28 | number: isValid, 29 | object: isValidObject, 30 | array: isValidArray, 31 | enum: isValid, 32 | reference: isValid, 33 | asset: isValid, 34 | } 35 | return MAP[model.type].call(this, model, value, entries) 36 | } 37 | -------------------------------------------------------------------------------- /src/lib/helpers/isEntryRefConflict.js: -------------------------------------------------------------------------------- 1 | export const isEntryRefConflict = (model, entry, entries) => { 2 | return init(model, entry.value, entries) 3 | } 4 | 5 | const initObject = (model, value, entries) => { 6 | return Object.keys(model.properties) 7 | .map(propName => { 8 | const subModel = model.properties[propName] 9 | const isValue = value.value.hasOwnProperty(propName) 10 | return isValue ? init(subModel, value.value[propName], entries) : false 11 | }) 12 | .some(item => item === true) 13 | } 14 | 15 | const initArray = (model, value, entries) => { 16 | return !model.items 17 | ? false 18 | : value.value.map(item => init(model.items, item, entries)).some(item => item === true) 19 | } 20 | 21 | const initReference = (model, value, entries) => { 22 | return !entries.filter(entry => entry.modelId === model.reference).some(entry => entry.id === value.value) 23 | } 24 | 25 | const init = (model, value, entries) => { 26 | const MAP = { 27 | 'string-line': () => false, 28 | 'string-multiline': () => false, 29 | 'string-html': () => false, 30 | 'string-markdown': () => false, 31 | boolean: () => false, 32 | number: () => false, 33 | object: initObject, 34 | array: initArray, 35 | enum: () => false, 36 | reference: initReference, 37 | asset: () => false, 38 | } 39 | return MAP[model.type].call(this, model, value, entries) 40 | } 41 | -------------------------------------------------------------------------------- /src/lib/helpers/isModelRefConflict.js: -------------------------------------------------------------------------------- 1 | export const isModelRefConflict = (model, models) => { 2 | return init(model, models) 3 | } 4 | 5 | const initObject = (model, models) => { 6 | return Object.keys(model.properties) 7 | .map(propName => { 8 | const subModel = model.properties[propName] 9 | return init(subModel, models) 10 | }) 11 | .some(item => item === true) 12 | } 13 | 14 | const initArray = (model, models) => { 15 | return !model.items ? false : init(model.items, models) 16 | } 17 | 18 | const initReference = (model, models) => { 19 | return !models.some(item => item.id === model.reference) 20 | } 21 | 22 | const init = (model, models) => { 23 | const MAP = { 24 | 'string-line': () => false, 25 | 'string-multiline': () => false, 26 | 'string-html': () => false, 27 | 'string-markdown': () => false, 28 | boolean: () => false, 29 | number: () => false, 30 | object: initObject, 31 | array: initArray, 32 | enum: () => false, 33 | reference: initReference, 34 | asset: () => false, 35 | } 36 | return MAP[model.type].call(this, model, models) 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/helpers/isNumber.js: -------------------------------------------------------------------------------- 1 | export const isNumber = value => { 2 | const regs = [ 3 | /^-[1-9]{1}[0-9]*$/, 4 | /^0{1}$/, 5 | /^[1-9]{1}[0-9]*$/, 6 | /^-[1-9]{1}[0-9]*\.[0-9]*[1-9]{1}$/, 7 | /^-0\.[0-9]*[1-9]{1}$/, 8 | /^0\.[0-9]*[1-9]{1}$/, 9 | /^[1-9]{1}[0-9]*\.[0-9]*[1-9]{1}$/, 10 | ] 11 | const isNumber = regs.some(item => item.test(value) && !isNaN(Number(value))) 12 | return isNumber 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/helpers/logout.js: -------------------------------------------------------------------------------- 1 | import { auth } from 'src/lib/services/Auth' 2 | 3 | export const logout = () => { 4 | auth.set(false) 5 | window.location.href = '/' 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/helpers/sortKeys.js: -------------------------------------------------------------------------------- 1 | export const sortKeys = obj => { 2 | return Object.keys(obj).sort((a, b) => (a > b ? 1 : -1)) 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/helpers/valueAsNumber.js: -------------------------------------------------------------------------------- 1 | export function valueAsNumber(value) { 2 | return typeof value === 'number' ? `${value}` : value ? value : '' 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/services/Auth.js: -------------------------------------------------------------------------------- 1 | import store from 'store' 2 | 3 | class Auth { 4 | set(isSet) { 5 | store.set('isAuthSet', isSet) 6 | } 7 | 8 | isSet() { 9 | return store.get('isAuthSet') || false 10 | } 11 | } 12 | 13 | export const auth = new Auth() 14 | -------------------------------------------------------------------------------- /src/lib/services/Fetcher.js: -------------------------------------------------------------------------------- 1 | import xhr from 'xhr' 2 | import { config } from 'src/config' 3 | import { logout } from 'src/lib/helpers/logout' 4 | 5 | export const fetch = ({ method, url, body, file, withCredentials = true }) => { 6 | return new Promise((resolve, reject) => { 7 | xhr( 8 | { 9 | withCredentials: withCredentials, 10 | json: !file, 11 | method: method, 12 | url: `${config.apiUrl}/${url}`, 13 | body: !file 14 | ? body 15 | : (() => { 16 | const formData = new FormData() 17 | formData.append('file', file) 18 | return formData 19 | })(), 20 | }, 21 | (err, resp, body) => { 22 | try { 23 | body = JSON.parse(body) 24 | } catch (err) {} 25 | if (err) { 26 | reject(err.message === '[object ProgressEvent]' ? new Error('Network error') : err) 27 | return 28 | } 29 | if (resp.statusCode === 401) { 30 | logout() 31 | return 32 | } 33 | if (resp.statusCode < 200 || resp.statusCode > 299) { 34 | reject({ 35 | statusCode: resp.statusCode, 36 | message: body && body.message ? body.message : body, 37 | }) 38 | return 39 | } 40 | resolve(body) 41 | } 42 | ) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/services/Jss.js: -------------------------------------------------------------------------------- 1 | import jss from 'jss' 2 | import preset from 'jss-preset-default' 3 | 4 | jss.setup(preset()) 5 | 6 | export default jss 7 | -------------------------------------------------------------------------------- /src/lib/services/Promise.js: -------------------------------------------------------------------------------- 1 | // import Promise from 'bluebird' 2 | 3 | // Promise.config({ cancellation: true }) 4 | 5 | export default Promise 6 | -------------------------------------------------------------------------------- /src/lib/services/Routes.js: -------------------------------------------------------------------------------- 1 | class Routes { 2 | home() { 3 | return `/` 4 | } 5 | 6 | projects() { 7 | return `/projects` 8 | } 9 | 10 | index(projectId) { 11 | return `/projects/${projectId}` 12 | } 13 | 14 | isIndex(projectId) { 15 | const { pathname } = window.location 16 | return pathname === `/projects/${projectId}` 17 | } 18 | 19 | models(projectId) { 20 | return `/projects/${projectId}/models` 21 | } 22 | 23 | isModels(projectId) { 24 | const { pathname } = window.location 25 | return pathname === `/projects/${projectId}/models` 26 | } 27 | 28 | entries(projectId, modelId) { 29 | return `/projects/${projectId}/models/${modelId}/entries` 30 | } 31 | 32 | isEntries(projectId) { 33 | const { pathname } = window.location 34 | return pathname.indexOf('entries') > -1 && pathname.indexOf(`/projects/${projectId}`) > -1 35 | } 36 | 37 | isExactEntry(projectId, modelId) { 38 | const { pathname } = window.location 39 | return pathname === `/projects/${projectId}/models/${modelId}/entries` 40 | } 41 | 42 | tokens(projectId) { 43 | return `/projects/${projectId}/keys` 44 | } 45 | 46 | isTokens(projectId) { 47 | const { pathname } = window.location 48 | return pathname === `/projects/${projectId}/keys` 49 | } 50 | 51 | explorer(projectId) { 52 | return `/projects/${projectId}/explorer` 53 | } 54 | 55 | isExplorer(projectId) { 56 | const { pathname } = window.location 57 | return pathname === `/projects/${projectId}/explorer` 58 | } 59 | 60 | notFound() { 61 | return `/not-found` 62 | } 63 | 64 | login() { 65 | return `/login` 66 | } 67 | 68 | registration() { 69 | return `/registration` 70 | } 71 | 72 | recoverPass() { 73 | return '/recover-pass' 74 | } 75 | 76 | contacts() { 77 | return '/contacts' 78 | } 79 | } 80 | 81 | export const routes = new Routes() 82 | -------------------------------------------------------------------------------- /src/lib/services/Validator.js: -------------------------------------------------------------------------------- 1 | import validator from 'tvalidator' 2 | 3 | const v = validator() 4 | 5 | export const validate = (...props) => { 6 | const { valid, errors } = v(...props) 7 | return { 8 | valid, 9 | errors: errors.map(error => { 10 | return { 11 | field: error.dataPath[0] || error.params.key, 12 | message: error.message, 13 | } 14 | }), 15 | originalErrors: errors, 16 | } 17 | } 18 | 19 | /* 20 | validator( 21 | // value 22 | { name: '12345678901' }, 23 | // schema 24 | { 25 | type: 'object', 26 | required: ['name'], 27 | properties: { 28 | name: { 29 | type: 'string', 30 | maxLength: 10, 31 | minLength: 1, 32 | }, 33 | }, 34 | }, 35 | // errors 36 | { 37 | properties: { 38 | name: { 39 | maxLength: 'Name too long', 40 | }, 41 | }, 42 | } 43 | ) 44 | 45 | { 46 | valid: false, 47 | errors: [ 48 | { 49 | params: { 50 | length: 11, 51 | maximum: 10, 52 | }, 53 | code: 'STRING_LENGTH_LONG', 54 | dataPath: ['name'], 55 | schemaPath: ['properties', 'name', 'maxLength'], 56 | message: 'Name too long', 57 | }, 58 | ], 59 | } 60 | */ 61 | -------------------------------------------------------------------------------- /src/lib/styles/margins.css: -------------------------------------------------------------------------------- 1 | .mt-xxs { margin-top: 3px !important; } 2 | .mt-xs { margin-top: 5px !important; } 3 | .mt-sm { margin-top: 10px !important; } 4 | .mt-md { margin-top: 15px !important; } 5 | .mt-lg { margin-top: 20px !important; } 6 | .mt-xl { margin-top: 30px !important; } 7 | .mt-xxl { margin-top: 50px !important; } 8 | 9 | .mr-xxs { margin-right: 3px !important; } 10 | .mr-xs { margin-right: 5px !important; } 11 | .mr-sm { margin-right: 10px !important; } 12 | .mr-md { margin-right: 15px !important; } 13 | .mr-lg { margin-right: 20px !important; } 14 | .mr-xl { margin-right: 30px !important; } 15 | .mr-xxl { margin-right: 50px !important; } 16 | 17 | .mb-xxs { margin-bottom: 3px !important; } 18 | .mb-xs { margin-bottom: 5px !important; } 19 | .mb-sm { margin-bottom: 10px !important; } 20 | .mb-md { margin-bottom: 15px !important; } 21 | .mb-lg { margin-bottom: 20px !important; } 22 | .mb-xl { margin-bottom: 30px !important; } 23 | .mb-xxl { margin-bottom: 50px !important; } 24 | 25 | .ml-xxs { margin-left: 3px !important; } 26 | .ml-xs { margin-left: 5px !important; } 27 | .ml-sm { margin-left: 10px !important; } 28 | .ml-md { margin-left: 15px !important; } 29 | .ml-lg { margin-left: 20px !important; } 30 | .ml-xl { margin-left: 30px !important; } 31 | .ml-xxl { margin-left: 50px !important; } 32 | 33 | .m-xxs { margin: 3px !important; } 34 | .m-xs { margin: 5px !important; } 35 | .m-sm { margin: 10px !important; } 36 | .m-md { margin: 15px !important; } 37 | .m-lg { margin: 20px !important; } 38 | .m-xl { margin: 30px !important; } 39 | .m-xxl { margin: 50px !important; } 40 | -------------------------------------------------------------------------------- /src/lib/styles/paddings.css: -------------------------------------------------------------------------------- 1 | .pt-xxs { padding-top: 3px !important; } 2 | .pt-xs { padding-top: 5px !important; } 3 | .pt-sm { padding-top: 10px !important; } 4 | .pt-md { padding-top: 15px !important; } 5 | .pt-lg { padding-top: 20px !important; } 6 | .pt-xl { padding-top: 30px !important; } 7 | .pt-xxl { padding-top: 50px !important; } 8 | 9 | .pr-xxs { padding-right: 3px !important; } 10 | .pr-xs { padding-right: 5px !important; } 11 | .pr-sm { padding-right: 10px !important; } 12 | .pr-md { padding-right: 15px !important; } 13 | .pr-lg { padding-right: 20px !important; } 14 | .pr-xl { padding-right: 30px !important; } 15 | .pr-xxl { padding-right: 50px !important; } 16 | 17 | .pb-xxs { padding-bottom: 3px !important; } 18 | .pb-xs { padding-bottom: 5px !important; } 19 | .pb-sm { padding-bottom: 10px !important; } 20 | .pb-md { padding-bottom: 15px !important; } 21 | .pb-lg { padding-bottom: 20px !important; } 22 | .pb-xl { padding-bottom: 30px !important; } 23 | .pb-xxl { padding-bottom: 50px !important; } 24 | 25 | .pl-xxs { padding-left: 3px !important; } 26 | .pl-xs { padding-left: 5px !important; } 27 | .pl-sm { padding-left: 10px !important; } 28 | .pl-md { padding-left: 15px !important; } 29 | .pl-lg { padding-left: 20px !important; } 30 | .pl-xl { padding-left: 30px !important; } 31 | .pl-xxl { padding-left: 50px !important; } 32 | 33 | .p-xxs { padding: 3px !important; } 34 | .p-xs { padding: 5px !important; } 35 | .p-sm { padding: 10px !important; } 36 | .p-md { padding: 15px !important; } 37 | .p-lg { padding: 20px !important; } 38 | .p-xl { padding: 30px !important; } 39 | .p-xxl { padding: 50px !important; } 40 | -------------------------------------------------------------------------------- /src/lib/styles/texts.css: -------------------------------------------------------------------------------- 1 | .text-center { 2 | text-align: center; } 3 | 4 | .text-right { 5 | text-align: right; } 6 | 7 | .text-left { 8 | text-align: left; } 9 | 10 | .text-italic { 11 | font-style: italic; } 12 | 13 | .text-one-line { 14 | overflow: hidden; 15 | white-space: nowrap; 16 | text-overflow: ellipsis; 17 | } 18 | 19 | .text-smaller-xs { font-size: 0.9em; } 20 | 21 | .text-no-underline { 22 | text-decoration: none; 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/types/entries/ArrayEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | import { createSubentry } from 'src/lib/helpers/createSubentry' 4 | 5 | export class ArrayEntryType extends EntryType { 6 | constructor(entry) { 7 | super(entry) 8 | entry.value.value.forEach((subentry, i) => { 9 | this.value.value[i] = createSubentry(subentry) 10 | }) 11 | deepFreeze(this) 12 | } 13 | 14 | _getBasicSchema() { 15 | const schema = super._getBasicSchema() 16 | schema.properties.value.properties.type = { enum: ['array'] } 17 | schema.properties.value.properties.value = { type: 'array' } 18 | return schema 19 | } 20 | 21 | toValue() { 22 | return this.value.value.map(item => item.toValue()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/types/entries/ArraySubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | import { createSubentry } from 'src/lib/helpers/createSubentry' 3 | 4 | export class ArraySubentryType extends SubentryType { 5 | constructor(subentry) { 6 | super(subentry) 7 | subentry.value.forEach((subentry, i) => { 8 | this.value[i] = createSubentry(subentry) 9 | }) 10 | } 11 | 12 | _getBasicSchema() { 13 | const schema = super._getBasicSchema() 14 | schema.properties.type = { enum: ['array'] } 15 | schema.properties.value = { type: 'array' } 16 | return schema 17 | } 18 | 19 | toValue() { 20 | return this.value.map(item => item.toValue()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/types/entries/AssetEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class AssetEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { enum: ['asset'] } 13 | schema.properties.value.properties.value = { type: 'string', minLength: 1 } 14 | 15 | return schema 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/types/entries/AssetSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class AssetSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { enum: ['asset'] } 7 | schema.properties.value = { type: 'string', minLength: 1 } 8 | return schema 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/types/entries/BooleanEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class BooleanEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { enum: ['boolean'] } 13 | schema.properties.value.properties.value = { type: 'boolean' } 14 | return schema 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/types/entries/BooleanSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class BooleanSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { enum: ['boolean'] } 7 | schema.properties.value = { type: 'boolean' } 8 | return schema 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/types/entries/EntryType.js: -------------------------------------------------------------------------------- 1 | import { validate } from 'src/lib/services/Validator' 2 | 3 | // abstract class 4 | export class EntryType { 5 | constructor(entry) { 6 | const { valid, errors } = this._validate(entry) 7 | if (!valid) { 8 | throw new Error(`Unvalid entry: ${entry.id}, message: ${errors[0].message}`) 9 | } 10 | Object.keys(entry).forEach(key => { 11 | this[key] = entry[key] 12 | }) 13 | } 14 | 15 | _validate(entry) { 16 | return validate(entry, this._getBasicSchema()) 17 | } 18 | 19 | _getBasicSchema() { 20 | return { 21 | type: 'object', 22 | additionalProperties: false, 23 | required: ['id', 'modelId', 'value', 'identificator'], 24 | properties: { 25 | id: { type: 'string', minLength: 1 }, 26 | modelId: { type: 'string', minLength: 1 }, 27 | identificator: { type: 'string' }, 28 | value: { 29 | type: 'object', 30 | additionalProperties: false, 31 | required: ['type', 'value'], 32 | properties: { 33 | type: {}, 34 | value: {}, 35 | }, 36 | }, 37 | }, 38 | } 39 | } 40 | 41 | toValue() { 42 | return this.value.value 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/types/entries/EnumEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class EnumEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { enum: ['enum'] } 13 | schema.properties.value.properties.value = { 14 | anyOf: [{ type: 'boolean' }, { type: 'string' }, { type: 'number' }], 15 | } 16 | 17 | return schema 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/types/entries/EnumSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class EnumSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { enum: ['enum'] } 7 | schema.properties.value = { 8 | anyOf: [{ type: 'boolean' }, { type: 'string' }, { type: 'number' }], 9 | } 10 | return schema 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/types/entries/NumberEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class NumberEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { enum: ['number'] } 13 | schema.properties.value.properties.value = { type: 'number' } 14 | 15 | return schema 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/types/entries/NumberSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class NumberSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { enum: ['number'] } 7 | schema.properties.value = { type: 'number' } 8 | return schema 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/types/entries/ObjectEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | import { createSubentry } from 'src/lib/helpers/createSubentry' 4 | 5 | export class ObjectEntryType extends EntryType { 6 | constructor(entry) { 7 | super(entry) 8 | Object.keys(entry.value.value).forEach(key => { 9 | this.value.value[key] = createSubentry(entry.value.value[key]) 10 | }) 11 | deepFreeze(this) 12 | } 13 | 14 | _getBasicSchema() { 15 | const schema = super._getBasicSchema() 16 | schema.properties.value.properties.type = { enum: ['object'] } 17 | schema.properties.value.properties.value = { type: 'object' } 18 | return schema 19 | } 20 | 21 | toValue() { 22 | return Object.keys(this.value.value).reduce((res, key) => { 23 | res[key] = this.value.value[key].toValue() 24 | return res 25 | }, {}) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/types/entries/ObjectSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | import { createSubentry } from 'src/lib/helpers/createSubentry' 3 | 4 | export class ObjectSubentryType extends SubentryType { 5 | constructor(subentry) { 6 | super(subentry) 7 | Object.keys(subentry.value).forEach(key => { 8 | this.value[key] = createSubentry(subentry.value[key]) 9 | }) 10 | } 11 | 12 | _getBasicSchema() { 13 | const schema = super._getBasicSchema() 14 | schema.properties.type = { enum: ['object'] } 15 | schema.properties.value = { type: 'object' } 16 | return schema 17 | } 18 | 19 | toValue() { 20 | return Object.keys(this.value).reduce((res, key) => { 21 | res[key] = this.value[key].toValue() 22 | return res 23 | }, {}) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/types/entries/ReferenceEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class ReferenceEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { enum: ['reference'] } 13 | schema.properties.value.properties.value = { type: 'string', minLength: 1 } 14 | 15 | return schema 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/types/entries/ReferenceSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class ReferenceSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { enum: ['reference'] } 7 | schema.properties.value = { type: 'string', minLength: 1 } 8 | return schema 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/types/entries/StringEntryType.js: -------------------------------------------------------------------------------- 1 | import { EntryType } from 'src/lib/types/entries/EntryType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class StringEntryType extends EntryType { 5 | constructor(entry) { 6 | super(entry) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | const schema = super._getBasicSchema() 12 | schema.properties.value.properties.type = { 13 | enum: ['string-line', 'string-multiline', 'string-html', 'string-markdown'], 14 | } 15 | schema.properties.value.properties.value = { type: 'string' } 16 | 17 | return schema 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/types/entries/StringSubentryType.js: -------------------------------------------------------------------------------- 1 | import { SubentryType } from 'src/lib/types/entries/SubentryType' 2 | 3 | export class StringSubentryType extends SubentryType { 4 | _getBasicSchema() { 5 | const schema = super._getBasicSchema() 6 | schema.properties.type = { 7 | enum: ['string-line', 'string-multiline', 'string-html', 'string-markdown'], 8 | } 9 | schema.properties.value = { type: 'string' } 10 | return schema 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/types/entries/SubentryType.js: -------------------------------------------------------------------------------- 1 | import { validate } from 'src/lib/services/Validator' 2 | 3 | // abstract class 4 | export class SubentryType { 5 | constructor(entry) { 6 | const { valid, errors } = this._validate(entry) 7 | if (!valid) { 8 | throw new Error(`Unvalid subentry: ${entry.id}, message: ${errors[0].message}`) 9 | } 10 | Object.keys(entry).forEach(key => { 11 | this[key] = entry[key] 12 | }) 13 | } 14 | 15 | _validate(entry) { 16 | return validate(entry, this._getBasicSchema()) 17 | } 18 | 19 | _getBasicSchema() { 20 | return { 21 | type: 'object', 22 | additionalProperties: false, 23 | required: ['type', 'value'], 24 | properties: { 25 | type: {}, 26 | value: {}, 27 | }, 28 | } 29 | } 30 | 31 | toValue() { 32 | return this.value 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/types/models/AbstractType.js: -------------------------------------------------------------------------------- 1 | import { validate } from 'src/lib/services/Validator' 2 | 3 | // abstract class 4 | export class AbstractType { 5 | constructor(model) { 6 | const { valid, errors } = this._validate(model) 7 | if (!valid) { 8 | throw new Error(`Unvalid model/submodel: ${model.type}, message: ${errors[0].message}`) 9 | } 10 | Object.keys(model).forEach(key => { 11 | this[key] = model[key] 12 | }) 13 | } 14 | 15 | _validate(model) { 16 | return validate(model, this._getBasicSchema()) 17 | } 18 | 19 | _getBasicSchema() { 20 | throw new Error('method _getBasicSchema must be implemented') 21 | } 22 | 23 | toSchema() { 24 | throw new Error('method toSchema must be implemented') 25 | } 26 | 27 | validateExactFields(model, fields) { 28 | const { errors } = this._validate(model) 29 | const filtredErrors = errors.filter(error => fields.some(field => field === error.field)) 30 | return { valid: !filtredErrors.length, errors: filtredErrors } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/types/models/ArrayModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import { createSubmodel } from 'src/lib/helpers/createSubmodel' 3 | import deepFreeze from 'deep-freeze' 4 | 5 | export class ArrayModelType extends ModelType { 6 | constructor(model) { 7 | super(model) 8 | if (model.items) { 9 | this.items = createSubmodel(model.items) 10 | } 11 | deepFreeze(this) 12 | } 13 | 14 | _getBasicSchema() { 15 | return { 16 | type: 'object', 17 | additionalProperties: false, 18 | required: ['id', 'title', 'apiId', 'type', 'uniqueItems'], 19 | properties: { 20 | id: { type: 'string', minLength: 1 }, 21 | title: { type: 'string', minLength: 1 }, 22 | apiId: { type: 'string', minLength: 1 }, 23 | type: { enum: ['array'] }, 24 | description: { type: 'string', minLength: 1 }, 25 | minItems: { type: 'integer', minimum: 0 }, 26 | maxItems: { type: 'integer', minimum: 0 }, 27 | uniqueItems: { enum: [false] }, 28 | items: { type: 'object' }, 29 | }, 30 | } 31 | } 32 | 33 | toSchema() { 34 | const model = this 35 | const schema = { type: 'array' } 36 | const props = ['minItems', 'maxItems', 'uniqueItems'] 37 | props.forEach(item => { 38 | if (model.hasOwnProperty(item)) { 39 | schema[item] = model[item] 40 | } 41 | }) 42 | if (model.items) { 43 | schema.items = model.items.toSchema() 44 | } else { 45 | /* app could work only with strict values, if model.items not set in enrty could be only empty array */ 46 | schema.maxItems = 0 47 | schema.minItems = 0 48 | } 49 | return schema 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/types/models/ArraySubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { ArrayModelType } from 'src/lib/types/models/ArrayModelType' 3 | import { createSubmodel } from 'src/lib/helpers/createSubmodel' 4 | 5 | export class ArraySubmodelType extends SubmodelType { 6 | constructor(submodel) { 7 | super(submodel) 8 | if (submodel.items) { 9 | this.items = createSubmodel(submodel.items) 10 | } 11 | } 12 | 13 | _getBasicSchema() { 14 | const schema = ArrayModelType.prototype._getBasicSchema() 15 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 16 | delete schema.properties.id 17 | delete schema.properties.apiId 18 | return schema 19 | } 20 | 21 | toSchema() { 22 | return ArrayModelType.prototype.toSchema.call(this) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/types/models/AssetModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class AssetModelType extends ModelType { 5 | constructor(model) { 6 | super(model) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | return { 12 | type: 'object', 13 | additionalProperties: false, 14 | required: ['id', 'title', 'apiId', 'type'], 15 | properties: { 16 | id: { type: 'string', minLength: 1 }, 17 | title: { type: 'string', minLength: 1 }, 18 | apiId: { type: 'string', minLength: 1 }, 19 | type: { 20 | enum: ['asset'], 21 | }, 22 | description: { type: 'string', minLength: 1 }, 23 | }, 24 | } 25 | } 26 | 27 | toSchema() { 28 | const schema = { type: 'string', minLength: 1 } 29 | return schema 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/types/models/AssetSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { AssetModelType } from 'src/lib/types/models/AssetModelType' 3 | 4 | export class AssetSubmodelType extends SubmodelType { 5 | _getBasicSchema() { 6 | const schema = AssetModelType.prototype._getBasicSchema() 7 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 8 | delete schema.properties.id 9 | delete schema.properties.apiId 10 | return schema 11 | } 12 | 13 | toSchema() { 14 | return AssetModelType.prototype.toSchema.call(this) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/types/models/BooleanModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class BooleanModelType extends ModelType { 5 | constructor(model) { 6 | super(model) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | return { 12 | type: 'object', 13 | additionalProperties: false, 14 | required: ['id', 'title', 'apiId', 'type', 'default'], 15 | properties: { 16 | id: { type: 'string', minLength: 1 }, 17 | title: { type: 'string', minLength: 1 }, 18 | apiId: { type: 'string', minLength: 1 }, 19 | type: { 20 | enum: ['boolean'], 21 | }, 22 | description: { type: 'string', minLength: 1 }, 23 | default: { type: 'boolean' }, 24 | }, 25 | } 26 | } 27 | 28 | toSchema() { 29 | const schema = { type: 'boolean' } 30 | return schema 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/types/models/BooleanSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { BooleanModelType } from 'src/lib/types/models/BooleanModelType' 3 | 4 | export class BooleanSubmodelType extends SubmodelType { 5 | _getBasicSchema() { 6 | const schema = BooleanModelType.prototype._getBasicSchema() 7 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 8 | delete schema.properties.id 9 | delete schema.properties.apiId 10 | return schema 11 | } 12 | 13 | toSchema() { 14 | return BooleanModelType.prototype.toSchema.call(this) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/types/models/EnumSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { EnumModelType } from 'src/lib/types/models/EnumModelType' 3 | 4 | export class EnumSubmodelType extends SubmodelType { 5 | _getBasicSchema() { 6 | const schema = EnumModelType.prototype._getBasicSchema() 7 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 8 | delete schema.properties.id 9 | delete schema.properties.apiId 10 | return schema 11 | } 12 | 13 | _validate(model) { 14 | const { valid, errors } = super._validate(model) 15 | const { 16 | valid: validByUniqueness, 17 | errors: uniquenessErrors, 18 | } = EnumModelType.prototype._validateUniqueness(model) 19 | const { valid: defaultFieldValid, errors: defaultFieldErrors } = EnumModelType.prototype._validateDefault( 20 | model 21 | ) 22 | return { 23 | valid: valid && validByUniqueness && defaultFieldValid, 24 | errors: errors.concat(uniquenessErrors, defaultFieldErrors), 25 | } 26 | } 27 | 28 | toSchema() { 29 | return EnumModelType.prototype.toSchema.call(this) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/types/models/ModelType.js: -------------------------------------------------------------------------------- 1 | import { AbstractType } from 'src/lib/types/models/AbstractType' 2 | 3 | // abstract class 4 | export class ModelType extends AbstractType {} 5 | -------------------------------------------------------------------------------- /src/lib/types/models/NumberModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class NumberModelType extends ModelType { 5 | constructor(model) { 6 | super(model) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | return { 12 | type: 'object', 13 | additionalProperties: false, 14 | required: ['id', 'title', 'apiId', 'type'], 15 | properties: { 16 | id: { type: 'string', minLength: 1 }, 17 | title: { type: 'string', minLength: 1 }, 18 | apiId: { type: 'string', minLength: 1 }, 19 | type: { 20 | enum: ['number'], 21 | }, 22 | description: { type: 'string', minLength: 1 }, 23 | default: { type: 'number' }, 24 | minimum: { type: 'number' }, 25 | maximum: { type: 'number' }, 26 | // TODO: exist if exist min and max 27 | exclusiveMaximum: { type: 'boolean' }, 28 | exclusiveMinimum: { type: 'boolean' }, 29 | multipleOf: { type: 'number', minimum: 0, exclusiveMinimum: true }, 30 | }, 31 | } 32 | } 33 | 34 | toSchema() { 35 | const model = this 36 | const schema = { type: 'number' } 37 | const props = ['minimum', 'maximum', 'exclusiveMaximum', 'exclusiveMinimum', 'multipleOf'] 38 | props.forEach(item => { 39 | if (model.hasOwnProperty(item)) { 40 | schema[item] = model[item] 41 | } 42 | }) 43 | return schema 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lib/types/models/NumberSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { NumberModelType } from 'src/lib/types/models/NumberModelType' 3 | 4 | export class NumberSubmodelType extends SubmodelType { 5 | _getBasicSchema() { 6 | const schema = NumberModelType.prototype._getBasicSchema() 7 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 8 | delete schema.properties.id 9 | delete schema.properties.apiId 10 | return schema 11 | } 12 | 13 | toSchema() { 14 | return NumberModelType.prototype.toSchema.call(this) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/types/models/ObjectSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { ObjectModelType } from 'src/lib/types/models/ObjectModelType' 3 | import { createSubmodel } from 'src/lib/helpers/createSubmodel' 4 | 5 | export class ObjectSubmodelType extends SubmodelType { 6 | constructor(submodel) { 7 | super(submodel) 8 | Object.keys(submodel.properties).forEach(propName => { 9 | this.properties[propName] = createSubmodel(submodel.properties[propName]) 10 | }) 11 | } 12 | 13 | _validate(model) { 14 | const { valid, errors } = super._validate(model) 15 | const { 16 | valid: requiredFieldsValid, 17 | errors: requiredFieldsErrors, 18 | } = ObjectModelType.prototype._validateRequireqFields(model) 19 | return { valid: valid && requiredFieldsValid, errors: errors.concat(requiredFieldsErrors) } 20 | } 21 | 22 | _getBasicSchema() { 23 | const schema = ObjectModelType.prototype._getBasicSchema() 24 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 25 | delete schema.properties.id 26 | delete schema.properties.apiId 27 | return schema 28 | } 29 | 30 | toSchema() { 31 | return ObjectModelType.prototype.toSchema.call(this) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/types/models/ReferenceModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class ReferenceModelType extends ModelType { 5 | constructor(model) { 6 | super(model) 7 | deepFreeze(this) 8 | } 9 | 10 | _getBasicSchema() { 11 | return { 12 | type: 'object', 13 | additionalProperties: false, 14 | required: ['id', 'title', 'apiId', 'type', 'reference'], 15 | properties: { 16 | id: { type: 'string', minLength: 1 }, 17 | title: { type: 'string', minLength: 1 }, 18 | apiId: { type: 'string', minLength: 1 }, 19 | type: { 20 | enum: ['reference'], 21 | }, 22 | description: { type: 'string', minLength: 1 }, 23 | reference: { type: 'string', minLength: 1 }, 24 | }, 25 | } 26 | } 27 | 28 | toSchema() { 29 | const schema = { type: 'string', minLength: 1 } 30 | return schema 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/types/models/ReferenceSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { ReferenceModelType } from 'src/lib/types/models/ReferenceModelType' 3 | 4 | export class ReferenceSubmodelType extends SubmodelType { 5 | _getBasicSchema() { 6 | const schema = ReferenceModelType.prototype._getBasicSchema() 7 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 8 | delete schema.properties.id 9 | delete schema.properties.apiId 10 | return schema 11 | } 12 | 13 | toSchema() { 14 | return ReferenceModelType.prototype.toSchema.call(this) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/types/models/StringModelType.js: -------------------------------------------------------------------------------- 1 | import { ModelType } from 'src/lib/types/models/ModelType' 2 | import deepFreeze from 'deep-freeze' 3 | 4 | export class StringModelType extends ModelType { 5 | constructor(model) { 6 | super(model) 7 | deepFreeze(this) 8 | } 9 | 10 | _validate(model) { 11 | const { valid, errors } = super._validate(model) 12 | const { valid: patternValid, errors: patternErrors } = this._validatePattern(model) 13 | return { valid: valid && patternValid, errors: errors.concat(patternErrors) } 14 | } 15 | 16 | _validatePattern(model) { 17 | if (model.pattern) { 18 | try { 19 | RegExp(model.pattern) 20 | } catch (error) { 21 | return { valid: false, errors: [{ field: 'pattern', message: error.message }] } 22 | } 23 | } 24 | return { valid: true, errors: [] } 25 | } 26 | 27 | _getBasicSchema() { 28 | return { 29 | type: 'object', 30 | additionalProperties: false, 31 | required: ['apiId', 'type', 'title', 'id'], 32 | properties: { 33 | id: { type: 'string', minLength: 1 }, 34 | apiId: { type: 'string', minLength: 1 }, 35 | type: { 36 | enum: ['string-line', 'string-multiline', 'string-html', 'string-markdown'], 37 | }, 38 | title: { type: 'string', minLength: 1 }, 39 | description: { type: 'string', minLength: 1 }, 40 | default: { type: 'string', minLength: 1 }, 41 | minLength: { type: 'integer', minimum: 0 }, 42 | maxLength: { type: 'integer', minimum: 0 }, 43 | pattern: { type: 'string', minLength: 1 }, 44 | }, 45 | } 46 | } 47 | 48 | toSchema() { 49 | const model = this 50 | const schema = { type: 'string' } 51 | const props = ['minLength', 'maxLength', 'pattern'] 52 | props.forEach(item => { 53 | if (model.hasOwnProperty(item)) { 54 | schema[item] = model[item] 55 | } 56 | }) 57 | return schema 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/lib/types/models/StringSubmodelType.js: -------------------------------------------------------------------------------- 1 | import { SubmodelType } from 'src/lib/types/models/SubmodelType' 2 | import { StringModelType } from 'src/lib/types/models/StringModelType' 3 | 4 | export class StringSubmodelType extends SubmodelType { 5 | _validate(model) { 6 | const { valid, errors } = super._validate(model) 7 | const { valid: patternValid, errors: patternErrors } = StringModelType.prototype._validatePattern(model) 8 | return { valid: valid && patternValid, errors: errors.concat(patternErrors) } 9 | } 10 | 11 | _getBasicSchema() { 12 | const schema = StringModelType.prototype._getBasicSchema() 13 | schema.required = schema.required.filter(field => field !== 'apiId' && field !== 'id') 14 | delete schema.properties.id 15 | delete schema.properties.apiId 16 | return schema 17 | } 18 | 19 | toSchema() { 20 | return StringModelType.prototype.toSchema.call(this) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/types/models/SubmodelType.js: -------------------------------------------------------------------------------- 1 | import { AbstractType } from 'src/lib/types/models/AbstractType' 2 | 3 | // abstract class 4 | export class SubmodelType extends AbstractType {} 5 | -------------------------------------------------------------------------------- /src/login/actions/onDone.js: -------------------------------------------------------------------------------- 1 | export const onDone = creds => ({ 2 | widget: 'login', 3 | type: 'onDone', 4 | payload: { creds }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/login/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/login/fetchers/post' 2 | 3 | export function postAction(creds) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'login', 7 | type: 'postStart', 8 | }) 9 | postFetcher(creds) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'login', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'login', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/login/components/Login.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | link: { 6 | fontSize: '0.9em', 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/login/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(creds) { 4 | return fetch({ method: 'POST', url: `login`, body: creds }) 5 | } 6 | -------------------------------------------------------------------------------- /src/login/reducer.js: -------------------------------------------------------------------------------- 1 | import { redirectSet, errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { routes } from 'src/lib/services/Routes' 3 | import { auth } from 'src/lib/services/Auth' 4 | import { postAction } from 'src/login/actions/post' 5 | import { dispatch } from 'src/store' 6 | 7 | export function postStart(state) { 8 | fetchingStart(state) 9 | state.login.loading.post = true 10 | } 11 | 12 | export function postEnd(state) { 13 | fetchingEnd(state) 14 | state.login.loading.post = false 15 | auth.set(true) 16 | redirectSet(state, routes.projects()) 17 | } 18 | 19 | export function postError(state, error) { 20 | fetchingEnd(state) 21 | state.login.loading.post = false 22 | errorSet(state, { show: true, error }) 23 | } 24 | 25 | export function onDone(state, { creds }) { 26 | setTimeout(() => dispatch(postAction(creds)), 0) 27 | } 28 | 29 | const modifiers = { 30 | postStart, 31 | postEnd, 32 | postError, 33 | onDone, 34 | } 35 | 36 | export const loginReducer = (state, action) => { 37 | const modifier = modifiers[action.type] 38 | modifier(state, action.payload) 39 | } 40 | -------------------------------------------------------------------------------- /src/mocks/getArray.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getArray = ({ max, sub, item }) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'array', 10 | title: `Array ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | uniqueItems: false, 12 | }, 13 | entry: { 14 | id: getId(), 15 | modelId: id, 16 | identificator: getId(), 17 | value: { type: 'array', value: max ? [item.entry] : [] }, 18 | }, 19 | } 20 | if (max) { 21 | mock.model.description = 'Array max description' 22 | mock.model.minItems = 1 23 | mock.model.maxItems = 3 24 | mock.model.items = item.model 25 | } 26 | if (sub) { 27 | delete mock.model.id 28 | delete mock.model.apiId 29 | mock.entry = mock.entry.value 30 | } 31 | return mock 32 | } 33 | -------------------------------------------------------------------------------- /src/mocks/getAsset.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getAsset = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'asset', 10 | title: `Asset ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | }, 12 | entry: { 13 | id: getId(), 14 | modelId: id, 15 | identificator: getId(), 16 | value: { 17 | type: 'asset', 18 | value: `http://localhost:3000/1.jpg`, 19 | }, 20 | }, 21 | } 22 | if (max) { 23 | mock.model.description = 'Asset max description' 24 | } 25 | if (sub) { 26 | delete mock.model.id 27 | delete mock.model.apiId 28 | mock.entry = mock.entry.value 29 | } 30 | return mock 31 | } 32 | -------------------------------------------------------------------------------- /src/mocks/getBoolean.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getBoolean = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'boolean', 10 | title: `Boolean ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | default: !!max, 12 | }, 13 | entry: { 14 | id: getId(), 15 | modelId: id, 16 | identificator: getId(), 17 | value: { type: 'boolean', value: false }, 18 | }, 19 | } 20 | if (max) { 21 | mock.entry.value.value = true 22 | mock.model.description = 'Boolean max description' 23 | } 24 | if (sub) { 25 | delete mock.model.id 26 | delete mock.model.apiId 27 | mock.entry = mock.entry.value 28 | } 29 | return mock 30 | } 31 | -------------------------------------------------------------------------------- /src/mocks/getEnum.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getEnum = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'enum', 10 | title: `Enum ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | enum: [ 12 | { 13 | label: 'True', 14 | value: true, 15 | }, 16 | ], 17 | }, 18 | entry: { 19 | id: getId(), 20 | modelId: id, 21 | identificator: getId(), 22 | value: { type: 'enum', value: true }, 23 | }, 24 | } 25 | if (max) { 26 | mock.model.enum.push({ label: 'first', value: 'first' }) 27 | mock.entry.value.value = 'first' 28 | mock.model.description = 'Enum max description' 29 | mock.model.default = true 30 | } 31 | if (sub) { 32 | delete mock.model.id 33 | delete mock.model.apiId 34 | mock.entry = mock.entry.value 35 | } 36 | return mock 37 | } 38 | -------------------------------------------------------------------------------- /src/mocks/getId.js: -------------------------------------------------------------------------------- 1 | export const getId = () => `${Math.round(Math.random() * 1e9)}` 2 | -------------------------------------------------------------------------------- /src/mocks/getNumber.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getNumber = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'number', 10 | title: `Number ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | }, 12 | entry: { 13 | id: getId(), 14 | modelId: id, 15 | identificator: getId(), 16 | value: { type: 'number', value: 0 }, 17 | }, 18 | } 19 | if (max) { 20 | mock.entry.value.value = 2.8 // 1.4 21 | mock.model.description = 'Number max description' 22 | mock.model.default = 1.4 23 | mock.model.minimum = 0.1 24 | mock.model.maximum = 9.8 25 | mock.model.multipleOf = 1.4 // 0.2 26 | mock.model.exclusiveMinimum = true 27 | mock.model.exclusiveMaximum = true 28 | } 29 | if (sub) { 30 | delete mock.model.id 31 | delete mock.model.apiId 32 | mock.entry = mock.entry.value 33 | } 34 | return mock 35 | } 36 | -------------------------------------------------------------------------------- /src/mocks/getObject.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getObject = ({ max, sub, props }) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'object', 10 | title: `Object ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | required: max ? Object.keys(props) : [], 12 | additionalProperties: false, 13 | properties: Object.keys(props).reduce((res, key) => { 14 | res[key] = props[key].model 15 | return res 16 | }, {}), 17 | }, 18 | entry: { 19 | id: getId(), 20 | modelId: id, 21 | identificator: getId(), 22 | value: { type: 'object', value: {} }, 23 | }, 24 | } 25 | if (max) { 26 | mock.model.description = 'Object max description' 27 | mock.entry.value.value = Object.keys(props).reduce((res, key) => { 28 | res[key] = props[key].entry 29 | return res 30 | }, {}) 31 | } 32 | if (sub) { 33 | delete mock.model.id 34 | delete mock.model.apiId 35 | mock.entry = mock.entry.value 36 | } 37 | return mock 38 | } 39 | -------------------------------------------------------------------------------- /src/mocks/getReference.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getReference = ({ max, sub, model, entry } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'reference', 10 | title: `Reference ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | reference: model.id, 12 | }, 13 | entry: { 14 | id: getId(), 15 | modelId: id, 16 | identificator: getId(), 17 | value: { type: 'reference', value: entry.id }, 18 | }, 19 | } 20 | if (max) { 21 | mock.model.description = 'Reference max description' 22 | } 23 | if (sub) { 24 | delete mock.model.id 25 | delete mock.model.apiId 26 | mock.entry = mock.entry.value 27 | } 28 | return mock 29 | } 30 | -------------------------------------------------------------------------------- /src/mocks/getStringHtml.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getStringHtml = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'string-html', 10 | title: `StringHtml ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | }, 12 | entry: { 13 | id: getId(), 14 | modelId: id, 15 | identificator: getId(), 16 | value: { type: 'string-html', value: '' }, 17 | }, 18 | } 19 | if (max) { 20 | mock.entry.value.value = '
a
' 21 | mock.model.description = 'StringHtml max description' 22 | mock.model.default = 'b' 23 | mock.model.minLength = 2 24 | mock.model.maxLength = 20 25 | // mock.model.pattern = '^([0-9]+)$' 26 | } 27 | if (sub) { 28 | delete mock.model.id 29 | delete mock.model.apiId 30 | mock.entry = mock.entry.value 31 | } 32 | return mock 33 | } 34 | -------------------------------------------------------------------------------- /src/mocks/getStringLine.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getStringLine = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'string-line', 10 | title: `StringLine ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | }, 12 | entry: { 13 | id: getId(), 14 | modelId: id, 15 | identificator: getId(), 16 | value: { type: 'string-line', value: '' }, 17 | }, 18 | } 19 | if (max) { 20 | mock.entry.value.value = 'lada' 21 | mock.model.description = 'StringLine max description' 22 | mock.model.default = 'bmw' 23 | mock.model.minLength = 2 24 | mock.model.maxLength = 20 25 | mock.model.pattern = '^([a-z]+)$' 26 | } 27 | if (sub) { 28 | delete mock.model.id 29 | delete mock.model.apiId 30 | mock.entry = mock.entry.value 31 | } 32 | return mock 33 | } 34 | -------------------------------------------------------------------------------- /src/mocks/getStringMarkdown.js: -------------------------------------------------------------------------------- 1 | import { stringMarkdownValue } from 'src/mocks/stringMarkdownValue' 2 | import { getId } from 'src/mocks/getId' 3 | 4 | export const getStringMarkdown = ({ max, sub } = {}) => { 5 | const id = getId() 6 | const mock = { 7 | model: { 8 | id: id, 9 | apiId: `api-id-${id}`, 10 | type: 'string-markdown', 11 | title: `StringMarkdown ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 12 | }, 13 | entry: { 14 | id: getId(), 15 | modelId: id, 16 | identificator: getId(), 17 | value: { type: 'string-markdown', value: '' }, 18 | }, 19 | } 20 | if (max) { 21 | mock.entry.value.value = stringMarkdownValue 22 | mock.model.description = 'StringMarkdown max description' 23 | mock.model.default = '## StringMarkdown' 24 | mock.model.minLength = 2 25 | mock.model.maxLength = 100 26 | // mock.model.pattern = '^([a-z]+)$' 27 | } 28 | if (sub) { 29 | delete mock.model.id 30 | delete mock.model.apiId 31 | mock.entry = mock.entry.value 32 | } 33 | return mock 34 | } 35 | -------------------------------------------------------------------------------- /src/mocks/getStringMultiline.js: -------------------------------------------------------------------------------- 1 | import { getId } from 'src/mocks/getId' 2 | 3 | export const getStringMultiline = ({ max, sub } = {}) => { 4 | const id = getId() 5 | const mock = { 6 | model: { 7 | id: id, 8 | apiId: `api-id-${id}`, 9 | type: 'string-multiline', 10 | title: `StringMultiline ${max ? 'max' : 'min'} ${sub ? 'subtype' : 'type'} title`, 11 | }, 12 | entry: { 13 | id: getId(), 14 | modelId: id, 15 | identificator: getId(), 16 | value: { type: 'string-multiline', value: '' }, 17 | }, 18 | } 19 | if (max) { 20 | mock.entry.value.value = 'aaabbb' 21 | mock.model.description = 'StringMultiline max description' 22 | mock.model.default = 'aaa' 23 | mock.model.minLength = 2 24 | mock.model.maxLength = 20 25 | mock.model.pattern = '^([a-z]+)$' 26 | } 27 | if (sub) { 28 | delete mock.model.id 29 | delete mock.model.apiId 30 | mock.entry = mock.entry.value 31 | } 32 | return mock 33 | } 34 | -------------------------------------------------------------------------------- /src/mocks/stringMarkdownValue.js: -------------------------------------------------------------------------------- 1 | export const stringMarkdownValue = `You` 2 | // ` 3 | // ## Medium Editor 4 | // 5 | // You know, the **Medium Editor** library is awesome. Just great. But there is a missing feature that some people would like to have. 6 | // 7 | // It's called _Markdown_. [@IonicaBizau](https://github.com/IonicaBizau) created a Medium Editor extension that adds this missing feature. 8 | // 9 | // > Walking on water and developing software from a specification are easy if both are frozen. _Edward Berard_ 10 | // 11 | // Let's test up the elements. 12 | // 13 | // > ## First Computer Bug 14 | // > 15 | // > In 1947, Grace Murray Hopper was working on the Harvard University Mark II Aiken Relay Calculator (a primitive computer). 16 | // > 17 | // > On the 9th of September, 1947, when the machine was experiencing problems, an investigation showed that there was a moth trapped between the points of Relay #70, in Panel F. 18 | // > 19 | // > The operators removed the moth and affixed it to the log. The entry reads: "First actual case of bug being found." 20 | // > 21 | // > _[Read the whole story »](http://www.jamesshuggins.com/h/tek1/first_computer_bug.htm)_ 22 | // 23 | // # Heading 1 24 | // 25 | // ## Heading 2 26 | // 27 | // ### Heading 3 28 | // 29 | // #### Heading 4 30 | // 31 | // ##### Heading 5 32 | // 33 | // ###### Heading 6 34 | // 35 | // * I am 36 | // * a list 37 | // * with 3 items 38 | // 39 | // 1. I am 40 | // 2. a numbered list 41 | // 3. with 3 items 42 | // 43 | // **bold** 44 | // 45 | // _italic_ 46 | // 47 | // underline 48 | // 49 | // awesome 50 | // 51 | // elements1 52 | // 53 | // elements1 54 | // 55 | // [Read the whole story »](http://www.jamesshuggins.com/h/tek1/first_computer_bug.htm) 56 | // 57 | // > quote 58 | // ` 59 | -------------------------------------------------------------------------------- /src/models/actions/delete.js: -------------------------------------------------------------------------------- 1 | import { deleteFetcher } from 'src/models/fetchers/delete' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | 4 | export function deleteAction(modelId, entries) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'models', 9 | type: 'deleteStart', 10 | }) 11 | deleteFetcher(projectId, modelId) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'models', 15 | type: 'deleteEnd', 16 | }) 17 | dispatch(getDataAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'models', 22 | type: 'deleteError', 23 | payload: error, 24 | }) 25 | dispatch(getDataAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/actions/onAddItem.js: -------------------------------------------------------------------------------- 1 | export const onAddItem = (id, dist) => ({ 2 | widget: 'models', 3 | type: 'onAddItem', 4 | payload: { id, dist }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onAddModel.js: -------------------------------------------------------------------------------- 1 | export const onAddModel = () => ({ 2 | widget: 'models', 3 | type: 'onAddModel', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onAddProp.js: -------------------------------------------------------------------------------- 1 | export const onAddProp = (id, dist) => ({ 2 | widget: 'models', 3 | type: 'onAddProp', 4 | payload: { id, dist }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDeleteItem.js: -------------------------------------------------------------------------------- 1 | export const onDeleteItem = (id, dist) => ({ 2 | widget: 'models', 3 | type: 'onDeleteItem', 4 | payload: { id, dist }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDeleteModel.js: -------------------------------------------------------------------------------- 1 | export const onDeleteModel = id => ({ 2 | widget: 'models', 3 | type: 'onDeleteModel', 4 | payload: { id }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDeleteProp.js: -------------------------------------------------------------------------------- 1 | export const onDeleteProp = (id, dist) => ({ 2 | widget: 'models', 3 | type: 'onDeleteProp', 4 | payload: { id, dist }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogAddDoneItem.js: -------------------------------------------------------------------------------- 1 | export const onDialogAddDoneItem = item => ({ 2 | widget: 'models', 3 | type: 'onDialogAddDoneItem', 4 | payload: { item }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogAddDoneModel.js: -------------------------------------------------------------------------------- 1 | export const onDialogAddDoneModel = model => ({ 2 | widget: 'models', 3 | type: 'onDialogAddDoneModel', 4 | payload: { model }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogAddDoneProp.js: -------------------------------------------------------------------------------- 1 | export const onDialogAddDoneProp = property => ({ 2 | widget: 'models', 3 | type: 'onDialogAddDoneProp', 4 | payload: { property }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogClose.js: -------------------------------------------------------------------------------- 1 | export const onDialogClose = dialogType => ({ 2 | widget: 'models', 3 | type: 'onDialogClose', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogConfirmConfirmItem.js: -------------------------------------------------------------------------------- 1 | export const onDialogConfirmConfirmItem = () => ({ 2 | widget: 'models', 3 | type: 'onDialogConfirmConfirmItem', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogConfirmConfirmModel.js: -------------------------------------------------------------------------------- 1 | export const onDialogConfirmConfirmModel = () => ({ 2 | widget: 'models', 3 | type: 'onDialogConfirmConfirmModel', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogConfirmConfirmProp.js: -------------------------------------------------------------------------------- 1 | export const onDialogConfirmConfirmProp = () => ({ 2 | widget: 'models', 3 | type: 'onDialogConfirmConfirmProp', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogEditDoneItem.js: -------------------------------------------------------------------------------- 1 | export const onDialogEditDoneItem = item => ({ 2 | widget: 'models', 3 | type: 'onDialogEditDoneItem', 4 | payload: { item }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogEditDoneModel.js: -------------------------------------------------------------------------------- 1 | export const onDialogEditDoneModel = model => ({ 2 | widget: 'models', 3 | type: 'onDialogEditDoneModel', 4 | payload: { model }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogEditDoneProp.js: -------------------------------------------------------------------------------- 1 | export const onDialogEditDoneProp = property => ({ 2 | widget: 'models', 3 | type: 'onDialogEditDoneProp', 4 | payload: { property }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onDialogExited.js: -------------------------------------------------------------------------------- 1 | export const onDialogExited = dialogType => ({ 2 | widget: 'models', 3 | type: 'onDialogExited', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onEditItem.js: -------------------------------------------------------------------------------- 1 | export const onEditItem = (id, dist, item) => ({ 2 | widget: 'models', 3 | type: 'onEditItem', 4 | payload: { id, dist, item }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onEditModel.js: -------------------------------------------------------------------------------- 1 | export const onEditModel = model => ({ 2 | widget: 'models', 3 | type: 'onEditModel', 4 | payload: { model }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/onEditProp.js: -------------------------------------------------------------------------------- 1 | export const onEditProp = (id, dist, property) => ({ 2 | widget: 'models', 3 | type: 'onEditProp', 4 | payload: { id, dist, property }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/models/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/models/fetchers/post' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | 4 | export function postAction(model) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'models', 9 | type: 'postStart', 10 | }) 11 | postFetcher(projectId, model) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'models', 15 | type: 'postEnd', 16 | }) 17 | dispatch(getDataAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'models', 22 | type: 'postError', 23 | payload: error, 24 | }) 25 | dispatch(getDataAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/actions/put.js: -------------------------------------------------------------------------------- 1 | import { putFetcher } from 'src/models/fetchers/put' 2 | import { getDataAction } from 'src/global/actions/getData' 3 | 4 | export function putAction(model) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'models', 9 | type: 'putStart', 10 | }) 11 | putFetcher(projectId, model) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'models', 15 | type: 'putEnd', 16 | }) 17 | dispatch(getDataAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'models', 22 | type: 'putError', 23 | payload: error, 24 | }) 25 | dispatch(getDataAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/components/basic/TypeBlock.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Grid, Typography } from 'material-ui' 3 | import { func, string, node } from 'prop-types' 4 | import { CircledIcon } from 'src/lib/components/CircledIcon' 5 | import { colors } from 'src/colors' 6 | 7 | import { cn } from './TypeBlock.style' 8 | 9 | export class TypeBlock extends Component { 10 | static propTypes = { 11 | onClick: func.isRequired, 12 | icon: node.isRequired, 13 | title: string.isRequired, 14 | description: string.isRequired, 15 | } 16 | 17 | render() { 18 | const { onClick, icon, title, description } = this.props 19 | return ( 20 | onClick()}> 21 | 22 | 23 | {icon} 24 | 25 | 26 | 27 | {title} 28 | {description} 29 | 30 | 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/models/components/basic/TypeBlock.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | typeBlock: { 7 | cursor: 'pointer', 8 | '& .avatar > div': { 9 | margin: 'auto', 10 | }, 11 | '&:hover .avatar > div': { 12 | 'background-color': `${colors.primary.d2} !important`, 13 | }, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/models/components/cards/ObjectCard.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | add: { 7 | borderLeft: `4px solid ${colors.primary.main}`, 8 | }, 9 | }) 10 | .attach() 11 | 12 | export const cn = classes 13 | -------------------------------------------------------------------------------- /src/models/components/dialogs/ConfirmDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, oneOf, bool } from 'prop-types' 3 | import { ConfirmDialog as AConfirmDialog } from 'src/lib/components/ConfirmDialog' 4 | 5 | export class ConfirmDialog extends Component { 6 | static propTypes = { 7 | mod: oneOf(['model', 'prop', 'item']), 8 | open: bool.isRequired, 9 | onConfirmItem: func.isRequired, 10 | onConfirmModel: func.isRequired, 11 | onConfirmProp: func.isRequired, 12 | onClose: func.isRequired, 13 | onExited: func.isRequired, 14 | } 15 | 16 | static defaultProps = { 17 | mod: null, 18 | } 19 | 20 | render() { 21 | const { mod, open, onConfirmItem, onConfirmModel, onConfirmProp, onClose, onExited } = this.props 22 | if (!mod) return null 23 | const MAP_TITLE = { 24 | model: 'Delete model?', 25 | prop: 'Delete property?', 26 | item: 'Delete item?', 27 | } 28 | const MAP_TEXT = { 29 | model: 'Are you sure you want to delete model? All entries related to this model will be lost.', 30 | prop: 31 | 'Are you sure you want to delete property? All data of entries related to this property will be lost.', 32 | item: 'Are you sure you want to delete item? All data of entries related to this item will be lost.', 33 | } 34 | const MAP_ACTIONS = { 35 | model: onConfirmModel, 36 | prop: onConfirmProp, 37 | item: onConfirmItem, 38 | } 39 | return ( 40 | 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/models/components/dialogs/Steps.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { number, array, arrayOf } from 'prop-types' 3 | import { Step } from 'src/models/components/dialogs/Step' 4 | 5 | export class Steps extends Component { 6 | static propTypes = { 7 | step: number.isRequired, 8 | steps: arrayOf(array).isRequired, 9 | } 10 | 11 | render() { 12 | const { step, steps, ...props } = this.props 13 | return 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/models/components/dialogs/enum-type/EnumControl.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | container: { 7 | paddingBottom: 42, 8 | }, 9 | deleteButton: { 10 | marginTop: 16, 11 | }, 12 | textError: { 13 | color: colors.error.main, 14 | }, 15 | }) 16 | .attach() 17 | 18 | export const cn = classes 19 | -------------------------------------------------------------------------------- /src/models/components/dialogs/reference-type/ReferenceItem.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | contentContainer: { 6 | paddingBottom: 210, 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/models/components/dialogs/reference-type/ReferenceModel.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | contentContainer: { 6 | paddingBottom: 210, 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/models/components/dialogs/reference-type/ReferenceProp.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | contentContainer: { 6 | paddingBottom: 210 - 76, 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/models/fetchers/delete.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const deleteFetcher = (projectId, id) => { 4 | return fetch({ method: 'DELETE', url: `projects/${projectId}/models/${id}` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/models/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const postFetcher = (projectId, model) => { 4 | return fetch({ method: 'POST', url: `projects/${projectId}/models`, body: model }) 5 | } 6 | -------------------------------------------------------------------------------- /src/models/fetchers/put.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const putFetcher = (projectId, model) => { 4 | return fetch({ method: 'PUT', url: `projects/${projectId}/models/${model.id}`, body: model }) 5 | } 6 | -------------------------------------------------------------------------------- /src/models/helpers/dialogConstructor.js: -------------------------------------------------------------------------------- 1 | export function dialogConstructor(props, initialModel) { 2 | const clearState = this.getClearState() 3 | if (initialModel) { 4 | Object.keys(initialModel).forEach(key => { 5 | clearState.model[key] = initialModel[key] 6 | }) 7 | } 8 | this.state = clearState 9 | } 10 | -------------------------------------------------------------------------------- /src/models/helpers/dialogTypeOnBlur.js: -------------------------------------------------------------------------------- 1 | import { validatePropertyName } from 'src/models/helpers/validatePropertyName' 2 | 3 | export function dialogTypeOnBlur({ Type, field }) { 4 | const { state } = this 5 | const { model } = state 6 | 7 | const { errors } = 8 | field !== 'propertyName' 9 | ? Type.prototype.validateExactFields(model, [field]) 10 | : validatePropertyName(model) 11 | state.errors[field] = errors.length ? errors[0].message : '' 12 | 13 | this.setState(state) 14 | } 15 | -------------------------------------------------------------------------------- /src/models/helpers/dialogTypeOnDone.js: -------------------------------------------------------------------------------- 1 | import clone from 'clone' 2 | import { validatePropertyName } from 'src/models/helpers/validatePropertyName' 3 | 4 | export function dialogTypeOnDone({ Type, fields, onDone }) { 5 | const { state } = this 6 | const { model } = state 7 | 8 | const { valid, errors } = Type.prototype.validateExactFields( 9 | model, 10 | fields.filter(item => item !== 'propertyName') 11 | ) 12 | fields.forEach(field => { 13 | const fieldErrors = errors.filter(error => error.field === field) 14 | state.errors[field] = fieldErrors.length ? fieldErrors[0].message : '' 15 | }) 16 | 17 | let propertyNameValid = true 18 | if (fields.some(item => item === 'propertyName')) { 19 | const { valid, errors } = validatePropertyName(model) 20 | propertyNameValid = valid 21 | state.errors.propertyName = errors.length ? errors[0].message : '' 22 | } 23 | 24 | if (valid && propertyNameValid) { 25 | onDone(clone(model)) 26 | } else { 27 | this.setState(state) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/models/helpers/onBooleanChange.js: -------------------------------------------------------------------------------- 1 | export function onBooleanChange(field, value) { 2 | const { state } = this 3 | const { model } = state 4 | 5 | model[field] = value 6 | 7 | this.setState(state) 8 | } 9 | -------------------------------------------------------------------------------- /src/models/helpers/onNumberChange.js: -------------------------------------------------------------------------------- 1 | import { isNumber } from 'src/lib/helpers/isNumber' 2 | 3 | export function onNumberChange(field, value) { 4 | const { state } = this 5 | const { model, errors } = state 6 | 7 | model[field] = isNumber(value) ? +value : value 8 | errors[field] = '' 9 | 10 | if (model[field] === '') { 11 | delete model[field] 12 | } 13 | 14 | this.setState(state) 15 | } 16 | -------------------------------------------------------------------------------- /src/models/helpers/onStringChange.js: -------------------------------------------------------------------------------- 1 | export function onStringChange(field, value) { 2 | const { state } = this 3 | const { model, errors } = state 4 | 5 | model[field] = value 6 | errors[field] = '' 7 | 8 | if (!model[field]) { 9 | delete model[field] 10 | } 11 | 12 | this.setState(state) 13 | } 14 | -------------------------------------------------------------------------------- /src/models/helpers/validatePropertyName.js: -------------------------------------------------------------------------------- 1 | import { validate } from 'src/lib/services/Validator' 2 | 3 | export function validatePropertyName(model) { 4 | return validate(model.propertyName, { type: 'string', minLength: 1 }) 5 | } 6 | -------------------------------------------------------------------------------- /src/not-found/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func } from 'prop-types' 3 | import { connect } from 'react-redux' 4 | import { Typography } from 'src/lib/components/Typography' 5 | import { Button } from 'src/lib/components/Button' 6 | import { redirectSetAction } from 'src/global/actions/redirectSet' 7 | import { routes } from 'src/lib/services/Routes' 8 | import { RedirectContainer } from 'src/global/components/RedirectContainer' 9 | import { ErrorContainer } from 'src/global/components/ErrorContainer' 10 | import { PageContainer } from 'src/lib/components/PageContainer' 11 | 12 | class ANotFound extends Component { 13 | static propTypes = { 14 | redirectSet: func.isRequired, 15 | } 16 | 17 | render() { 18 | const { redirectSet } = this.props 19 | return ( 20 | 21 |
22 | 23 | 24 | 25 | Look like this page doesn't exist 26 |
27 | 30 |
31 |
32 |
33 |
34 | ) 35 | } 36 | } 37 | 38 | export const NotFound = connect( 39 | state => ({}), 40 | dispatch => ({ 41 | redirectSet: (...props) => dispatch(redirectSetAction(...props)), 42 | }) 43 | )(ANotFound) 44 | -------------------------------------------------------------------------------- /src/projects/actions/deleteAction.js: -------------------------------------------------------------------------------- 1 | import { deleteFetcher } from 'src/projects/fetchers/deleteFetcher' 2 | import { getAction } from 'src/projects/actions/getAction' 3 | 4 | export function deleteAction(project) { 5 | return function(dispatch, getState) { 6 | dispatch({ 7 | widget: 'projects', 8 | type: 'deleteStart', 9 | }) 10 | deleteFetcher(project.id) 11 | .then(() => { 12 | dispatch({ 13 | widget: 'projects', 14 | type: 'deleteEnd', 15 | }) 16 | dispatch(getAction()) 17 | }) 18 | .catch(error => { 19 | dispatch({ 20 | widget: 'projects', 21 | type: 'deleteError', 22 | payload: error, 23 | }) 24 | dispatch(getAction()) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/projects/actions/getAction.js: -------------------------------------------------------------------------------- 1 | import { getProjectsFetcher } from 'src/global/fetchers/getProjects' 2 | 3 | export function getAction() { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'projects', 7 | type: 'getStart', 8 | }) 9 | getProjectsFetcher() 10 | .then(projects => { 11 | dispatch({ 12 | widget: 'projects', 13 | type: 'getEnd', 14 | payload: { projects }, 15 | }) 16 | }) 17 | .catch(error => { 18 | dispatch({ 19 | widget: 'projects', 20 | type: 'getError', 21 | payload: error, 22 | }) 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/projects/actions/onAddDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onAddDialogDone = project => ({ 2 | widget: 'projects', 3 | type: 'onAddDialogDone', 4 | payload: { project }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onAddProject.js: -------------------------------------------------------------------------------- 1 | export const onAddProject = () => ({ 2 | widget: 'projects', 3 | type: 'onAddProject', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onConfirmDialogConfirm.js: -------------------------------------------------------------------------------- 1 | export const onConfirmDialogConfirm = () => ({ 2 | widget: 'projects', 3 | type: 'onConfirmDialogConfirm', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onDeleteProject.js: -------------------------------------------------------------------------------- 1 | export const onDeleteProject = id => ({ 2 | widget: 'projects', 3 | type: 'onDeleteProject', 4 | payload: { id }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onDialogClose.js: -------------------------------------------------------------------------------- 1 | export const onDialogClose = dialogType => ({ 2 | widget: 'projects', 3 | type: 'onDialogClose', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onDialogExited.js: -------------------------------------------------------------------------------- 1 | export const onDialogExited = dialogType => ({ 2 | widget: 'projects', 3 | type: 'onDialogExited', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onEditDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onEditDialogDone = project => ({ 2 | widget: 'projects', 3 | type: 'onEditDialogDone', 4 | payload: { project }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onEditProject.js: -------------------------------------------------------------------------------- 1 | export const onEditProject = project => ({ 2 | widget: 'projects', 3 | type: 'onEditProject', 4 | payload: { project }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/onLogoutClick.js: -------------------------------------------------------------------------------- 1 | export const onLogoutClick = () => ({ 2 | widget: 'projects', 3 | type: 'onLogoutClick', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/projects/actions/postAction.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/projects/fetchers/postFetcher' 2 | import { getAction } from 'src/projects/actions/getAction' 3 | 4 | export function postAction(project) { 5 | return function(dispatch, getState) { 6 | dispatch({ 7 | widget: 'projects', 8 | type: 'postStart', 9 | }) 10 | postFetcher(project) 11 | .then(() => { 12 | dispatch({ 13 | widget: 'projects', 14 | type: 'postEnd', 15 | }) 16 | dispatch(getAction()) 17 | }) 18 | .catch(error => { 19 | dispatch({ 20 | widget: 'projects', 21 | type: 'postError', 22 | payload: error, 23 | }) 24 | dispatch(getAction()) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/projects/actions/putAction.js: -------------------------------------------------------------------------------- 1 | import { putFetcher } from 'src/projects/fetchers/putFetcher' 2 | import { getAction } from 'src/projects/actions/getAction' 3 | 4 | export function putAction(oldProject, newProject) { 5 | return function(dispatch, getState) { 6 | dispatch({ 7 | widget: 'projects', 8 | type: 'putStart', 9 | }) 10 | putFetcher(newProject) 11 | .then(() => { 12 | dispatch({ 13 | widget: 'projects', 14 | type: 'putEnd', 15 | }) 16 | dispatch(getAction()) 17 | }) 18 | .catch(error => { 19 | dispatch({ 20 | widget: 'projects', 21 | type: 'putError', 22 | payload: error, 23 | }) 24 | dispatch(getAction()) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/projects/components/ConfirmDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bool } from 'prop-types' 3 | import { ConfirmDialog as AConfirmDialog } from 'src/lib/components/ConfirmDialog' 4 | 5 | export class ConfirmDialog extends Component { 6 | static propTypes = { 7 | mount: bool.isRequired, 8 | } 9 | 10 | render() { 11 | const { mount, ...props } = this.props 12 | return !mount ? null : 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/projects/components/ProjectDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, bool, string, object } from 'prop-types' 3 | import { Dialog } from 'src/lib/components/Dialog' 4 | import { ProjectDialogInner } from 'src/projects/components/ProjectDialogInner' 5 | 6 | export class ProjectDialog extends Component { 7 | static propTypes = { 8 | project: object, 9 | title: string.isRequired, 10 | mount: bool.isRequired, 11 | open: bool.isRequired, 12 | onDone: func.isRequired, 13 | onClose: func.isRequired, 14 | onExited: func.isRequired, 15 | } 16 | 17 | static defaultProps = { 18 | project: null, 19 | } 20 | 21 | render() { 22 | const { mount, project, open, onClose, onDone, title, onExited } = this.props 23 | if (!mount) return null 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/projects/components/Projects.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | import { colors } from 'src/colors' 3 | 4 | const { classes } = jss 5 | .createStyleSheet({ 6 | card: { 7 | boxShadow: 8 | '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)', 9 | textAlign: 'center', 10 | borderRadius: '2px', 11 | }, 12 | newProjectCard: { 13 | boxShadow: 14 | '0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12)', 15 | textAlign: 'center', 16 | borderRadius: '2px', 17 | cursor: 'pointer', 18 | height: '100%', 19 | background: colors.white.t3, 20 | '&:hover': { 21 | background: colors.white.t5, 22 | }, 23 | }, 24 | newProjectCardInner: { 25 | padding: '60px 25px', 26 | }, 27 | newProjectText: { 28 | color: 'rgba(0, 0, 0, 0.54)', 29 | fontSize: '30px', 30 | }, 31 | avatar: { 32 | margin: 'auto', 33 | width: '100%', 34 | height: '100%', 35 | }, 36 | cardBody: { 37 | cursor: 'pointer', 38 | borderRadius: '2px 2px 0 0', 39 | padding: '25px 25px', 40 | background: colors.white.main, 41 | transition: '0.2s', 42 | '&:hover': { 43 | background: colors.white.t4, 44 | }, 45 | }, 46 | cardFooter: { 47 | cursor: 'default', 48 | padding: '20px 25px', 49 | borderRadius: '0 0 2px 2px', 50 | background: colors.white.main, 51 | }, 52 | }) 53 | .attach() 54 | 55 | export const cn = classes 56 | -------------------------------------------------------------------------------- /src/projects/fetchers/deleteFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const deleteFetcher = projectId => { 4 | return fetch({ method: 'DELETE', url: `projects/${projectId}` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/projects/fetchers/postFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const postFetcher = project => { 4 | return fetch({ method: 'POST', url: `projects`, body: project }) 5 | } 6 | -------------------------------------------------------------------------------- /src/projects/fetchers/putFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const putFetcher = project => { 4 | return fetch({ method: 'PUT', url: `projects/${project.id}`, body: project }) 5 | } 6 | -------------------------------------------------------------------------------- /src/recover-pass/actions/onDone.js: -------------------------------------------------------------------------------- 1 | export const onDone = creds => ({ 2 | widget: 'recoverPass', 3 | type: 'onDone', 4 | payload: { creds }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/recover-pass/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/recover-pass/fetchers/post' 2 | 3 | export function postAction(creds) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'recoverPass', 7 | type: 'postStart', 8 | }) 9 | postFetcher(creds) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'recoverPass', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'recoverPass', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/recover-pass/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(creds) { 4 | return fetch({ method: 'POST', url: `recover`, body: creds }) 5 | } 6 | -------------------------------------------------------------------------------- /src/recover-pass/reducer.js: -------------------------------------------------------------------------------- 1 | import { errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { postAction } from 'src/recover-pass/actions/post' 3 | import { dispatch } from 'src/store' 4 | 5 | export function postStart(state) { 6 | fetchingStart(state) 7 | state.recoverPass.loading.post = true 8 | } 9 | 10 | export function postEnd(state) { 11 | fetchingEnd(state) 12 | state.recoverPass.loading.post = false 13 | state.recoverPass.showMessage = true 14 | } 15 | 16 | export function postError(state, error) { 17 | fetchingEnd(state) 18 | state.recoverPass.loading.post = false 19 | errorSet(state, { show: true, error }) 20 | } 21 | 22 | export function onDone(state, { creds }) { 23 | setTimeout(() => dispatch(postAction(creds)), 0) 24 | } 25 | 26 | const modifiers = { 27 | postStart, 28 | postEnd, 29 | postError, 30 | onDone, 31 | } 32 | 33 | export const recoverPassReducer = (state, action) => { 34 | const modifier = modifiers[action.type] 35 | modifier(state, action.payload) 36 | } 37 | -------------------------------------------------------------------------------- /src/reducer.js: -------------------------------------------------------------------------------- 1 | import clone from 'clone' 2 | import { state } from './state' 3 | import { globalReducer } from 'src/global/reducer' 4 | import { modelsReducer } from 'src/models/reducer' 5 | import { entriesReducer } from 'src/entries/reducer' 6 | import { projectsReducer } from 'src/projects/reducer' 7 | import { loginReducer } from 'src/login/reducer' 8 | import { registrationReducer } from 'src/registration/reducer' 9 | import { emailConfirmReducer } from 'src/email-confirm/reducer' 10 | import { recoverPassReducer } from 'src/recover-pass/reducer' 11 | import { changePassReducer } from 'src/change-pass/reducer' 12 | import { tokensReducer } from 'src/tokens/reducer' 13 | import { contactsReducer } from 'src/contacts/reducer' 14 | 15 | const initialState = state() 16 | 17 | const MAP = { 18 | global: globalReducer, 19 | models: modelsReducer, 20 | entries: entriesReducer, 21 | projects: projectsReducer, 22 | login: loginReducer, 23 | registration: registrationReducer, 24 | emailConfirm: emailConfirmReducer, 25 | recoverPass: recoverPassReducer, 26 | changePass: changePassReducer, 27 | tokens: tokensReducer, 28 | contacts: contactsReducer, 29 | } 30 | 31 | export const reducer = (oldState = initialState, action) => { 32 | const state = clone(oldState) 33 | const widget = action.widget 34 | if (widget) { 35 | MAP[widget](state, action) 36 | } 37 | return state 38 | } 39 | -------------------------------------------------------------------------------- /src/registration/actions/onDone.js: -------------------------------------------------------------------------------- 1 | export const onDone = creds => ({ 2 | widget: 'registration', 3 | type: 'onDone', 4 | payload: { creds }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/registration/actions/post.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/registration/fetchers/post' 2 | 3 | export function postAction(creds) { 4 | return function(dispatch, getState) { 5 | dispatch({ 6 | widget: 'registration', 7 | type: 'postStart', 8 | }) 9 | postFetcher(creds) 10 | .then(() => { 11 | dispatch({ 12 | widget: 'registration', 13 | type: 'postEnd', 14 | }) 15 | }) 16 | .catch(error => { 17 | dispatch({ 18 | widget: 'registration', 19 | type: 'postError', 20 | payload: error, 21 | }) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/registration/components/Registration.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | link: { 6 | fontSize: '0.9em', 7 | }, 8 | }) 9 | .attach() 10 | 11 | export const cn = classes 12 | -------------------------------------------------------------------------------- /src/registration/fetchers/post.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export function postFetcher(creds) { 4 | return fetch({ method: 'POST', url: `register`, body: creds }) 5 | } 6 | -------------------------------------------------------------------------------- /src/registration/reducer.js: -------------------------------------------------------------------------------- 1 | import { errorSet, fetchingStart, fetchingEnd } from 'src/global/reducer' 2 | import { postAction } from 'src/registration/actions/post' 3 | import { dispatch } from 'src/store' 4 | 5 | export function postStart(state) { 6 | fetchingStart(state) 7 | state.registration.loading.post = true 8 | } 9 | 10 | export function postEnd(state) { 11 | fetchingEnd(state) 12 | state.registration.loading.post = false 13 | state.registration.showMessage = true 14 | } 15 | 16 | export function postError(state, error) { 17 | fetchingEnd(state) 18 | state.registration.loading.post = false 19 | errorSet(state, { show: true, error }) 20 | } 21 | 22 | export function onDone(state, { creds }) { 23 | setTimeout(() => dispatch(postAction(creds)), 0) 24 | } 25 | 26 | const modifiers = { 27 | postStart, 28 | postEnd, 29 | postError, 30 | onDone, 31 | } 32 | 33 | export const registrationReducer = (state, action) => { 34 | const modifier = modifiers[action.type] 35 | modifier(state, action.payload) 36 | } 37 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import { reducer } from './reducer' 4 | 5 | export const store = createStore(reducer, applyMiddleware(thunk)) 6 | 7 | export const dispatch = store.dispatch 8 | -------------------------------------------------------------------------------- /src/tokens/actions/deleteAction.js: -------------------------------------------------------------------------------- 1 | import { deleteFetcher } from 'src/tokens/fetchers/deleteFetcher' 2 | import { getAction } from 'src/tokens/actions/getAction' 3 | 4 | export function deleteAction(token) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'tokens', 9 | type: 'deleteStart', 10 | }) 11 | deleteFetcher(projectId, token.id) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'tokens', 15 | type: 'deleteEnd', 16 | }) 17 | dispatch(getAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'tokens', 22 | type: 'deleteError', 23 | payload: error, 24 | }) 25 | dispatch(getAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/tokens/actions/getAction.js: -------------------------------------------------------------------------------- 1 | import { getFetcher } from 'src/tokens/fetchers/getFetcher' 2 | 3 | export function getAction() { 4 | return function(dispatch, getState) { 5 | const { projectId } = getState().global 6 | dispatch({ 7 | widget: 'tokens', 8 | type: 'getStart', 9 | }) 10 | getFetcher(projectId) 11 | .then(tokens => { 12 | dispatch({ 13 | widget: 'tokens', 14 | type: 'getEnd', 15 | payload: { tokens }, 16 | }) 17 | }) 18 | .catch(error => { 19 | dispatch({ 20 | widget: 'tokens', 21 | type: 'getError', 22 | payload: error, 23 | }) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/tokens/actions/onAdd.js: -------------------------------------------------------------------------------- 1 | export const onAdd = () => ({ 2 | widget: 'tokens', 3 | type: 'onAdd', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onAddDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onAddDialogDone = token => ({ 2 | widget: 'tokens', 3 | type: 'onAddDialogDone', 4 | payload: { token }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onConfirmDialogConfirm.js: -------------------------------------------------------------------------------- 1 | export const onConfirmDialogConfirm = () => ({ 2 | widget: 'tokens', 3 | type: 'onConfirmDialogConfirm', 4 | payload: {}, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onDelete.js: -------------------------------------------------------------------------------- 1 | export const onDelete = id => ({ 2 | widget: 'tokens', 3 | type: 'onDelete', 4 | payload: { id }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onDialogClose.js: -------------------------------------------------------------------------------- 1 | export const onDialogClose = dialogType => ({ 2 | widget: 'tokens', 3 | type: 'onDialogClose', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onDialogExited.js: -------------------------------------------------------------------------------- 1 | export const onDialogExited = dialogType => ({ 2 | widget: 'tokens', 3 | type: 'onDialogExited', 4 | payload: { dialogType }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onEdit.js: -------------------------------------------------------------------------------- 1 | export const onEdit = token => ({ 2 | widget: 'tokens', 3 | type: 'onEdit', 4 | payload: { token }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/onEditDialogDone.js: -------------------------------------------------------------------------------- 1 | export const onEditDialogDone = token => ({ 2 | widget: 'tokens', 3 | type: 'onEditDialogDone', 4 | payload: { token }, 5 | }) 6 | -------------------------------------------------------------------------------- /src/tokens/actions/postAction.js: -------------------------------------------------------------------------------- 1 | import { postFetcher } from 'src/tokens/fetchers/postFetcher' 2 | import { getAction } from 'src/tokens/actions/getAction' 3 | 4 | export function postAction(token) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'tokens', 9 | type: 'postStart', 10 | }) 11 | postFetcher(projectId, token) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'tokens', 15 | type: 'postEnd', 16 | }) 17 | dispatch(getAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'tokens', 22 | type: 'postError', 23 | payload: error, 24 | }) 25 | dispatch(getAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/tokens/actions/putAction.js: -------------------------------------------------------------------------------- 1 | import { putFetcher } from 'src/tokens/fetchers/putFetcher' 2 | import { getAction } from 'src/tokens/actions/getAction' 3 | 4 | export function putAction(token) { 5 | return function(dispatch, getState) { 6 | const { projectId } = getState().global 7 | dispatch({ 8 | widget: 'tokens', 9 | type: 'putStart', 10 | }) 11 | putFetcher(projectId, token) 12 | .then(() => { 13 | dispatch({ 14 | widget: 'tokens', 15 | type: 'putEnd', 16 | }) 17 | dispatch(getAction()) 18 | }) 19 | .catch(error => { 20 | dispatch({ 21 | widget: 'tokens', 22 | type: 'putError', 23 | payload: error, 24 | }) 25 | dispatch(getAction()) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/tokens/components/ConfirmDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { bool } from 'prop-types' 3 | import { ConfirmDialog as AConfirmDialog } from 'src/lib/components/ConfirmDialog' 4 | 5 | export class ConfirmDialog extends Component { 6 | static propTypes = { 7 | mount: bool.isRequired, 8 | } 9 | 10 | render() { 11 | const { mount, ...props } = this.props 12 | return !mount ? null : 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/tokens/components/TokenDialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { func, bool, string, object } from 'prop-types' 3 | import { Dialog } from 'src/lib/components/Dialog' 4 | import { TokenDialogInner } from 'src/tokens/components/TokenDialogInner' 5 | 6 | export class TokenDialog extends Component { 7 | static propTypes = { 8 | token: object, 9 | title: string.isRequired, 10 | mount: bool.isRequired, 11 | open: bool.isRequired, 12 | onDone: func.isRequired, 13 | onClose: func.isRequired, 14 | onExited: func.isRequired, 15 | } 16 | 17 | static defaultProps = { 18 | token: null, 19 | } 20 | 21 | render() { 22 | const { mount, token, open, onClose, onDone, title, onExited } = this.props 23 | if (!mount) return null 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/tokens/components/basic/Card.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { string, func, arrayOf } from 'prop-types' 3 | import { Button } from 'src/lib/components/Button' 4 | import { Typography } from 'src/lib/components/Typography' 5 | 6 | import { cn } from './Card.style' 7 | 8 | export class Card extends Component { 9 | static propTypes = { 10 | title: string.isRequired, 11 | onEdit: func.isRequired, 12 | onDelete: func.isRequired, 13 | labels: arrayOf(string).isRequired, 14 | } 15 | 16 | render() { 17 | const { title, onEdit, onDelete, labels } = this.props 18 | return ( 19 |
20 |
21 | {title} 22 |
23 |
24 | {labels.map((label, key) => ( 25 | 26 | {label} 27 | 28 | ))} 29 |
30 | 31 | 34 | 35 | 38 |
39 |
40 |
41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/tokens/components/basic/Card.style.js: -------------------------------------------------------------------------------- 1 | import jss from 'src/lib/services/Jss' 2 | 3 | const { classes } = jss 4 | .createStyleSheet({ 5 | container: { 6 | background: '#fff', 7 | boxShadow: 8 | '0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12)', 9 | borderRadius: '2px', 10 | marginBottom: '15px', 11 | '&:hover $cardButtons': { 12 | visibility: 'visible !important', 13 | }, 14 | }, 15 | cardButtons: { 16 | display: 'inline-block', 17 | visibility: 'hidden', 18 | }, 19 | }) 20 | .attach() 21 | 22 | export const cn = classes 23 | -------------------------------------------------------------------------------- /src/tokens/fetchers/deleteFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const deleteFetcher = (projectId, tokenId) => { 4 | return fetch({ method: 'DELETE', url: `projects/${projectId}/tokens/${tokenId}` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/tokens/fetchers/getFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const getFetcher = projectId => { 4 | return fetch({ method: 'GET', url: `projects/${projectId}/tokens` }) 5 | } 6 | -------------------------------------------------------------------------------- /src/tokens/fetchers/postFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const postFetcher = (projectId, token) => { 4 | return fetch({ method: 'POST', url: `projects/${projectId}/tokens`, body: token }) 5 | } 6 | -------------------------------------------------------------------------------- /src/tokens/fetchers/putFetcher.js: -------------------------------------------------------------------------------- 1 | import { fetch } from 'src/lib/services/Fetcher' 2 | 3 | export const putFetcher = (projectId, token) => { 4 | return fetch({ method: 'PUT', url: `projects/${projectId}/tokens/${token.id}`, body: token }) 5 | } 6 | --------------------------------------------------------------------------------