Remove settings manager package

This commit is contained in:
Pierre Burgy 2017-07-13 08:57:21 +02:00
parent adc2726a09
commit 319504ac9b
147 changed files with 0 additions and 6267 deletions

View File

@ -1,7 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = false
indent_style = space
indent_size = 2

View File

@ -1,103 +0,0 @@
# From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto
#
# The above will handle all files NOT found below
#
#
## These files are text and should be normalized (Convert crlf => lf)
#
# source code
*.php text
*.css text
*.sass text
*.scss text
*.less text
*.styl text
*.js text eol=lf
*.coffee text
*.json text
*.htm text
*.html text
*.xml text
*.svg text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
*.sh text
*.bat text
# templates
*.ejs text
*.hbt text
*.jade text
*.haml text
*.hbs text
*.dot text
*.tmpl text
*.phtml text
# git config
.gitattributes text
.gitignore text
.gitconfig text
# code analysis config
.jshintrc text
.jscsrc text
.jshintignore text
.csslintrc text
# misc config
*.yaml text
*.yml text
.editorconfig text
# build config
*.npmignore text
*.bowerrc text
# Heroku
Procfile text
.slugignore text
# Documentation
*.md text
LICENSE text
AUTHORS text
#
## These files are binary and should be left untouched
#
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary

View File

@ -1,122 +0,0 @@
# Contributing to react-boilerplate
Love react-boilerplate and want to help? Thanks so much, there's something to do for everybody!
Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features.
## Using the issue tracker
The [issue tracker](https://github.com/mxstbr/react-boilerplate/issues) is
the preferred channel for [bug reports](#bugs), [features requests](#features)
and [submitting pull requests](#pull-requests).
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the latest `master` or development branch in the repository.
3. **Isolate the problem** &mdash; ideally create a [reduced test case](https://css-tricks.com/reduced-test-cases/) and a live example.
A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What browser(s) and OS
experience the problem? What would you expect to be the outcome? All these details will help people to fix any potential bugs.
Example:
> Short and descriptive example bug report title
>
> A summary of the issue and the browser/OS environment in which it occurs. If
> suitable, include the steps required to reproduce the bug.
>
> 1. This is the first step
> 2. This is the second step
> 3. Further steps, etc.
>
> `<url>` - a link to the reduced test case
>
> Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as
> causing the bug, and potential solutions (and your opinions on their
> merits).
<a name="features"></a>
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible.
<a name="pull-requests"></a>
## Pull requests
Good pull requests - patches, improvements, new features - are a fantastic
help. They should remain focused in scope and avoid containing unrelated
commits.
**Please ask first** before embarking on any significant pull request (e.g.
implementing features, refactoring code, porting to a different language),
otherwise you risk spending a lot of time working on something that the
project's developers might not want to merge into the project.
Please adhere to the coding conventions used throughout a project (indentation,
accurate comments, etc.) and any other requirements (such as test coverage).
Since the `master` branch is what people actually use in production, we have a
`dev` branch that unstable changes get merged into first. Only when we
consider that stable we merge it into the `master` branch and release the
changes for real.
Adhering to the following process is the best way to get your work
included in the project:
1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/react-boilerplate.git
# Navigate to the newly cloned directory
cd react-boilerplate
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/mxstbr/react-boilerplate.git
```
2. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout dev
git pull upstream dev
```
3. Create a new topic branch (off the `dev` branch) to contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
4. Commit your changes in logical chunks. Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/about-git-rebase/) feature to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream dev branch into your topic branch:
```bash
git pull [--rebase] upstream dev
```
6. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project
owners to license your work under the terms of the [MIT License](https://github.com/mxstbr/react-boilerplate/blob/master/LICENSE.md).

View File

@ -1,30 +0,0 @@
# React Boilerplate
Before opening a new issue, please take a moment to review our [**community guidelines**](https://github.com/mxstbr/react-boilerplate/blob/master/.github/CONTRIBUTING.md) to make the contribution process easy and effective for everyone involved.
Please direct redux-saga related questions to stack overflow:
http://stackoverflow.com/questions/tagged/redux-saga
For questions related to the boilerplate itself, you can also find answers on our gitter chat:
https://gitter.im/mxstbr/react-boilerplate
**Before opening a new issue, you may find an answer in already closed issues**:
https://github.com/mxstbr/react-boilerplate/issues?q=is%3Aissue+is%3Aclosed
## Issue Type
- [ ] Bug (https://github.com/mxstbr/react-boilerplate/blob/master/.github/CONTRIBUTING.md#bug-reports)
- [ ] Feature (https://github.com/mxstbr/react-boilerplate/blob/master/.github/CONTRIBUTING.md#feature-requests)
## Description
(Add images if possible)
## Steps to reproduce
(Add link to a demo on https://jsfiddle.net or similar if possible)
# Versions
- Node/NPM:
- Browser:

View File

@ -1,22 +0,0 @@
## React Boilerplate
Thank you for contributing! Please take a moment to review our [**contributing guidelines**](https://github.com/mxstbr/react-boilerplate/blob/master/.github/CONTRIBUTING.md)
to make the process easy and effective for everyone involved.
**Please open an issue** before embarking on any significant pull request, especially those that
add a new library or change existing tests, otherwise you risk spending a lot of time working
on something that might not end up being merged into the project.
Before opening a pull request, please ensure:
- [ ] You have followed our [**contributing guidelines**](https://github.com/mxstbr/react-boilerplate/blob/master/.github/CONTRIBUTING.md)
- [ ] Pull request has tests (we are going for 100% coverage!)
- [ ] Code is well-commented, linted and follows project conventions
- [ ] Documentation is updated (if necessary)
- [ ] Internal code generators and templates are updated (if necessary)
- [ ] Description explains the issue/use-case resolved and auto-closes related issues
Be kind to code reviewers, please try to keep pull requests as small and focused as possible :)
**IMPORTANT**: By submitting a patch, you agree to allow the project
owners to license your work under the terms of the [MIT License](https://github.com/mxstbr/react-boilerplate/blob/master/LICENSE.md).

View File

@ -1,10 +0,0 @@
# Don't check auto-generated stuff into git
coverage
build
node_modules
stats.json
# Cruft
.DS_Store
npm-debug.log
.idea

View File

@ -1,37 +0,0 @@
# Strapi Settings Manager
## Description
Strapi plugin built to let you easily configure your Strapi applications.
## Installation
Create a new Strapi application: `strapi new myApp`.
Go in your new Strapi application: `cd myApp`.
Go in the plugins repository: `cd plugins`.
Clone this repository: `git clone https://github.com/strapi/strapi-settings-manager settings-manager`.
Go in the Settings Manager repository: `cd settings-manager`.
Setup the plugin: `npm run setup`. This will install the required node modules and build the React plugin.
Go at the root of your application: `cd ../..`.
Start your application: `strapi start`.
Summary:
```
strapi new myApp \
&& cd myApp \
&& cd plugins \
&& git clone https://github.com/strapi/strapi-settings-manager settings-manager \
&& cd settings-manager \
&& npm run setup \
&& cd ../.. \
&& strapi start
```
Note: in the near future, the way to install the plugins will be easier (eg. `strapi install settings-manager`).

View File

@ -1,29 +0,0 @@
{
"routes": [
{
"method": "GET",
"path": "/settings/general",
"handler": "SettingsManager.getGeneralSettings",
"config": {
"policies": []
}
},
{
"action": "updateSettings",
"method": "PUT",
"path": "/settings",
"handler": "SettingsManager.updateSettings",
"config": {
"policies": []
}
}, {
"action": "updateSettings",
"method": "GET",
"path": "/:file",
"handler": "SettingsManager.file",
"config": {
"policies": []
}
}
]
}

View File

@ -1,47 +0,0 @@
'use strict';
const path = require('path');
const _ = require('lodash');
const fs = require('fs');
/**
* A set of functions called "actions" for `SettingsManager`
*/
module.exports = {
getGeneralSettings: async (ctx) => {
// Pick values from `strapi.config`
const settings = _.pick(strapi.config, [
'name',
'version',
'description'
]);
ctx.body = settings;
},
updateSettings: async (ctx) => {
const data = ctx.request.body;
try {
const settingsUpdated = await strapi.plugins['settings-manager'].services.settingsservice.configurationsManager(strapi, data);
ctx.body = settingsUpdated.values;
} catch (err) {
console.log('err', err);
ctx.status = err && err.status || 400;
return ctx.body = {
message: err.msg || 'An error occurred during settings update'
};
}
},
file: async (ctx) => {
try {
const file = fs.readFileSync(path.resolve(__dirname, '..', 'public', 'build', ctx.params.file));
ctx.body = file;
} catch (err) {
ctx.body = ctx.notFound();
}
}
};

View File

@ -1,42 +0,0 @@
{
"name": "strapi-plugin-settings-manager",
"version": "3.0.0-alpha.3",
"lockfileVersion": 1,
"dependencies": {
"fs-extra": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
"integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA="
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug="
},
"klaw": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
"integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk="
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
},
"validator": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
"integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g="
}
}
}

View File

@ -1,35 +0,0 @@
{
"name": "strapi-plugin-settings-manager",
"version": "3.0.0-alpha.3",
"description": "Strapi Plugin - Settings Manager",
"engines": {
"node": ">= 7.0.0",
"npm": ">= 3.0.0"
},
"author": {
"email": "hi@strapi.io",
"name": "Strapi team",
"url": "http://strapi.io"
},
"maintainers": [
{
"name": "Strapi team",
"email": "hi@strapi.io",
"url": "http://strapi.io"
}
],
"license": "MIT",
"scripts": {
"setup": "npm i && cd public && npm i && npm run build"
},
"pre-commit": [
"lint:admin"
],
"dependencies": {
"fs-extra": "^1.0.0",
"lodash": "^4.17.4",
"semver": "^5.3.0",
"validator": "^6.3.0"
},
"devDependencies": {}
}

View File

@ -1,37 +0,0 @@
{
"parser": "babel-eslint",
"extends": "eslint:recommended",
"plugins": [
"react",
"babel",
"redux-saga"
],
"env": {
"commonjs": true,
"es6": true,
"node": true,
"mocha": true
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"globals": {
"strapi": true,
"window": true,
"fetch": true
},
"rules": {
"generator-star-spacing": 0,
"babel/generator-star-spacing": 0,
"indent": ["error", 2, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"no-console": 0,
"quotes": ["error", "single"],
"semi": ["error", "always"],
"react/jsx-uses-vars": [2]
}
}

View File

@ -1,10 +0,0 @@
# Don't check auto-generated stuff into git
coverage
build
node_modules
stats.json
# Cruft
.DS_Store
npm-debug.log
.idea

View File

@ -1,63 +0,0 @@
/**
* app.js
*
* This is the entry file for the application,
* only setup and plugin code.
*/
import { browserHistory } from 'react-router';
import configureStore from './store';
// Create redux store with history
// this uses the singleton browserHistory provided by react-router
// Optionally, this could be changed to leverage a created history
// e.g. `const browserHistory = useRouterHistory(createBrowserHistory)();`
const initialState = {};
const store = configureStore(initialState, browserHistory);
// Set up the router, wrapping all Routes in the App component
import App from 'containers/App';
import createRoutes from './routes';
import { translationMessages } from './i18n';
// Plugin identifier based on the package.json `name` value
const pluginId = require('../package.json').name.replace(/^strapi-/i, '');
// Register the plugin
if (window.Strapi) {
window.Strapi.registerPlugin({
name: 'Settings Manager',
id: pluginId,
leftMenuLink: {
label: 'Settings Manager',
to: '/settings-manager',
},
mainComponent: App,
routes: createRoutes(store),
translationMessages,
});
}
// Hot reloadable translation json files
if (module.hot) {
// modules.hot.accept does not accept dynamic dependencies,
// have to be constants at compile-time
module.hot.accept('./i18n', () => {
if (window.Strapi) {
System.import('./i18n')
.then((result) => {
const translationMessagesUpdated = result.translationMessages;
window.Strapi.refresh(pluginId).translationMessages(translationMessagesUpdated);
});
}
});
}
// API
const apiUrl = window.Strapi && `${window.Strapi.apiUrl}/${pluginId}`;
// Export store
export {
store,
apiUrl,
};

View File

@ -1,33 +0,0 @@
/**
*
* Container
*
*/
import React from 'react';
import LeftMenu from 'components/LeftMenu';
import styles from './styles.scss';
class Container extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={styles.container}>
<div className={`row row-eq-height ${styles.containerContent}`}>
<div className={`col-lg-3 p-l-0 p-r-0 ${styles.containerLeftContent}`}>
<LeftMenu></LeftMenu>
</div>
<div className={`col-lg-9 ${styles.containerRightContent}`}>
{this.props.children}
</div>
</div>
</div>
);
}
}
Container.propTypes = {
children: React.PropTypes.array,
};
export default Container;

View File

@ -1,21 +0,0 @@
.container { /* stylelint-disable */
}
.containerContent {
position: relative;
border: 1px solid #D7D8D9;
border-radius: 0.4rem;
overflow: hidden;
display: flex;
}
.containerLeftContent {
padding-left: 0;
padding-right: 0;
}
.containerRightContent {
background-color: #FFF;
padding: 2.4rem 3rem 50rem;
}

View File

@ -1,11 +0,0 @@
// import Container from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<Container />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,48 +0,0 @@
/**
*
* LeftMenu
*
*/
import React from 'react';
import LeftMenuLink from 'components/LeftMenuLink';
import styles from './styles.scss';
class LeftMenu extends React.Component { // eslint-disable-line react/prefer-stateless-function
links = [{
value: 'general',
to: '',
}, {
value: 'languages',
to: 'languages',
}, {
value: 'databases',
to: 'databases',
}, {
value: 'security',
to: 'security',
}, {
value: 'server',
to: 'server',
}, {
value: 'advanced',
to: 'advanced',
}];
render() {
const linksElements = this.links.map((link, i) => (<LeftMenuLink key={i} link={link}></LeftMenuLink>));
return (
<div className={styles.leftMenu}>
<nav className={styles.leftMenuNav}>
<ul className={styles.leftMenuList}>
{linksElements}
</ul>
</nav>
</div>
);
}
}
export default LeftMenu;

View File

@ -1,11 +0,0 @@
.leftMenu { /* stylelint-disable */
background-color: #F7F8F9;
height: 100%;
width: 100%;
padding-top: 1.7rem;
}
.leftMenuList {
padding-left: 1rem;
padding-right: 1rem;
}

View File

@ -1,11 +0,0 @@
// import LeftMenu from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<LeftMenu />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,36 +0,0 @@
/**
*
* LeftMenuLink
*
*/
import React from 'react';
import { Link } from 'react-router';
import appMessages from 'containers/App/messages.json';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
class LeftMenuLink extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const messageKey = `${this.props.link.value}SectionTitle`;
return (
<li className={styles.leftMenuLink}>
<Link
className={styles.leftMenuLinkDestination}
activeClassName={styles.leftMenuLinkDestinationActive}
to={`/plugins/settings-manager/${this.props.link.to}`}
>
<FormattedMessage {...appMessages[messageKey]} />
<i className={`ion ion-arrow-right-c ${styles.leftMenuLinkIcon}`}></i>
</Link>
</li>
);
}
}
export default LeftMenuLink;
LeftMenuLink.propTypes = {
link: React.PropTypes.object,
};

View File

@ -1,44 +0,0 @@
/* Import */
@import '../../styles/variables/variables';
.leftMenuLink { /* stylelint-disable */
margin-bottom: .4rem;
}
.leftMenuLinkDestination {
display: block;
position: relative;
color: #3B3F49;
padding-left: 2rem;
padding-right: 2rem;
line-height: 3.6rem;
font-size: 1.4rem;
border-radius: 2rem;
&:hover {
color: white;
text-decoration: none;
background-color: $strapi-blue;
}
}
.leftMenuLinkDestinationActive {
color: white;
text-decoration: none;
background-color: #1C5DE7;
}
.leftMenuLinkDestinationActive .leftMenuLinkIcon {
display: block;
}
.leftMenuLinkIcon {
display: none;
float: right;
padding-top: 1.1rem;
z-index: 10;
}
.leftMenuLinkDestination:hover .leftMenuLinkIcon {
display: block;
}

View File

@ -1,11 +0,0 @@
// import LeftMenuLink from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<LeftMenuLink />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,30 +0,0 @@
/**
*
* PluginHeader
*
*/
import React from 'react';
import PluginHeaderTitle from 'components/PluginHeaderTitle';
import PluginHeaderActions from 'components/PluginHeaderActions';
import styles from './styles.scss';
class PluginHeader extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={`${styles.pluginHeader} row`}>
<div className="row">
<div className="col-lg-8">
<PluginHeaderTitle></PluginHeaderTitle>
</div>
<div className="col-lg-4">
<PluginHeaderActions {...this.props}></PluginHeaderActions>
</div>
</div>
</div>
);
}
}
export default PluginHeader;

View File

@ -1,3 +0,0 @@
.pluginHeader { /* stylelint-disable */
margin-bottom: 3.4rem;
}

View File

@ -1,11 +0,0 @@
// import PluginHeader from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<PluginHeader />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,46 +0,0 @@
/**
*
* PluginHeaderActions
*
*/
import React from 'react';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
class PluginHeaderActions extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={`${styles.pluginHeaderActions} pull-lg-right`}>
<button
type="button"
className={`${styles.pluginHeaderActionsButton} btn btn-secondary`}
onClick={this.props.onCancel}
>
<FormattedMessage {...messages.cancelLabel} />
</button>
<button
type="submit"
className={`${styles.pluginHeaderActionsButton} btn btn-primary`}
disabled={this.props.loading}
onClick={this.props.onFormSubmit}
>
<FormattedMessage {...messages.saveLabel} />
</button>
</div>
);
}
}
PluginHeaderActions.propTypes = {
loading: React.PropTypes.bool,
onCancel: React.PropTypes.func,
onFormSubmit: React.PropTypes.func,
};
export default PluginHeaderActions;

View File

@ -1,10 +0,0 @@
{
"cancelLabel": {
"id": "settings-manager.components.PluginHeaderActions.cancelLabel",
"defaultMessage": "Cancel"
},
"saveLabel": {
"id": "settings-manager.components.PluginHeaderActions.saveLabel",
"defaultMessage": "Save"
}
}

View File

@ -1,8 +0,0 @@
.pluginHeaderActions { /* stylelint-disable */
margin-top: 0.6rem;
}
.pluginHeaderActionsButton {
margin-left: 1rem;
border-radius: 4rem;
}

View File

@ -1,11 +0,0 @@
// import PluginHeaderActions from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<PluginHeaderActions />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,30 +0,0 @@
/**
*
* PluginHeaderTitle
*
*/
import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
class PluginHeaderTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={styles.pluginHeaderTitle}>
<h1 className={styles.pluginHeaderTitleName}>
<FormattedMessage {...messages.title} />
</h1>
<p className={styles.pluginHeaderTitleDescription}>
<FormattedMessage {...messages.description} />
</p>
</div>
);
}
}
export default PluginHeaderTitle;

View File

@ -1,10 +0,0 @@
{
"title": {
"id": "settings-manager.components.PluginHeaderTitle.title",
"defaultMessage": "Settings Manager"
},
"description": {
"id": "settings-manager.components.PluginHeaderTitle.description",
"defaultMessage": "Easily configure your settings"
}
}

View File

@ -1,18 +0,0 @@
/* Import */
@import '../../styles/variables/variables';
.pluginHeaderTitle { /* stylelint-disable */
color: #282A31;
}
.pluginHeaderTitleName {
font-size: 2.4rem;
margin-top: 0.7rem;
margin-bottom: 0.2rem;
}
.pluginHeaderTitleDescription {
font-size: 1.3rem;
color: $gray-light;
margin: 0;
}

View File

@ -1,11 +0,0 @@
// import PluginHeaderTitle from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<PluginHeaderTitle />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,27 +0,0 @@
/**
*
* RightContentSectionTitle
*
*/
import React from 'react';
import styles from './styles.scss';
class RightContentSectionTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div>
<h3 className={styles.rightContentSectionTitle}>{this.props.title}</h3>
<p className={styles.rightContentSectionSubTitle}>{this.props.description}</p>
</div>
);
}
}
RightContentSectionTitle.propTypes = {
title: React.PropTypes.string,
description: React.PropTypes.string,
};
export default RightContentSectionTitle;

View File

@ -1,14 +0,0 @@
/* Import */
@import '../../styles/variables/variables';
.rightContentSectionTitle { /* stylelint-disable */
text-transform: uppercase;
font-size: 1.4rem;
margin-top: 2.6rem;
}
.rightContentSectionSubTitle {
color: $gray-light;
font-size: 1.2rem;
margin-bottom: 1.2rem;
}

View File

@ -1,11 +0,0 @@
// import RightContentSectionTitle from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<RightContentSectionTitle />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,28 +0,0 @@
/**
*
* RightContentTitle
*
*/
import React from 'react';
import styles from './styles.scss';
class RightContentTitle extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div className={styles.rightContentTitle}>
<h2 className={styles.rightContentTitleName}>{this.props.title}</h2>
<p className={styles.rightContentTitleDescription}>{this.props.description}</p>
<hr className={styles.rightContentTitleSeparator}></hr>
</div>
);
}
}
RightContentTitle.propTypes = {
title: React.PropTypes.string,
description: React.PropTypes.string,
};
export default RightContentTitle;

View File

@ -1,23 +0,0 @@
/* Import */
@import '../../styles/variables/variables';
.rightContentTitle { /* stylelint-disable */
}
.rightContentTitleName {
font-size: 1.8rem;
margin-bottom: .3rem;
}
.rightContentTitleDescription {
font-size: 1.3rem;
color: $gray-light;
margin-bottom: 1.5rem;
}
.rightContentTitleSeparator {
margin-left: -3rem;
margin-right: -3rem;
margin-bottom: 1.5rem;
}

View File

@ -1,11 +0,0 @@
// import RightContentTitle from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<RightContentTitle />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,55 +0,0 @@
/*
*
* AdvancedPage
*
*/
import React from 'react';
import Helmet from 'react-helmet';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
export class AdvancedPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.advancedPage}>
<Helmet
title="Settings Manager - Advanced"
meta={[
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader />
<Container>
<RightContentTitle
title={formatMessage(appMessages.advancedSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</div>
</div>
);
}
}
AdvancedPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(AdvancedPage);

View File

@ -1,6 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.AdvancedPage.rightSectionDescription",
"defaultMessage": "Configure your advanced settings"
}
}

View File

@ -1,3 +0,0 @@
.advancedPage { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import AdvancedPage from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<AdvancedPage />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,32 +0,0 @@
/**
*
* This component is the skeleton around the actual pages, and should only
* contain code that should be seen on all pages. (e.g. navigation bar)
*
*/
import React from 'react';
import { Provider } from 'react-redux';
import { store } from '../../app';
import appMessages from 'containers/App/messages.json';
import { define } from '../../i18n';
define(appMessages);
import '../../styles/main.scss';
export default class App extends React.Component { // eslint-disable-line react/prefer-stateless-function
static propTypes = {
children: React.PropTypes.node,
};
render() {
return (
<Provider store={store}>
<div>
{React.Children.toArray(this.props.children)}
</div>
</Provider>
);
}
}

View File

@ -1,30 +0,0 @@
{
"generalSectionTitle": {
"id": "settings-manager.components.App.generalSectionTitle",
"defaultMessage": "General"
},
"languagesSectionTitle": {
"id": "settings-manager.components.App.languagesSectionTitle",
"defaultMessage": "Languages"
},
"databasesSectionTitle": {
"id": "settings-manager.components.App.databasesSectionTitle",
"defaultMessage": "Databases"
},
"securitySectionTitle": {
"id": "settings-manager.components.App.securitySectionTitle",
"defaultMessage": "Security"
},
"serverSectionTitle": {
"id": "settings-manager.components.App.serverSectionTitle",
"defaultMessage": "Server"
},
"advancedSectionTitle": {
"id": "settings-manager.components.App.advancedSectionTitle",
"defaultMessage": "Advanced"
},
"comingSoon": {
"id": "settings-manager.components.App.comingSoon",
"defaultMessage": "Coming soon"
}
}

View File

@ -1,54 +0,0 @@
/*
* DatabasesPage
*/
import React from 'react';
import Helmet from 'react-helmet';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
export class DatabasesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.databasesPage}>
<Helmet
title="Settings Manager - Databases"
meta={[
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader />
<Container>
<RightContentTitle
title={formatMessage(appMessages.databasesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</div>
</div>
);
}
}
DatabasesPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(DatabasesPage);

View File

@ -1,6 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.DatabasesPage.rightSectionDescription",
"defaultMessage": "Configure your databases settings"
}
}

View File

@ -1,3 +0,0 @@
.databasesPage { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import DatabasesPage from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<DatabasesPage />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,142 +0,0 @@
/*
* Actions
*/
import {
LOAD_GENERAL_SETTINGS,
LOAD_GENERAL_SETTINGS_SUCCESS,
LOAD_GENERAL_SETTINGS_ERROR,
CHANGE_NAME,
CHANGE_DESCRIPTION,
CHANGE_VERSION,
UPDATE_GENERAL_SETTINGS,
UPDATE_GENERAL_SETTINGS_SUCCESS,
UPDATE_GENERAL_SETTINGS_ERROR,
CANCEL_GENERAL_SETTINGS,
} from './constants';
/**
* Load the generalSettings, this action starts the request saga
*
* @return {object} An action object with a type of LOAD_GENERAL_SETTINGS
*/
export function loadGeneralSettings() {
return {
type: LOAD_GENERAL_SETTINGS,
};
}
/**
* Dispatched when the generalSettings are loaded by the request saga
*
* @param {object} generalSettings The generalSettings data
*
* @return {object} An action object with a type of LOAD_GENERAL_SETTINGS_SUCCESS passing the generalSettings
*/
export function generalSettingsLoaded(data) {
return {
type: LOAD_GENERAL_SETTINGS_SUCCESS,
data,
};
}
/**
* Dispatched when loading the generalSettings fails
*
* @param {object} error The error
*
* @return {object} An action object with a type of LOAD_GENERAL_SETTINGS_ERROR passing the error
*/
export function generalSettingsLoadingError(error) {
return {
type: LOAD_GENERAL_SETTINGS_ERROR,
error,
};
}
/**
* Change the `name` value
*
* @return {object} An action object with a type of CHANGE_NAME
*/
export function changeName(name) {
return {
type: CHANGE_NAME,
name,
};
}
/**
* Change the `description` value
*
* @return {object} An action object with a type of CHANGE_DESCRIPTION
*/
export function changeDescription(description) {
return {
type: CHANGE_DESCRIPTION,
description,
};
}
/**
* Change the `version` value
*
* @return {object} An action object with a type of CHANGE_VERSION
*/
export function changeVersion(version) {
return {
type: CHANGE_VERSION,
version,
};
}
/**
* Update the generalSettings, this action starts the request saga
*
* @return {object} An action object with a type of UPDATE_GENERAL_SETTINGS
*/
export function updateGeneralSettings(data) {
return {
type: UPDATE_GENERAL_SETTINGS,
data,
};
}
/**
* Dispatched when the generalSettings are updated by the request saga
*
* @param {object} generalSettings The generalSettings data
*
* @return {object} An action object with a type of UPDATE_GENERAL_SETTINGS_SUCCESS passing the generalSettings
*/
export function generalSettingsUpdated(data) {
return {
type: UPDATE_GENERAL_SETTINGS_SUCCESS,
data,
};
}
/**
* Dispatched when updating the generalSettings fails
*
* @param {object} error The error
*
* @return {object} An action object with a type of UPDATE_GENERAL_SETTINGS_ERROR passing the error
*/
export function generalSettingsUpdatedError(error) {
return {
type: UPDATE_GENERAL_SETTINGS_ERROR,
error,
};
}
/**
* Dispatched when cancelling the data modifications
*
* @return {object} An action object with a type of CANCEL_GENERAL_SETTINGS passing the error
*/
export function cancelGeneralSettings() {
return {
type: CANCEL_GENERAL_SETTINGS,
};
}

View File

@ -1,21 +0,0 @@
/*
* Constants
* Each action has a corresponding type, which the reducer knows and picks up on.
* To avoid weird typos between the reducer and the actions, we save them as
* constants here. We prefix them with 'yourplugin/YourComponent' so we avoid
* reducers accidentally picking up actions they shouldn't.
*
* Follow this format:
* export const YOUR_ACTION_CONSTANT = 'your-plugin/YourContainer/YOUR_ACTION_CONSTANT';
*/
export const LOAD_GENERAL_SETTINGS = 'settingsManager/HomePage/LOAD_GENERAL_SETTINGS';
export const LOAD_GENERAL_SETTINGS_SUCCESS = 'settingsManager/HomePage/LOAD_GENERAL_SETTINGS_SUCCESS';
export const LOAD_GENERAL_SETTINGS_ERROR = 'settingsManager/HomePage/LOAD_GENERAL_SETTINGS_ERROR';
export const CHANGE_NAME = 'settingsManager/HomePage/CHANGE_NAME';
export const CHANGE_DESCRIPTION = 'settingsManager/HomePage/CHANGE_DESCRIPTION';
export const CHANGE_VERSION = 'settingsManager/HomePage/CHANGE_VERSION';
export const UPDATE_GENERAL_SETTINGS = 'settingsManager/HomePage/UPDATE_GENERAL_SETTINGS';
export const UPDATE_GENERAL_SETTINGS_SUCCESS = 'settingsManager/HomePage/UPDATE_GENERAL_SETTINGS_SUCCESS';
export const UPDATE_GENERAL_SETTINGS_ERROR = 'settingsManager/HomePage/UPDATE_GENERAL_SETTINGS_ERROR';
export const CANCEL_GENERAL_SETTINGS = 'settingsManager/HomePage/CANCEL_GENERAL_SETTINGS';

View File

@ -1,175 +0,0 @@
/*
* HomePage
*/
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import {
// selectHome,
selectLoading,
selectError,
// selectGeneralSettings,
selectName,
selectDescription,
selectVersion,
// selectLocationState,
} from './selectors';
import {
loadGeneralSettings,
changeName,
changeDescription,
changeVersion,
updateGeneralSettings,
cancelGeneralSettings,
} from './actions';
import styles from './styles.scss';
export class HomePage extends React.Component {
componentDidMount() {
this.props.onPageLoad();
}
render() {
const { formatMessage } = this.props.intl;
return (
<div>
<div className="container">
<PluginHeader {...this.props}></PluginHeader>
<Container>
<RightContentTitle
title={formatMessage(appMessages.generalSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(messages.rightContentSectionTitle)}
description={formatMessage(messages.rightContentSectionDescription)}
/>
<form onSubmit={this.props.onFormSubmit}>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationName" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.nameLabel} />
</label>
<div className="col-xs-5">
<input
className="form-control"
type="text"
placeholder={formatMessage(messages.namePlaceholder)}
id="applicationName"
value={this.props.name || ''}
onChange={this.props.onChangeName}
autoFocus
/>
</div>
</div>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationDescription" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.descriptionLabel} />
</label>
<div className="col-xs-5">
<input
className="form-control"
type="text"
placeholder={formatMessage(messages.descriptionPlaceholder)}
id="applicationDescription"
value={this.props.description || ''}
onChange={this.props.onChangeDescription}
/>
</div>
</div>
<div className={`form-group row ${styles.homePageRightContentFormGroup}`}>
<label htmlFor="applicationVersion" className="col-xs-7 col-form-label">
<FormattedMessage {...messages.versionLabel} />
</label>
<div className="col-xs-5">
<input
className="form-control"
type="text"
placeholder={formatMessage(messages.versionPlaceholder)}
id="applicationVersion"
value={this.props.version || ''}
onChange={this.props.onChangeVersion}
/>
</div>
</div>
<button className="btn btn-primary hidden-xs-up" type="submit">Submit</button>
</form>
</Container>
</div>
</div>
);
}
}
HomePage.propTypes = {
// changeRoute: React.PropTypes.func,
description: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.bool,
]),
// error: React.PropTypes.oneOfType([
// React.PropTypes.object,
// React.PropTypes.bool,
// ]),
intl: intlShape.isRequired,
// loading: React.PropTypes.bool,
name: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.bool,
]),
// onCancel: React.PropTypes.func,
onChangeName: React.PropTypes.func,
onChangeDescription: React.PropTypes.func,
onChangeVersion: React.PropTypes.func,
onFormSubmit: React.PropTypes.func,
onPageLoad: React.PropTypes.func,
version: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.bool,
]),
};
export function mapDispatchToProps(dispatch) {
return {
onCancel: () => dispatch(cancelGeneralSettings()),
onChangeName: (evt) => dispatch(changeName(evt.target.value)),
onChangeDescription: (evt) => dispatch(changeDescription(evt.target.value)),
onChangeVersion: (evt) => dispatch(changeVersion(evt.target.value)),
onFormSubmit: (evt) => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
dispatch(updateGeneralSettings());
},
onPageLoad: (evt) => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
dispatch(loadGeneralSettings());
},
dispatch,
};
}
const mapStateToProps = createStructuredSelector({
name: selectName(),
description: selectDescription(),
error: selectError(),
loading: selectLoading(),
version: selectVersion(),
});
// Wrap the component to inject dispatch and state into it
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(HomePage));

View File

@ -1,42 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.HomePage.rightSectionDescription",
"defaultMessage": "Configure your general settings"
},
"rightContentSectionTitle": {
"id": "settings-manager.components.HomePage.rightContentSectionTitle",
"defaultMessage": "Application"
},
"rightContentSectionDescription": {
"id": "settings-manager.components.HomePage.rightContentSectionDescription",
"defaultMessage": "The general settings of your Strapi application."
},
"nameLabel": {
"id": "settings-manager.components.HomePage.nameLabel",
"defaultMessage": "Name"
},
"namePlaceholder": {
"id": "settings-manager.components.HomePage.namePlaceholder",
"defaultMessage": "My Application"
},
"descriptionLabel": {
"id": "settings-manager.components.HomePage.descriptionLabel",
"defaultMessage": "Description"
},
"descriptionPlaceholder": {
"id": "settings-manager.components.HomePage.descriptionPlaceholder",
"defaultMessage": "A Strapi application"
},
"versionLabel": {
"id": "settings-manager.components.HomePage.versionLabel",
"defaultMessage": "Version"
},
"versionPlaceholder": {
"id": "settings-manager.components.HomePage.versionPlaceholder",
"defaultMessage": "0.0.1"
},
"submit": {
"id": "settings-manager.components.HomePage.submit",
"defaultMessage": "Submit"
}
}

View File

@ -1,97 +0,0 @@
/*
* Reducer
*
* The reducer takes care of our data. Using actions, we can change our
* application state.
* To add a new action, add it to the switch statement in the reducer function
*
* Example:
* case YOUR_ACTION_CONSTANT:
* return state.set('yourStateVariable', true);
*/
import {
LOAD_GENERAL_SETTINGS_SUCCESS,
LOAD_GENERAL_SETTINGS,
LOAD_GENERAL_SETTINGS_ERROR,
CHANGE_NAME,
CHANGE_DESCRIPTION,
CHANGE_VERSION,
UPDATE_GENERAL_SETTINGS,
UPDATE_GENERAL_SETTINGS_SUCCESS,
UPDATE_GENERAL_SETTINGS_ERROR,
CANCEL_GENERAL_SETTINGS,
} from './constants';
import { fromJS } from 'immutable';
// The initial state of the App
const initialState = fromJS({
loading: false,
error: false,
name: false,
description: false,
version: false,
backup: fromJS({
name: false,
description: false,
version: false,
}),
});
function appReducer(state = initialState, action) {
switch (action.type) {
case LOAD_GENERAL_SETTINGS:
return state
.set('loading', true)
.set('error', false)
.set('name', false)
.set('description', false)
.set('version', false);
case LOAD_GENERAL_SETTINGS_SUCCESS:
return state
.set('loading', false)
.set('name', action.data.name)
.set('description', action.data.description)
.set('version', action.data.version)
.setIn(['backup', 'name'], action.data.name)
.setIn(['backup', 'description'], action.data.description)
.setIn(['backup', 'version'], action.data.version);
case LOAD_GENERAL_SETTINGS_ERROR:
return state
.set('error', action.error)
.set('loading', false);
case CHANGE_NAME:
return state
.set('name', action.name);
case CHANGE_DESCRIPTION:
return state
.set('description', action.description);
case CHANGE_VERSION:
return state
.set('version', action.version);
case UPDATE_GENERAL_SETTINGS:
return state
.set('loading', true)
.set('error', false);
case UPDATE_GENERAL_SETTINGS_SUCCESS:
return state
.set('loading', false)
.setIn(['backup', 'name'], action.data.name)
.setIn(['backup', 'description'], action.data.description)
.setIn(['backup', 'version'], action.data.version);
case UPDATE_GENERAL_SETTINGS_ERROR:
return state
.set('error', action.error)
.set('loading', false);
case CANCEL_GENERAL_SETTINGS:
return state
.set('name', state.get('backup').get('name'))
.set('description', state.get('backup').get('description'))
.set('version', state.get('backup').get('version'));
default:
return state;
}
}
export default appReducer;

View File

@ -1,112 +0,0 @@
/**
* Set of asynchronous functions.
*/
import { take, call, put, select, fork, cancel } from 'redux-saga/effects';
import { LOCATION_CHANGE } from 'react-router-redux';
import request from 'utils/request';
import {
LOAD_GENERAL_SETTINGS,
UPDATE_GENERAL_SETTINGS,
} from 'containers/HomePage/constants';
import {
generalSettingsLoaded,
generalSettingsLoadingError,
generalSettingsUpdated,
generalSettingsUpdatedError,
} from 'containers/HomePage/actions';
import {
selectName,
selectDescription,
selectVersion,
} from 'containers/HomePage/selectors';
import { apiUrl } from '../../app';
/**
* General Settings request/response handler
*/
export function* getGeneralSettings() {
const requestURL = `${apiUrl}/settings/general`;
// Call our request helper (see 'utils/request')
const generalSettings = yield call(request, requestURL);
if (!generalSettings.err) {
yield put(generalSettingsLoaded(generalSettings.data));
} else {
yield put(generalSettingsLoadingError(generalSettings.err));
}
}
/**
* Update general settings
*/
export function* updateGeneralSettings() {
const data = {
values: {
version: yield select(selectVersion()),
name: yield select(selectName()),
description: yield select(selectDescription()),
},
type: 'general',
};
const requestURL = `${apiUrl}/settings`;
// Call our request helper (see 'utils/request')
const generalSettings = yield call(
request,
requestURL, {
method: 'PUT',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}
);
if (!generalSettings.err) {
window.Strapi.notification.success('Your settings have successfully updated.');
yield put(generalSettingsUpdated(generalSettings.data));
} else {
window.Strapi.notification.error(generalSettings.err.message || 'An error occurred during settings update.');
yield put(generalSettingsUpdatedError(generalSettings.err));
}
}
/**
* Watches for LOAD_GENERAL_SETTINGS action and calls handler
*/
export function* getGeneralSettingsWatcher() {
while (yield take(LOAD_GENERAL_SETTINGS)) {
yield call(getGeneralSettings);
}
}
/**
* Watches for UPDATE_GENERAL_SETTINGS action and calls handler
*/
export function* updateGeneralSettingsWatcher() {
while (yield take(UPDATE_GENERAL_SETTINGS)) {
yield call(updateGeneralSettings);
}
}
/**
* Root saga manages watcher lifecycle
*/
export function* generalSettingsData() {
// Fork watchers so we can continue execution
const watcher = yield fork(getGeneralSettingsWatcher);
yield fork(updateGeneralSettingsWatcher);
// Suspend execution until location changes
yield take(LOCATION_CHANGE);
yield cancel(watcher);
}
// Bootstrap sagas
export default [
generalSettingsData,
];

View File

@ -1,67 +0,0 @@
/**
* The home state selectors
*/
import { createSelector } from 'reselect';
/*
* Select home state
*/
const selectHome = () => (state) => state.get('home');
const selectLoading = () => createSelector(
selectHome(),
(homeState) => homeState.get('loading')
);
const selectError = () => createSelector(
selectHome(),
(homeState) => homeState.get('error')
);
const selectGeneralSettings = () => createSelector(
selectHome(),
(homeState) => homeState.get('generalSettings')
);
const selectName = () => createSelector(
selectHome(),
(homeState) => homeState.get('name')
);
const selectDescription = () => createSelector(
selectHome(),
(homeState) => homeState.get('description')
);
const selectVersion = () => createSelector(
selectHome(),
(homeState) => homeState.get('version')
);
const selectLocationState = () => {
let prevRoutingState;
let prevRoutingStateJS;
return (state) => {
const routingState = state.get('route'); // or state.route
if (!routingState.equals(prevRoutingState)) {
prevRoutingState = routingState;
prevRoutingStateJS = routingState.toJS();
}
return prevRoutingStateJS;
};
};
export {
selectHome,
selectLoading,
selectError,
selectGeneralSettings,
selectName,
selectDescription,
selectVersion,
selectLocationState,
};

View File

@ -1,14 +0,0 @@
/**
* styles.scss
*
* Home container styles
*/
.homePage { /* stylelint-disable */
}
.homePageRightContentFormGroup {
padding-left: 1rem;
margin-bottom: 0.5rem;
}

View File

@ -1,54 +0,0 @@
/*
* LanguagesPage
*/
import React from 'react';
import Helmet from 'react-helmet';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
export class LanguagesPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.languagesPage}>
<Helmet
title="Settings Manager - Languages"
meta={[
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader />
<Container>
<RightContentTitle
title={formatMessage(appMessages.languagesSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</div>
</div>
);
}
}
LanguagesPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(LanguagesPage);

View File

@ -1,6 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.LanguagesPage.rightSectionDescription",
"defaultMessage": "Configure your languages settings"
}
}

View File

@ -1,3 +0,0 @@
.languagesPage { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import LanguagesPage from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<LanguagesPage />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,30 +0,0 @@
/**
* NotFoundPage
*
* This is the page we show when the user visits a url that doesn't have a route
*
* NOTE: while this component should technically be a stateless functional
* component (SFC), hot reloading does not currently support SFCs. If hot
* reloading is not a neccessity for you then you can refactor it and remove
* the linting exception.
*/
import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
export default class NotFound extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
<div>
<div className="container">
<h1>
<FormattedMessage {...messages.pageNotFound} />
</h1>
</div>
</div>
);
}
}

View File

@ -1,13 +0,0 @@
/*
* NotFoundPage Messages
*
* This contains all the text for the NotFoundPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
pageNotFound: {
id: 'app.components.NotFoundPage.pageNotFound',
defaultMessage: 'Page not found.',
},
});

View File

@ -1,53 +0,0 @@
/*
* SecurityPage
*/
import React from 'react';
import Helmet from 'react-helmet';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
export class SecurityPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.securityPage}>
<Helmet
title="Settings Manager - Security"
meta={[
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader />
<Container>
<RightContentTitle
title={formatMessage(appMessages.securitySectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</div>
</div>
);
}
}
SecurityPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(SecurityPage);

View File

@ -1,6 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.SecurityPage.rightSectionDescription",
"defaultMessage": "Configure your security settings"
}
}

View File

@ -1,3 +0,0 @@
.securityPage { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import SecurityPage from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<SecurityPage />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,53 +0,0 @@
/*
* ServerPage
*/
import React from 'react';
import Helmet from 'react-helmet';
import PluginHeader from 'components/PluginHeader';
import Container from 'components/Container';
import RightContentTitle from 'components/RightContentTitle';
import RightContentSectionTitle from 'components/RightContentSectionTitle';
import { injectIntl, intlShape } from 'react-intl';
import appMessages from 'containers/App/messages.json';
import messages from './messages.json';
import { define } from '../../i18n';
define(messages);
import styles from './styles.scss';
export class ServerPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
const { formatMessage } = this.props.intl;
return (
<div className={styles.serverPage}>
<Helmet
title="Settings Manager - Server"
meta={[
{ name: 'description', content: formatMessage(messages.rightSectionDescription) },
]}
/>
<div className="container">
<PluginHeader />
<Container>
<RightContentTitle
title={formatMessage(appMessages.serverSectionTitle)}
description={formatMessage(messages.rightSectionDescription)}
/>
<RightContentSectionTitle
title={formatMessage(appMessages.comingSoon)}
description=""
/>
</Container>
</div>
</div>
);
}
}
ServerPage.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(ServerPage);

View File

@ -1,6 +0,0 @@
{
"rightSectionDescription": {
"id": "settings-manager.components.ServerPage.rightSectionDescription",
"defaultMessage": "Configure your server settings"
}
}

View File

@ -1,3 +0,0 @@
.serverPage { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import ServerPage from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<ServerPage />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(true);
});
});

View File

@ -1,25 +0,0 @@
/**
* i18n.js
*
* This will setup the i18n language files and locale data for your plugin.
*
*/
import { defineMessages } from 'react-intl';
import enTranslationMessages from './translations/en.json';
import frTranslationMessages from './translations/fr.json';
const translationMessages = {
en: enTranslationMessages,
fr: frTranslationMessages,
};
const define = (messages) => {
defineMessages(messages);
};
export {
translationMessages,
define,
};

View File

@ -1,18 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<!-- The first thing in any HTML file should be the charset -->
<meta charset="utf-8">
<!-- Make the page mobile compatible -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Allow installing the app to the homescreen -->
<meta name="mobile-web-app-capable" content="yes">
<title>Settings Manager - Strapi</title>
</head>
<body>
<!-- Display a message if JS has been disabled on the browser. -->
<noscript>If you're seeing this message, that means <strong>JavaScript has been disabled on your browser</strong>, please <strong>enable JS</strong> to make this app work.</noscript>
<!-- The app hooks into this div -->
<div id="app"></div>
</body>
</html>

View File

@ -1,46 +0,0 @@
/**
* Combine all reducers in this file and export the combined reducers.
* If we were to do this in store.js, reducers wouldn't be hot reloadable.
*/
import { combineReducers } from 'redux-immutable';
import { fromJS } from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
/*
* routeReducer
*
* The reducer merges route location changes into our immutable state.
* The change is necessitated by moving to react-router-redux@4
*
*/
// Initial routing state
const routeInitialState = fromJS({
locationBeforeTransitions: null,
});
/**
* Merge route into the global application state
*/
function routeReducer(state = routeInitialState, action) {
switch (action.type) {
/* istanbul ignore next */
case LOCATION_CHANGE:
return state.merge({
locationBeforeTransitions: action.payload,
});
default:
return state;
}
}
/**
* Creates the main reducer with the asynchronously loaded ones
*/
export default function createReducer(asyncReducers) {
return combineReducers({
route: routeReducer,
...asyncReducers,
});
}

View File

@ -1,94 +0,0 @@
// These are the pages you can go to.
// They are all wrapped in the App component, which should contain the navbar etc
// See http://blog.mxstbr.com/2016/01/react-apps-with-pages for more information
// about the code splitting business
import { getAsyncInjectors } from 'utils/asyncInjectors';
const loadModule = (cb) => (componentModule) => {
cb(null, componentModule.default);
};
const errorLoading = (err) => {
console.error('Dynamic page loading failed', err); // eslint-disable-line no-console
};
export default function createRoutes(store) {
// Create reusable async injectors using getAsyncInjectors factory
const { injectReducer, injectSagas } = getAsyncInjectors(store); // eslint-disable-line no-unused-vars
return [
{
path: '',
name: 'home',
getComponent(nextState, cb) {
const reducer = require('containers/HomePage/reducer'); // eslint-disable-line global-require
const sagas = require('containers/HomePage/sagas'); // eslint-disable-line global-require
const component = require('containers/HomePage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
injectReducer('home', reducer.default);
injectSagas(sagas.default);
renderRoute(component);
},
}, {
path: '/languages',
name: 'languages',
getComponent(nextState, cb) {
const component = require('containers/LanguagesPage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
renderRoute(component);
},
}, {
path: '/databases',
name: 'databases',
getComponent(nextState, cb) {
const component = require('containers/DatabasesPage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
renderRoute(component);
},
}, {
path: '/security',
name: 'security',
getComponent(nextState, cb) {
const component = require('containers/SecurityPage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
renderRoute(component);
},
}, {
path: '/server',
name: 'server',
getComponent(nextState, cb) {
const component = require('containers/ServerPage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
renderRoute(component);
},
}, {
path: '/advanced',
name: 'advanced',
getComponent(nextState, cb) {
const component = require('containers/AdvancedPage'); // eslint-disable-line global-require
const renderRoute = loadModule(cb);
renderRoute(component);
},
}, {
path: '*',
name: 'notfound',
getComponent(nextState, cb) {
System.import('containers/NotFoundPage')
.then(loadModule(cb))
.catch(errorLoading);
},
},
];
}

View File

@ -1,51 +0,0 @@
/**
* Create the store with asynchronously loaded reducers
*/
import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';
const sagaMiddleware = createSagaMiddleware();
const devtools = window.devToolsExtension || (() => (noop) => noop);
export default function configureStore(initialState = {}, history) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [
sagaMiddleware,
routerMiddleware(history),
];
const enhancers = [
applyMiddleware(...middlewares),
devtools(),
];
const store = createStore(
createReducer(),
fromJS(initialState),
compose(...enhancers)
);
// Create hook for async sagas
store.runSaga = sagaMiddleware.run;
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
System.import('./reducers').then((reducerModule) => {
const createReducers = reducerModule.default;
const nextReducers = createReducers(store.asyncReducers);
store.replaceReducer(nextReducers);
});
}
// Initialize it with no other reducers
store.asyncReducers = {};
return store;
}

View File

@ -1,2 +0,0 @@
// Import variables
@import 'variables/variables';

View File

@ -1,2 +0,0 @@
// Import admin variables
@import "../../../../../strapi-generate-admin/files/admin/public/app/styles/variables/variables";

View File

@ -1,27 +0,0 @@
/**
* Test store addons
*/
import expect from 'expect';
import configureStore from '../store'; // eslint-disable-line
import { browserHistory } from 'react-router';
describe('configureStore', () => {
let store;
before(() => {
store = configureStore({}, browserHistory);
});
describe('asyncReducers', () => {
it('should contain an object for async reducers', () => {
expect(typeof store.asyncReducers).toEqual('object');
});
});
describe('runSaga', () => {
it('should contain a hook for `sagaMiddleware.run`', () => {
expect(typeof store.runSaga).toEqual('function');
});
});
});

View File

@ -1,28 +0,0 @@
{
"settings-manager.components.AdvancedPage.rightSectionDescription": "",
"settings-manager.components.App.advancedSectionTitle": "",
"settings-manager.components.App.comingSoon": "",
"settings-manager.components.App.databasesSectionTitle": "",
"settings-manager.components.App.generalSectionTitle": "",
"settings-manager.components.App.languagesSectionTitle": "",
"settings-manager.components.App.securitySectionTitle": "",
"settings-manager.components.App.serverSectionTitle": "",
"settings-manager.components.DatabasesPage.rightSectionDescription": "",
"settings-manager.components.HomePage.descriptionLabel": "",
"settings-manager.components.HomePage.descriptionPlaceholder": "",
"settings-manager.components.HomePage.nameLabel": "",
"settings-manager.components.HomePage.namePlaceholder": "",
"settings-manager.components.HomePage.rightContentSectionDescription": "",
"settings-manager.components.HomePage.rightContentSectionTitle": "",
"settings-manager.components.HomePage.rightSectionDescription": "",
"settings-manager.components.HomePage.submit": "",
"settings-manager.components.HomePage.versionLabel": "",
"settings-manager.components.HomePage.versionPlaceholder": "",
"settings-manager.components.LanguagesPage.rightSectionDescription": "",
"settings-manager.components.PluginHeaderActions.cancelLabel": "",
"settings-manager.components.PluginHeaderActions.saveLabel": "",
"settings-manager.components.PluginHeaderTitle.description": "",
"settings-manager.components.PluginHeaderTitle.title": "",
"settings-manager.components.SecurityPage.rightSectionDescription": "",
"settings-manager.components.ServerPage.rightSectionDescription": ""
}

View File

@ -1,28 +0,0 @@
{
"settings-manager.components.AdvancedPage.rightSectionDescription": "",
"settings-manager.components.App.advancedSectionTitle": "",
"settings-manager.components.App.comingSoon": "",
"settings-manager.components.App.databasesSectionTitle": "",
"settings-manager.components.App.generalSectionTitle": "",
"settings-manager.components.App.languagesSectionTitle": "",
"settings-manager.components.App.securitySectionTitle": "",
"settings-manager.components.App.serverSectionTitle": "",
"settings-manager.components.DatabasesPage.rightSectionDescription": "",
"settings-manager.components.HomePage.descriptionLabel": "",
"settings-manager.components.HomePage.descriptionPlaceholder": "",
"settings-manager.components.HomePage.nameLabel": "",
"settings-manager.components.HomePage.namePlaceholder": "",
"settings-manager.components.HomePage.rightContentSectionDescription": "",
"settings-manager.components.HomePage.rightContentSectionTitle": "",
"settings-manager.components.HomePage.rightSectionDescription": "",
"settings-manager.components.HomePage.submit": "",
"settings-manager.components.HomePage.versionLabel": "",
"settings-manager.components.HomePage.versionPlaceholder": "",
"settings-manager.components.LanguagesPage.rightSectionDescription": "",
"settings-manager.components.PluginHeaderActions.cancelLabel": "",
"settings-manager.components.PluginHeaderActions.saveLabel": "",
"settings-manager.components.PluginHeaderTitle.description": "",
"settings-manager.components.PluginHeaderTitle.title": "",
"settings-manager.components.SecurityPage.rightSectionDescription": "",
"settings-manager.components.ServerPage.rightSectionDescription": ""
}

View File

@ -1,28 +0,0 @@
{
"settings-manager.components.AdvancedPage.rightSectionDescription": "Configurez vos réglages avancés",
"settings-manager.components.App.advancedSectionTitle": "Avancés",
"settings-manager.components.App.comingSoon": "Bientôt disponible",
"settings-manager.components.App.databasesSectionTitle": "Base de données",
"settings-manager.components.App.generalSectionTitle": "Général",
"settings-manager.components.App.languagesSectionTitle": "Langages",
"settings-manager.components.App.securitySectionTitle": "Sécurité",
"settings-manager.components.App.serverSectionTitle": "Serveur",
"settings-manager.components.DatabasesPage.rightSectionDescription": "Configurez vos réglages de bases de données",
"settings-manager.components.HomePage.descriptionLabel": "Description",
"settings-manager.components.HomePage.descriptionPlaceholder": "Une application Strapi",
"settings-manager.components.HomePage.nameLabel": "Nom",
"settings-manager.components.HomePage.namePlaceholder": "monApplication",
"settings-manager.components.HomePage.rightContentSectionDescription": "Réglages généraux de votre application Strapi.",
"settings-manager.components.HomePage.rightContentSectionTitle": "Application",
"settings-manager.components.HomePage.rightSectionDescription": "Configurez vos réglages généraux",
"settings-manager.components.HomePage.submit": "Soumettre",
"settings-manager.components.HomePage.versionLabel": "Version",
"settings-manager.components.HomePage.versionPlaceholder": "0.0.1",
"settings-manager.components.LanguagesPage.rightSectionDescription": "Configurez vos réglages de langages",
"settings-manager.components.PluginHeaderActions.cancelLabel": "Annuler",
"settings-manager.components.PluginHeaderActions.saveLabel": "Sauvegarder",
"settings-manager.components.PluginHeaderTitle.description": "Mettez facilement à jour vos configurations",
"settings-manager.components.PluginHeaderTitle.title": "Gestionnaire de configurations",
"settings-manager.components.SecurityPage.rightSectionDescription": "Configurez vos réglages de sécurité",
"settings-manager.components.ServerPage.rightSectionDescription": "Configurez vos réglages serveur"
}

View File

@ -1,72 +0,0 @@
import { conformsTo, isEmpty, isFunction, isObject, isString } from 'lodash';
import invariant from 'invariant';
import warning from 'warning';
import createReducer from 'reducers';
/**
* Validate the shape of redux store
*/
export function checkStore(store) {
const shape = {
dispatch: isFunction,
subscribe: isFunction,
getState: isFunction,
replaceReducer: isFunction,
runSaga: isFunction,
asyncReducers: isObject,
};
invariant(
conformsTo(store, shape),
'(app/utils...) asyncInjectors: Expected a valid redux store'
);
}
/**
* Inject an asynchronously loaded reducer
*/
export function injectAsyncReducer(store, isValid) {
return function injectReducer(name, asyncReducer) {
if (!isValid) checkStore(store);
invariant(
isString(name) && !isEmpty(name) && isFunction(asyncReducer),
'(app/utils...) injectAsyncReducer: Expected `asyncReducer` to be a reducer function'
);
store.asyncReducers[name] = asyncReducer; // eslint-disable-line no-param-reassign
store.replaceReducer(createReducer(store.asyncReducers));
};
}
/**
* Inject an asynchronously loaded saga
*/
export function injectAsyncSagas(store, isValid) {
return function injectSagas(sagas) {
if (!isValid) checkStore(store);
invariant(
Array.isArray(sagas),
'(app/utils...) injectAsyncSagas: Expected `sagas` to be an array of generator functions'
);
warning(
!isEmpty(sagas),
'(app/utils...) injectAsyncSagas: Received an empty `sagas` array'
);
sagas.map(store.runSaga);
};
}
/**
* Helper for creating injectors
*/
export function getAsyncInjectors(store) {
checkStore(store);
return {
injectReducer: injectAsyncReducer(store, true),
injectSagas: injectAsyncSagas(store, true),
};
}

View File

@ -1,59 +0,0 @@
import 'whatwg-fetch';
/**
* Parses the JSON returned by a network request
*
* @param {object} response A response from a network request
*
* @return {object} The parsed JSON from the request
*/
function parseJSON(response) {
return response.json();
}
/**
* Checks if a network request came back fine, and throws an error if not
*
* @param {object} response A response from a network request
*
* @return {Promise} Returns either the response, or throws an error
*/
function checkStatus(response) {
return new Promise((resolve) => {
if (response.status >= 200 && response.status < 300) {
return resolve(response);
}
return parseJSON(response)
.then((data) => {
const error = new Error(data.message || response.statusText);
error.data = data;
error.response = response;
throw error;
});
});
}
/**
* Requests a URL, returning a promise
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
*
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
// Default headers
const params = options || { };
const defaultHeaders = {
Accept: 'application/json',
'Content-Type': 'application/json',
};
params.headers = params && params.headers ? params.headers : defaultHeaders;
return fetch(url, params)
.then(checkStatus)
.then(parseJSON)
.then((data) => ({ data }))
.catch((err) => ({ err }));
}

View File

@ -1,164 +0,0 @@
/**
* Test async injectors
*/
import expect from 'expect';
import configureStore from 'store';
import { memoryHistory } from 'react-router';
import { put } from 'redux-saga/effects';
import { fromJS } from 'immutable';
import {
injectAsyncReducer,
injectAsyncSagas,
getAsyncInjectors,
} from 'utils/asyncInjectors';
// Fixtures
const initialState = fromJS({ reduced: 'soon' });
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'TEST':
return state.set('reduced', action.payload);
default:
return state;
}
};
function* testSaga() {
yield put({ type: 'TEST', payload: 'yup' });
}
const sagas = [
testSaga,
];
describe('asyncInjectors', () => {
let store;
describe('getAsyncInjectors', () => {
before(() => {
store = configureStore({}, memoryHistory);
});
it('given a store, should return all async injectors', () => {
const { injectReducer, injectSagas } = getAsyncInjectors(store);
injectReducer('test', reducer);
injectSagas(sagas);
const actual = store.getState().get('test');
const expected = initialState.merge({ reduced: 'yup' });
expect(actual.toJS()).toEqual(expected.toJS());
});
it('should throw if passed invalid store shape', () => {
let result = false;
Reflect.deleteProperty(store, 'dispatch');
try {
getAsyncInjectors(store);
} catch (err) {
result = err.name === 'Invariant Violation';
}
expect(result).toEqual(true);
});
});
describe('helpers', () => {
before(() => {
store = configureStore({}, memoryHistory);
});
describe('injectAsyncReducer', () => {
it('given a store, it should provide a function to inject a reducer', () => {
const injectReducer = injectAsyncReducer(store);
injectReducer('test', reducer);
const actual = store.getState().get('test');
const expected = initialState;
expect(actual.toJS()).toEqual(expected.toJS());
});
it('should throw if passed invalid name', () => {
let result = false;
const injectReducer = injectAsyncReducer(store);
try {
injectReducer('', reducer);
} catch (err) {
result = err.name === 'Invariant Violation';
}
try {
injectReducer(999, reducer);
} catch (err) {
result = err.name === 'Invariant Violation';
}
expect(result).toEqual(true);
});
it('should throw if passed invalid reducer', () => {
let result = false;
const injectReducer = injectAsyncReducer(store);
try {
injectReducer('bad', 'nope');
} catch (err) {
result = err.name === 'Invariant Violation';
}
try {
injectReducer('coolio', 12345);
} catch (err) {
result = err.name === 'Invariant Violation';
}
expect(result).toEqual(true);
});
});
describe('injectAsyncSagas', () => {
it('given a store, it should provide a function to inject a saga', () => {
const injectSagas = injectAsyncSagas(store);
injectSagas(sagas);
const actual = store.getState().get('test');
const expected = initialState.merge({ reduced: 'yup' });
expect(actual.toJS()).toEqual(expected.toJS());
});
it('should throw if passed invalid saga', () => {
let result = false;
const injectSagas = injectAsyncSagas(store);
try {
injectSagas({ testSaga });
} catch (err) {
result = err.name === 'Invariant Violation';
}
try {
injectSagas(testSaga);
} catch (err) {
result = err.name === 'Invariant Violation';
}
expect(result).toEqual(true);
});
});
});
});

View File

@ -1,44 +0,0 @@
# http://www.appveyor.com/docs/appveyor-yml
# Set build version format here instead of in the admin panel
version: "{build}"
# Do not build on gh tags
skip_tags: true
# Test against these versions of Node.js
environment:
matrix:
# Node versions to run
- nodejs_version: "5.0"
# Fix line endings in Windows. (runs before repo cloning)
init:
- git config --global core.autocrlf input
# Install scripts--runs after repo cloning
install:
# Install chrome
- choco install -y googlechrome
# Install the latest stable version of Node
- ps: Install-Product node $env:nodejs_version
- npm -g install npm
- set PATH=%APPDATA%\npm;%PATH%
- npm install
# Disable automatic builds
build: off
# Post-install test scripts
test_script:
# Output debugging info
- node --version
- npm --version
# run build and run tests
- npm run build
# remove, as appveyor doesn't support secure variables on pr builds
# so `COVERALLS_REPO_TOKEN` cannot be set, without hard-coding in this file
#on_success:
#- npm run coveralls

View File

@ -1,56 +0,0 @@
const resolve = require('path').resolve;
const pullAll = require('lodash/pullAll');
const uniq = require('lodash/uniq');
const ReactBoilerplate = {
// This refers to the react-boilerplate version this project is based on.
version: '3.0.0',
/**
* The DLL Plugin provides a dramatic speed increase to webpack build and hot module reloading
* by caching the module metadata for all of our npm dependencies. We enable it by default
* in development.
*
*
* To disable the DLL Plugin, set this value to false.
*/
dllPlugin: {
defaults: {
/**
* we need to exclude dependencies which are not intended for the browser
* by listing them here.
*/
exclude: [
'chalk',
'compression',
'cross-env',
'express',
'ip',
'minimist',
'sanitize.css',
],
/**
* Specify any additional dependencies here. We include core-js and lodash
* since a lot of our dependencies depend on them and they get picked up by webpack.
*/
include: ['core-js', 'eventsource-polyfill', 'babel-polyfill', 'lodash'],
// The path where the DLL manifest and bundle will get built
path: resolve('../node_modules/react-boilerplate-dlls'),
},
entry(pkg) {
const dependencyNames = Object.keys(pkg.dependencies);
const exclude = pkg.dllPlugin.exclude || ReactBoilerplate.dllPlugin.defaults.exclude;
const include = pkg.dllPlugin.include || ReactBoilerplate.dllPlugin.defaults.include;
const includeDependencies = uniq(dependencyNames.concat(include));
return {
reactBoilerplateDeps: pullAll(includeDependencies, exclude),
};
},
},
};
module.exports = ReactBoilerplate;

View File

@ -1,33 +0,0 @@
/**
*
* {{ properCase name }}
*
*/
import React from 'react';
{{#if wantMessages}}
import { FormattedMessage } from 'react-intl';
import messages from './messages';
{{/if}}
{{#if wantCSS}}
import styles from './styles.css';
{{/if}}
class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
{{#if wantCSS}}
<div className={{curly true}}styles.{{ camelCase name }}{{curly}}>
{{else}}
<div>
{{/if}}
{{#if wantMessages}}
<FormattedMessage {...messages.header} />
{{/if}}
</div>
);
}
}
export default {{ properCase name }};

View File

@ -1,74 +0,0 @@
/**
* Component Generator
*/
const componentExists = require('../utils/componentExists');
module.exports = {
description: 'Add an unconnected component',
prompts: [{
type: 'list',
name: 'type',
message: 'Select the type of component',
default: 'Stateless Function',
choices: () => ['ES6 Class', 'Stateless Function'],
}, {
type: 'input',
name: 'name',
message: 'What should it be called?',
default: 'Button',
validate: (value) => {
if ((/.+/).test(value)) {
return componentExists(value) ? 'A component or container with this name already exists' : true;
}
return 'The name is required';
},
}, {
type: 'confirm',
name: 'wantCSS',
default: true,
message: 'Does it have styling?',
}, {
type: 'confirm',
name: 'wantMessages',
default: true,
message: 'Do you want i18n messages (i.e. will this component use text)?',
}],
actions: (data) => {
// Generate index.js and index.test.js
const actions = [{
type: 'add',
path: '../../app/components/{{properCase name}}/index.js',
templateFile: data.type === 'ES6 Class' ? './component/es6.js.hbs' : './component/stateless.js.hbs',
abortOnFail: true,
}, {
type: 'add',
path: '../../app/components/{{properCase name}}/tests/index.test.js',
templateFile: './component/test.js.hbs',
abortOnFail: true,
}];
// If they want a CSS file, add styles.css
if (data.wantCSS) {
actions.push({
type: 'add',
path: '../../app/components/{{properCase name}}/styles.css',
templateFile: './component/styles.css.hbs',
abortOnFail: true,
});
}
// If they want a i18n messages file
if (data.wantMessages) {
actions.push({
type: 'add',
path: '../../app/components/{{properCase name}}/messages.js',
templateFile: './component/messages.js.hbs',
abortOnFail: true,
});
}
return actions;
},
};

View File

@ -1,13 +0,0 @@
/*
* {{ properCase name }} Messages
*
* This contains all the text for the {{ properCase name }} component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
header: {
id: 'app.components.{{ properCase name }}.header',
defaultMessage: 'This is the {{ properCase name}} component !',
},
});

View File

@ -1,32 +0,0 @@
/**
*
* {{ properCase name }}
*
*/
import React from 'react';
{{#if wantMessages}}
import { FormattedMessage } from 'react-intl';
import messages from './messages';
{{/if}}
{{#if wantCSS}}
import styles from './styles.css';
{{/if}}
function {{ properCase name }}() {
return (
{{#if wantCSS}}
<div className={{curly true}}styles.{{ camelCase name }}{{curly}}>
{{else}}
<div>
{{/if}}
{{#if wantMessages}}
<FormattedMessage {...messages.header} />
{{/if}}
</div>
);
}
export default {{ properCase name }};

View File

@ -1,3 +0,0 @@
.{{ camelCase name }} { /* stylelint-disable */
}

View File

@ -1,11 +0,0 @@
// import {{ properCase name }} from '../index';
import expect from 'expect';
// import { shallow } from 'enzyme';
// import React from 'react';
describe('<{{ properCase name }} />', () => {
it('Expect to have unit tests specified', () => {
expect(true).toEqual(false);
});
});

View File

@ -1,15 +0,0 @@
/*
*
* {{ properCase name }} actions
*
*/
import {
DEFAULT_ACTION,
} from './constants';
export function defaultAction() {
return {
type: DEFAULT_ACTION,
};
}

View File

@ -1,18 +0,0 @@
import expect from 'expect';
import {
defaultAction,
} from '../actions';
import {
DEFAULT_ACTION,
} from '../constants';
describe('{{ properCase name }} actions', () => {
describe('Default Action', () => {
it('has a type of DEFAULT_ACTION', () => {
const expected = {
type: DEFAULT_ACTION,
};
expect(defaultAction()).toEqual(expected);
});
});
});

View File

@ -1,7 +0,0 @@
/*
*
* {{ properCase name }} constants
*
*/
export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION';

View File

@ -1,153 +0,0 @@
/**
* Container Generator
*/
const componentExists = require('../utils/componentExists');
module.exports = {
description: 'Add a container component',
prompts: [{
type: 'input',
name: 'name',
message: 'What should it be called?',
default: 'Form',
validate: (value) => {
if ((/.+/).test(value)) {
return componentExists(value) ? 'A component or container with this name already exists' : true;
}
return 'The name is required';
},
}, {
type: 'confirm',
name: 'wantHeaders',
default: false,
message: 'Do you want headers?',
}, {
type: 'confirm',
name: 'wantCSS',
default: false,
message: 'Does it have styling?',
}, {
type: 'confirm',
name: 'wantActionsAndReducer',
default: true,
message: 'Do you want an actions/constants/selectors/reducer tupel for this container?',
}, {
type: 'confirm',
name: 'wantSagas',
default: true,
message: 'Do you want sagas for asynchronous flows? (e.g. fetching data)',
}, {
type: 'confirm',
name: 'wantMessages',
default: true,
message: 'Do you want i18n messages (i.e. will this component use text)?',
}],
actions: (data) => {
// Generate index.js and index.test.js
const actions = [{
type: 'add',
path: '../../app/containers/{{properCase name}}/index.js',
templateFile: './container/index.js.hbs',
abortOnFail: true,
}, {
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/index.test.js',
templateFile: './container/test.js.hbs',
abortOnFail: true,
}];
// If they want a CSS file, add styles.css
if (data.wantCSS) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/styles.css',
templateFile: './container/styles.css.hbs',
abortOnFail: true,
});
}
// If component wants messages
if (data.wantMessages) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/messages.js',
templateFile: './container/messages.js.hbs',
abortOnFail: true,
});
}
// If they want actions and a reducer, generate actions.js, constants.js,
// reducer.js and the corresponding tests for actions and the reducer
if (data.wantActionsAndReducer) {
// Actions
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/actions.js',
templateFile: './container/actions.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/actions.test.js',
templateFile: './container/actions.test.js.hbs',
abortOnFail: true,
});
// Constants
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/constants.js',
templateFile: './container/constants.js.hbs',
abortOnFail: true,
});
// Selectors
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/selectors.js',
templateFile: './container/selectors.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/selectors.test.js',
templateFile: './container/selectors.test.js.hbs',
abortOnFail: true,
});
// Reducer
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/reducer.js',
templateFile: './container/reducer.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/reducer.test.js',
templateFile: './container/reducer.test.js.hbs',
abortOnFail: true,
});
}
// Sagas
if (data.wantSagas) {
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/sagas.js',
templateFile: './container/sagas.js.hbs',
abortOnFail: true,
});
actions.push({
type: 'add',
path: '../../app/containers/{{properCase name}}/tests/sagas.test.js',
templateFile: './container/sagas.test.js.hbs',
abortOnFail: true,
});
}
return actions;
},
};

View File

@ -1,61 +0,0 @@
/*
*
* {{properCase name }}
*
*/
import React from 'react';
import { connect } from 'react-redux';
{{#if wantHeaders}}
import Helmet from 'react-helmet';
{{/if}}
{{#if wantActionsAndReducer}}
import select{{properCase name}} from './selectors';
{{/if}}
{{#if wantMessages}}
import { FormattedMessage } from 'react-intl';
import messages from './messages';
{{/if}}
{{#if wantCSS}}
import styles from './styles.css';
{{/if}}
export class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function
render() {
return (
{{#if wantCSS}}
<div className={{curly true}}styles.{{ camelCase name }}{{curly}}>
{{else}}
<div>
{{/if}}
{{#if wantHeaders}}
<Helmet
title="{{properCase name}}"
meta={{curly true}}[
{{curly true}} name: 'description', content: 'Description of {{properCase name}}' {{curly}},
]{{curly}}
/>
{{/if}}
{{#if wantMessages}}
<FormattedMessage {...messages.header} />
{{/if}}
</div>
);
}
}
{{#if wantActionsAndReducer}}
const mapStateToProps = select{{properCase name}}();
{{/if}}
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
{{#if wantActionsAndReducer}}
export default connect(mapStateToProps, mapDispatchToProps)({{ properCase name }});
{{else}}
export default connect(mapDispatchToProps)({{ properCase name }});
{{/if}}

View File

@ -1,13 +0,0 @@
/*
* {{properCase name }} Messages
*
* This contains all the text for the {{properCase name }} component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
header: {
id: 'app.containers.{{properCase name }}.header',
defaultMessage: 'This is {{properCase name}} container !',
},
});

Some files were not shown because too many files have changed in this diff Show More