diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd68d57c..0de9d24d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* [#213](https://github.com/shlinkio/shlink-web-client/issues/213) The versions of both shlink-web-client and currently consumed Shlink server are now displayed in the footer.
* [#221](https://github.com/shlinkio/shlink-web-client/issues/221) Improved how servers are handled, displaying meaningful errors when a not-found or a not-reachable server is tried to be loaded.
+* [#226](https://github.com/shlinkio/shlink-web-client/issues/226) Created servers can now be edited.
#### Changed
diff --git a/package-lock.json b/package-lock.json
index 7945df60..175f0e87 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5340,7 +5340,7 @@
},
"discontinuous-range": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "resolved": "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
"dev": true
},
@@ -9346,7 +9346,7 @@
},
"is-subset": {
"version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
+ "resolved": "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz",
"integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=",
"dev": true
},
@@ -10557,13 +10557,13 @@
},
"lodash.escape": {
"version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
+ "resolved": "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz",
"integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=",
"dev": true
},
"lodash.flattendeep": {
"version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "resolved": "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
"integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
"dev": true
},
@@ -10575,7 +10575,7 @@
},
"lodash.isequal": {
"version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "resolved": "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
@@ -13817,7 +13817,7 @@
},
"railroad-diagrams": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "resolved": "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
"dev": true
},
@@ -14977,7 +14977,7 @@
},
"rst-selector-parser": {
"version": "2.2.3",
- "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
+ "resolved": "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
"integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
"dev": true,
"requires": {
diff --git a/src/App.js b/src/App.js
index ba40b7e8..4cecebff 100644
--- a/src/App.js
+++ b/src/App.js
@@ -3,14 +3,15 @@ import { Route, Switch } from 'react-router-dom';
import './App.scss';
import NotFound from './common/NotFound';
-const App = (MainHeader, Home, MenuLayout, CreateServer) => () => (
+const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer) => () => (
-
+
+
diff --git a/src/common/AsideMenu.js b/src/common/AsideMenu.js
index 56e7d10c..dca91132 100644
--- a/src/common/AsideMenu.js
+++ b/src/common/AsideMenu.js
@@ -1,4 +1,9 @@
-import { faList as listIcon, faLink as createIcon, faTags as tagsIcon } from '@fortawesome/free-solid-svg-icons';
+import {
+ faList as listIcon,
+ faLink as createIcon,
+ faTags as tagsIcon,
+ faPen as editIcon,
+} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { NavLink } from 'react-router-dom';
@@ -7,8 +12,13 @@ import classNames from 'classnames';
import { serverType } from '../servers/prop-types';
import './AsideMenu.scss';
-const AsideMenuItem = ({ children, to, ...rest }) => (
-
+const AsideMenuItem = ({ children, to, className, ...rest }) => (
+
{children}
);
@@ -16,6 +26,7 @@ const AsideMenuItem = ({ children, to, ...rest }) => (
AsideMenuItem.propTypes = {
children: PropTypes.node.isRequired,
to: PropTypes.string.isRequired,
+ className: PropTypes.string,
};
const propTypes = {
@@ -48,8 +59,15 @@ const AsideMenu = (DeleteServerButton) => {
Manage tags
-
-
+
+
+ Edit this server
+
+
);
diff --git a/src/common/AsideMenu.scss b/src/common/AsideMenu.scss
index 43bb7682..46a88786 100644
--- a/src/common/AsideMenu.scss
+++ b/src/common/AsideMenu.scss
@@ -67,6 +67,9 @@ $asideMenuMobileWidth: 280px;
.aside-menu__item--danger {
color: $dangerColor;
+}
+
+.aside-menu__item--push {
margin-top: auto;
}
diff --git a/src/common/MenuLayout.js b/src/common/MenuLayout.js
index 1951a63f..ad63d427 100644
--- a/src/common/MenuLayout.js
+++ b/src/common/MenuLayout.js
@@ -6,35 +6,23 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import * as PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
-import Message from '../utils/Message';
+import { withSelectedServer } from '../servers/helpers/withSelectedServer';
import NotFound from './NotFound';
import './MenuLayout.scss';
const propTypes = {
match: PropTypes.object,
- selectServer: PropTypes.func,
location: PropTypes.object,
selectedServer: serverType,
};
const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisits, ShlinkVersions, ServerError) => {
- const MenuLayoutComp = ({ match, location, selectedServer, selectServer }) => {
+ const MenuLayoutComp = ({ match, location, selectedServer }) => {
const [ showSideBar, setShowSidebar ] = useState(false);
const { params: { serverId } } = match;
- useEffect(() => {
- selectServer(serverId);
- }, [ serverId ]);
useEffect(() => setShowSidebar(false), [ location ]);
- if (!selectedServer) {
- return ;
- }
-
- if (selectedServer.serverNotFound) {
- return ;
- }
-
if (selectedServer.serverNotReachable) {
return ;
}
@@ -91,7 +79,7 @@ const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisi
MenuLayoutComp.propTypes = propTypes;
- return MenuLayoutComp;
+ return withSelectedServer(MenuLayoutComp, ServerError);
};
export default MenuLayout;
diff --git a/src/container/index.js b/src/container/index.js
index 483c8acd..771a46e8 100644
--- a/src/container/index.js
+++ b/src/container/index.js
@@ -26,7 +26,7 @@ const connect = (propsFromState, actionServiceNames = []) =>
actionServiceNames.reduce(mapActionService, {})
);
-bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateServer');
+bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateServer', 'EditServer');
provideCommonServices(bottle, connect, withRouter);
provideShortUrlsServices(bottle, connect);
diff --git a/src/servers/CreateServer.js b/src/servers/CreateServer.js
index 42f41410..235ecd82 100644
--- a/src/servers/CreateServer.js
+++ b/src/servers/CreateServer.js
@@ -1,91 +1,56 @@
-import { assoc, dissoc, pipe } from 'ramda';
-import React from 'react';
+import React, { useEffect } from 'react';
import { v4 as uuid } from 'uuid';
import PropTypes from 'prop-types';
import './CreateServer.scss';
+import { ServerForm } from './helpers/ServerForm';
const SHOW_IMPORT_MSG_TIME = 4000;
+const propTypes = {
+ createServer: PropTypes.func,
+ history: PropTypes.shape({
+ push: PropTypes.func,
+ }),
+ resetSelectedServer: PropTypes.func,
+};
-const CreateServer = (ImportServersBtn, stateFlagTimeout) => class CreateServer extends React.Component {
- static propTypes = {
- createServer: PropTypes.func,
- history: PropTypes.shape({
- push: PropTypes.func,
- }),
- resetSelectedServer: PropTypes.func,
- };
+const CreateServer = (ImportServersBtn, useStateFlagTimeout) => {
+ const CreateServerComp = ({ createServer, history: { push }, resetSelectedServer }) => {
+ const [ serversImported, setServersImported ] = useStateFlagTimeout(false, SHOW_IMPORT_MSG_TIME);
+ const handleSubmit = (serverData) => {
+ const id = uuid();
+ const server = { id, ...serverData };
- state = {
- name: '',
- url: '',
- apiKey: '',
- serversImported: false,
- };
+ createServer(server);
+ push(`/server/${id}/list-short-urls/1`);
+ };
- handleSubmit = (e) => {
- e.preventDefault();
-
- const { createServer, history: { push } } = this.props;
- const server = pipe(
- assoc('id', uuid()),
- dissoc('serversImported')
- )(this.state);
-
- createServer(server);
- push(`/server/${server.id}/list-short-urls/1`);
- };
-
- componentDidMount() {
- this.props.resetSelectedServer();
- }
-
- render() {
- const renderInputGroup = (id, placeholder, type = 'text') => (
-
-
-
- this.setState({ [id]: e.target.value })}
- />
-
-
- );
+ useEffect(() => {
+ resetSelectedServer();
+ }, []);
return (