diff --git a/src/servers/CreateServer.js b/src/servers/CreateServer.js
index 6cda5046..5e8e7fe7 100644
--- a/src/servers/CreateServer.js
+++ b/src/servers/CreateServer.js
@@ -1,34 +1,33 @@
-import { assoc, pick } from 'ramda';
+import { assoc, dissoc, pick, pipe } from 'ramda';
import React from 'react';
import { connect } from 'react-redux';
import { createServer } from './reducers/server';
import { resetSelectedServer } from './reducers/selectedServer';
import { v4 as uuid } from 'uuid';
-import { UncontrolledTooltip } from 'reactstrap';
import './CreateServer.scss';
+import ImportServersBtn from './helpers/ImportServersBtn';
export class CreateServer extends React.Component {
state = {
name: '',
url: '',
apiKey: '',
+ serversImported: false,
};
submit = e => {
e.preventDefault();
const { createServer, history: { push } } = this.props;
- const server = assoc('id', uuid(), this.state);
+ const server = pipe(
+ assoc('id', uuid()),
+ dissoc('serversImported')
+ )(this.state);
createServer(server);
push(`/server/${server.id}/list-short-urls/1`)
};
- constructor(props) {
- super(props);
- this.fileRef = React.createRef();
- }
-
componentDidMount() {
this.props.resetSelectedServer();
}
@@ -60,30 +59,29 @@ export class CreateServer extends React.Component {
{renderInputGroup('apiKey', 'API key')}
-
-
- You can create servers by importing a CSV file with columns "name", "apiKey" and "url"
-
- console.log(file)}
- accept="text/csv"
- className="create-server__csv-select"
- ref={this.fileRef}
- />
+ {
+ this.setState({ serversImported: true });
+ setTimeout(() => this.setState({ serversImported: false }), 4000);
+ }} />
+
+ {this.state.serversImported && (
+
+
+
+ Servers properly imported. You can now select one from the list :)
+
+
+
+ )}
);
}
}
-export default connect(pick(['selectedServer']), {createServer, resetSelectedServer })(CreateServer);
+export default connect(
+ pick(['selectedServer']),
+ {createServer, resetSelectedServer }
+)(CreateServer);
diff --git a/src/servers/helpers/ImportServersBtn.js b/src/servers/helpers/ImportServersBtn.js
new file mode 100644
index 00000000..9d43041f
--- /dev/null
+++ b/src/servers/helpers/ImportServersBtn.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { UncontrolledTooltip } from 'reactstrap';
+import serversImporter from '../services/ServersImporter';
+import { createServers } from '../reducers/server';
+import { assoc } from 'ramda';
+import { v4 as uuid } from 'uuid';
+import PropTypes from 'prop-types';
+
+const defaultProps = {
+ serversImporter,
+};
+const propTypes = {
+ onChange: PropTypes.func,
+};
+
+export class ImportServersBtn extends React.Component {
+ constructor(props) {
+ super(props);
+ this.fileRef = React.createRef();
+ }
+
+ render() {
+ const { serversImporter, onImport } = this.props;
+ const onChange = e => serversImporter.importServersFromFile(e.target.files[0]).then(
+ servers => {
+ const { createServers } = this.props;
+ const serversWithIds = servers.map(server => assoc('id', uuid(), server));
+ createServers(serversWithIds);
+ onImport(serversWithIds);
+ }
+ );
+
+ return (
+
+
+
+
+ You can create servers by importing a CSV file with columns "name", "apiKey" and "url"
+
+
+
+ );
+ }
+}
+
+ImportServersBtn.defaultProps = defaultProps;
+ImportServersBtn.propTypes = propTypes;
+
+export default connect(null, { createServers })(ImportServersBtn);
diff --git a/src/servers/reducers/server.js b/src/servers/reducers/server.js
index 08c86c2e..adc6e895 100644
--- a/src/servers/reducers/server.js
+++ b/src/servers/reducers/server.js
@@ -2,17 +2,11 @@ import ServersService from '../services/ServersService';
import { curry } from 'ramda';
export const FETCH_SERVERS = 'shlink/servers/FETCH_SERVERS';
-export const CREATE_SERVER = 'shlink/servers/CREATE_SERVER';
-export const DELETE_SERVER = 'shlink/servers/DELETE_SERVER';
export default function reducer(state = {}, action) {
switch (action.type) {
case FETCH_SERVERS:
- case DELETE_SERVER:
return action.servers;
- case CREATE_SERVER:
- const server = action.server;
- return { ...state, [server.id]: server };
default:
return state;
}
@@ -35,3 +29,9 @@ export const _deleteServer = (ServersService, server) => {
return _listServers(ServersService);
};
export const deleteServer = curry(_deleteServer)(ServersService);
+
+export const _createServers = (ServersService, servers) => {
+ ServersService.createServers(servers);
+ return _listServers(ServersService);
+};
+export const createServers = curry(_createServers)(ServersService);
diff --git a/src/servers/services/ServersImporter.js b/src/servers/services/ServersImporter.js
new file mode 100644
index 00000000..8e2c364e
--- /dev/null
+++ b/src/servers/services/ServersImporter.js
@@ -0,0 +1,27 @@
+import csvjson from 'csvjson';
+
+export class ServersImporter {
+ constructor(csvjson) {
+ this.csvjson = csvjson;
+ }
+
+ importServersFromFile = (file) => {
+ if (!file || file.type !== 'text/csv') {
+ return Promise.reject('No file provided or file is not a CSV');
+ }
+
+ const reader = new FileReader();
+ return new Promise(resolve => {
+ reader.onloadend = e => {
+ const content = e.target.result;
+ const servers = this.csvjson.toObject(content);
+
+ resolve(servers);
+ };
+ reader.readAsText(file);
+ });
+ };
+}
+
+const serversImporter = new ServersImporter(csvjson);
+export default serversImporter;
diff --git a/src/servers/services/ServersService.js b/src/servers/services/ServersService.js
index 6fda7987..35f7f9b5 100644
--- a/src/servers/services/ServersService.js
+++ b/src/servers/services/ServersService.js
@@ -1,5 +1,5 @@
import Storage from '../../utils/Storage';
-import { dissoc } from 'ramda';
+import { assoc, dissoc, reduce } from 'ramda';
const SERVERS_STORAGE_KEY = 'servers';
@@ -8,25 +8,26 @@ export class ServersService {
this.storage = storage;
}
- listServers = () => {
- return this.storage.get(SERVERS_STORAGE_KEY) || {};
+ listServers = () => this.storage.get(SERVERS_STORAGE_KEY) || {};
+
+ findServerById = serverId => this.listServers()[serverId];
+
+ createServer = server => this.createServers([server]);
+
+ createServers = servers => {
+ const allServers = reduce(
+ (serversObj, server) => assoc(server.id, server, serversObj),
+ this.listServers(),
+ servers
+ );
+ this.storage.set(SERVERS_STORAGE_KEY, allServers);
};
- findServerById = serverId => {
- const servers = this.listServers();
- return servers[serverId];
- };
-
- createServer = server => {
- const servers = this.listServers();
- servers[server.id] = server;
- this.storage.set(SERVERS_STORAGE_KEY, servers);
- };
-
- deleteServer = server => {
- const servers = dissoc(server.id, this.listServers());
- this.storage.set(SERVERS_STORAGE_KEY, servers);
- };
+ deleteServer = server =>
+ this.storage.set(
+ SERVERS_STORAGE_KEY,
+ dissoc(server.id, this.listServers())
+ );
}
export default new ServersService(Storage);
diff --git a/src/utils/ColorGenerator.js b/src/utils/ColorGenerator.js
index d7a4f9e2..126df448 100644
--- a/src/utils/ColorGenerator.js
+++ b/src/utils/ColorGenerator.js
@@ -15,9 +15,6 @@ export class ColorGenerator {
constructor(storage) {
this.storage = storage;
this.colors = this.storage.get('colors') || {};
-
- this.getColorForKey = this.getColorForKey.bind(this);
- this.setColorForKey = this.setColorForKey.bind(this);
}
getColorForKey = key => {
diff --git a/test/servers/reducers/server.test.js b/test/servers/reducers/server.test.js
index cd3f24b1..7fa70143 100644
--- a/test/servers/reducers/server.test.js
+++ b/test/servers/reducers/server.test.js
@@ -2,8 +2,6 @@ import reduce, {
_createServer,
_deleteServer,
_listServers,
- CREATE_SERVER,
- DELETE_SERVER,
FETCH_SERVERS,
} from '../../../src/servers/reducers/server';
import * as sinon from 'sinon';
@@ -24,17 +22,6 @@ describe('serverReducer', () => {
expect(reduce({}, { type: FETCH_SERVERS, servers })).toEqual(servers)
);
- it('returns servers when action is DELETE_SERVER', () =>
- expect(reduce({}, { type: DELETE_SERVER, servers })).toEqual(servers)
- );
-
- it('adds server to list when action is CREATE_SERVER', () => {
- const server = { id: 'abc123' };
- expect(reduce({}, { type: CREATE_SERVER, server })).toEqual({
- [server.id]: server
- })
- });
-
it('returns default when action is unknown', () =>
expect(reduce({}, { type: 'unknown' })).toEqual({})
);