mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-05-31 01:26:16 +00:00
Updated styles in javascript to fulfill adidas rules
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
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 PropTypes from 'prop-types';
|
||||
import { resetSelectedServer } from './reducers/selectedServer';
|
||||
import { createServer } from './reducers/server';
|
||||
import './CreateServer.scss';
|
||||
import ImportServersBtn from './helpers/ImportServersBtn';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const SHOW_IMPORT_MSG_TIME = 4000;
|
||||
const propTypes = {
|
||||
createServer: PropTypes.func,
|
||||
history: PropTypes.shape({
|
||||
@@ -16,7 +17,7 @@ const propTypes = {
|
||||
resetSelectedServer: PropTypes.func,
|
||||
};
|
||||
|
||||
export class CreateServer extends React.Component {
|
||||
export class CreateServerComponent extends React.Component {
|
||||
state = {
|
||||
name: '',
|
||||
url: '',
|
||||
@@ -24,7 +25,7 @@ export class CreateServer extends React.Component {
|
||||
serversImported: false,
|
||||
};
|
||||
|
||||
submit = e => {
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { createServer, history: { push } } = this.props;
|
||||
@@ -34,7 +35,7 @@ export class CreateServer extends React.Component {
|
||||
)(this.state);
|
||||
|
||||
createServer(server);
|
||||
push(`/server/${server.id}/list-short-urls/1`)
|
||||
push(`/server/${server.id}/list-short-urls/1`);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -42,7 +43,7 @@ export class CreateServer extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const renderInputGroup = (id, placeholder, type = 'text') =>
|
||||
const renderInputGroup = (id, placeholder, type = 'text') => (
|
||||
<div className="form-group row">
|
||||
<label htmlFor={id} className="col-lg-1 col-md-2 col-form-label create-server__label">
|
||||
{placeholder}:
|
||||
@@ -54,24 +55,27 @@ export class CreateServer extends React.Component {
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
value={this.state[id]}
|
||||
onChange={e => this.setState({ [id]: e.target.value })}
|
||||
required
|
||||
onChange={(e) => this.setState({ [id]: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="create-server">
|
||||
<form onSubmit={this.submit}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{renderInputGroup('name', 'Name')}
|
||||
{renderInputGroup('url', 'URL', 'url')}
|
||||
{renderInputGroup('apiKey', 'API key')}
|
||||
|
||||
<div className="text-right">
|
||||
<ImportServersBtn onImport={() => {
|
||||
this.setState({ serversImported: true });
|
||||
setTimeout(() => this.setState({ serversImported: false }), 4000);
|
||||
}} />
|
||||
<ImportServersBtn
|
||||
onImport={() => {
|
||||
this.setState({ serversImported: true });
|
||||
setTimeout(() => this.setState({ serversImported: false }), SHOW_IMPORT_MSG_TIME);
|
||||
}}
|
||||
/>
|
||||
<button className="btn btn-outline-primary">Create server</button>
|
||||
</div>
|
||||
|
||||
@@ -90,9 +94,11 @@ export class CreateServer extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
CreateServer.propTypes = propTypes;
|
||||
CreateServerComponent.propTypes = propTypes;
|
||||
|
||||
export default connect(
|
||||
pick(['selectedServer']),
|
||||
{createServer, resetSelectedServer }
|
||||
)(CreateServer);
|
||||
const CreateServer = connect(
|
||||
pick([ 'selectedServer' ]),
|
||||
{ createServer, resetSelectedServer }
|
||||
)(CreateServerComponent);
|
||||
|
||||
export default CreateServer;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import deleteIcon from '@fortawesome/fontawesome-free-solid/faMinusCircle';
|
||||
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import DeleteServerModal from './DeleteServerModal';
|
||||
import { serverType } from './prop-types';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const propTypes = {
|
||||
server: serverType,
|
||||
@@ -20,8 +20,8 @@ export default class DeleteServerButton extends React.Component {
|
||||
<React.Fragment>
|
||||
<span
|
||||
className={className}
|
||||
onClick={() => this.setState({ isModalOpen: true })}
|
||||
key="deleteServerBtn"
|
||||
onClick={() => this.setState({ isModalOpen: true })}
|
||||
>
|
||||
<FontAwesomeIcon icon={deleteIcon} />
|
||||
<span className="aside-menu__item-text">Delete this server</span>
|
||||
@@ -29,7 +29,7 @@ export default class DeleteServerButton extends React.Component {
|
||||
|
||||
<DeleteServerModal
|
||||
isOpen={this.state.isModalOpen}
|
||||
toggle={() => this.setState({ isModalOpen: !this.state.isModalOpen })}
|
||||
toggle={() => this.setState(({ isModalOpen }) => ({ isModalOpen: !isModalOpen }))}
|
||||
server={server}
|
||||
key="deleteServerModal"
|
||||
/>
|
||||
|
||||
@@ -11,9 +11,13 @@ const propTypes = {
|
||||
toggle: PropTypes.func.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
server: serverType,
|
||||
deleteServer: PropTypes.func,
|
||||
history: PropTypes.shape({
|
||||
push: PropTypes.func,
|
||||
}),
|
||||
};
|
||||
|
||||
export const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }) => {
|
||||
export const DeleteServerModalComponent = ({ server, toggle, isOpen, deleteServer, history }) => {
|
||||
const closeModal = () => {
|
||||
deleteServer(server);
|
||||
toggle();
|
||||
@@ -38,9 +42,11 @@ export const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, histor
|
||||
);
|
||||
};
|
||||
|
||||
DeleteServerModal.propTypes = propTypes;
|
||||
DeleteServerModalComponent.propTypes = propTypes;
|
||||
|
||||
export default compose(
|
||||
const DeleteServerModal = compose(
|
||||
withRouter,
|
||||
connect(null, { deleteServer })
|
||||
)(DeleteServerModal);
|
||||
)(DeleteServerModalComponent);
|
||||
|
||||
export default DeleteServerModal;
|
||||
|
||||
@@ -3,21 +3,31 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap';
|
||||
|
||||
import { listServers } from './reducers/server';
|
||||
import PropTypes from 'prop-types';
|
||||
import { selectServer } from '../servers/reducers/selectedServer';
|
||||
import serversExporter from '../servers/services/ServersExporter';
|
||||
import { listServers } from './reducers/server';
|
||||
import { serverType } from './prop-types';
|
||||
|
||||
const defaultProps = {
|
||||
serversExporter,
|
||||
};
|
||||
const propTypes = {
|
||||
servers: PropTypes.object,
|
||||
serversExporter: PropTypes.shape({
|
||||
exportServers: PropTypes.func,
|
||||
}),
|
||||
selectedServer: serverType,
|
||||
selectServer: PropTypes.func,
|
||||
listServers: PropTypes.func,
|
||||
};
|
||||
|
||||
export class ServersDropdown extends React.Component {
|
||||
export class ServersDropdownComponent extends React.Component {
|
||||
renderServers = () => {
|
||||
const { servers, selectedServer, selectServer, serversExporter } = this.props;
|
||||
|
||||
if (isEmpty(servers)) {
|
||||
return <DropdownItem disabled><i>Add a server first...</i></DropdownItem>
|
||||
return <DropdownItem disabled><i>Add a server first...</i></DropdownItem>;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -28,15 +38,17 @@ export class ServersDropdown extends React.Component {
|
||||
tag={Link}
|
||||
to={`/server/${id}/list-short-urls/1`}
|
||||
active={selectedServer && selectedServer.id === id}
|
||||
onClick={() => selectServer(id)} // FIXME This should be implicit
|
||||
|
||||
// FIXME This should be implicit
|
||||
onClick={() => selectServer(id)}
|
||||
>
|
||||
{name}
|
||||
</DropdownItem>
|
||||
))}
|
||||
<DropdownItem divider />
|
||||
<DropdownItem
|
||||
onClick={serversExporter.exportServers}
|
||||
className="servers-dropdown__export-item"
|
||||
onClick={() => serversExporter.exportServers()}
|
||||
>
|
||||
Export servers
|
||||
</DropdownItem>
|
||||
@@ -58,9 +70,12 @@ export class ServersDropdown extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ServersDropdown.defaultProps = defaultProps;
|
||||
ServersDropdownComponent.defaultProps = defaultProps;
|
||||
ServersDropdownComponent.propTypes = propTypes;
|
||||
|
||||
export default connect(
|
||||
pick(['servers', 'selectedServer']),
|
||||
const ServersDropdown = connect(
|
||||
pick([ 'servers', 'selectedServer' ]),
|
||||
{ listServers, selectServer }
|
||||
)(ServersDropdown);
|
||||
)(ServersDropdownComponent);
|
||||
|
||||
export default ServersDropdown;
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import React from 'react';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { UncontrolledTooltip } from 'reactstrap';
|
||||
import serversImporter, { serversImporterType } from '../services/ServersImporter';
|
||||
import { createServers } from '../reducers/server';
|
||||
import { assoc } from 'ramda';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import PropTypes from 'prop-types';
|
||||
import { createServers } from '../reducers/server';
|
||||
import serversImporter, { serversImporterType } from '../services/ServersImporter';
|
||||
|
||||
const defaultProps = {
|
||||
serversImporter,
|
||||
onImport: () => {},
|
||||
onImport: () => ({}),
|
||||
};
|
||||
const propTypes = {
|
||||
onImport: PropTypes.func,
|
||||
serversImporter: serversImporterType,
|
||||
createServers: PropTypes.func,
|
||||
fileRef: PropTypes.oneOfType([PropTypes.object, PropTypes.node]),
|
||||
fileRef: PropTypes.oneOfType([ PropTypes.object, PropTypes.node ]),
|
||||
};
|
||||
|
||||
export class ImportServersBtn extends React.Component {
|
||||
export class ImportServersBtnComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.fileRef = props.fileRef || React.createRef();
|
||||
@@ -26,9 +26,9 @@ export class ImportServersBtn extends React.Component {
|
||||
|
||||
render() {
|
||||
const { serversImporter: { importServersFromFile }, onImport, createServers } = this.props;
|
||||
const onChange = e =>
|
||||
const onChange = (e) =>
|
||||
importServersFromFile(e.target.files[0])
|
||||
.then(servers => servers.map(server => assoc('id', uuid(), server)))
|
||||
.then((servers) => servers.map((server) => assoc('id', uuid(), server)))
|
||||
.then(createServers)
|
||||
.then(onImport);
|
||||
|
||||
@@ -37,28 +37,30 @@ export class ImportServersBtn extends React.Component {
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-secondary mr-2"
|
||||
onClick={() => this.fileRef.current.click()}
|
||||
id="importBtn"
|
||||
onClick={() => this.fileRef.current.click()}
|
||||
>
|
||||
Import from file
|
||||
</button>
|
||||
<UncontrolledTooltip placement="top" target="importBtn">
|
||||
You can create servers by importing a CSV file with columns "name", "apiKey" and "url"
|
||||
You can create servers by importing a CSV file with columns <b>name</b>, <b>apiKey</b> and <b>url</b>
|
||||
</UncontrolledTooltip>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
onChange={onChange}
|
||||
accept="text/csv"
|
||||
className="create-server__csv-select"
|
||||
ref={this.fileRef}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ImportServersBtn.defaultProps = defaultProps;
|
||||
ImportServersBtn.propTypes = propTypes;
|
||||
ImportServersBtnComponent.defaultProps = defaultProps;
|
||||
ImportServersBtnComponent.propTypes = propTypes;
|
||||
|
||||
export default connect(null, { createServers })(ImportServersBtn);
|
||||
const ImportServersBtn = connect(null, { createServers })(ImportServersBtnComponent);
|
||||
|
||||
export default ImportServersBtn;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import ShlinkApiClient from '../../api/ShlinkApiClient';
|
||||
import serversService from '../../servers/services/ServersService';
|
||||
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams'
|
||||
import { curry } from 'ramda';
|
||||
|
||||
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
||||
export const RESET_SELECTED_SERVER = 'shlink/selectedServer/RESET_SELECTED_SERVER';
|
||||
import shlinkApiClient from '../../api/ShlinkApiClient';
|
||||
import serversService from '../../servers/services/ServersService';
|
||||
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams';
|
||||
|
||||
const defaultState = null;
|
||||
|
||||
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
||||
|
||||
export const RESET_SELECTED_SERVER = 'shlink/selectedServer/RESET_SELECTED_SERVER';
|
||||
|
||||
export default function reducer(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case SELECT_SERVER:
|
||||
@@ -21,15 +22,17 @@ export default function reducer(state = defaultState, action) {
|
||||
|
||||
export const resetSelectedServer = () => ({ type: RESET_SELECTED_SERVER });
|
||||
|
||||
export const _selectServer = (ShlinkApiClient, serversService, serverId) => dispatch => {
|
||||
export const _selectServer = (shlinkApiClient, serversService, serverId) => (dispatch) => {
|
||||
dispatch(resetShortUrlParams());
|
||||
|
||||
const selectedServer = serversService.findServerById(serverId);
|
||||
ShlinkApiClient.setConfig(selectedServer);
|
||||
|
||||
shlinkApiClient.setConfig(selectedServer);
|
||||
|
||||
dispatch({
|
||||
type: SELECT_SERVER,
|
||||
selectedServer
|
||||
})
|
||||
selectedServer,
|
||||
});
|
||||
};
|
||||
export const selectServer = curry(_selectServer)(ShlinkApiClient, serversService);
|
||||
|
||||
export const selectServer = curry(_selectServer)(shlinkApiClient, serversService);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import serversService from '../services/ServersService';
|
||||
import { curry } from 'ramda';
|
||||
import serversService from '../services/ServersService';
|
||||
|
||||
export const FETCH_SERVERS = 'shlink/servers/FETCH_SERVERS';
|
||||
|
||||
@@ -12,26 +12,33 @@ export default function reducer(state = {}, action) {
|
||||
}
|
||||
}
|
||||
|
||||
export const _listServers = serversService => ({
|
||||
export const _listServers = (serversService) => ({
|
||||
type: FETCH_SERVERS,
|
||||
servers: serversService.listServers(),
|
||||
});
|
||||
|
||||
export const listServers = () => _listServers(serversService);
|
||||
|
||||
export const _createServer = (serversService, server) => {
|
||||
serversService.createServer(server);
|
||||
|
||||
return _listServers(serversService);
|
||||
};
|
||||
|
||||
export const createServer = curry(_createServer)(serversService);
|
||||
|
||||
export const _deleteServer = (serversService, server) => {
|
||||
serversService.deleteServer(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);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import serversService from './ServersService';
|
||||
import { dissoc, head, keys, values } from 'ramda';
|
||||
import csvjson from 'csvjson';
|
||||
import serversService from './ServersService';
|
||||
|
||||
const saveCsv = (window, csv) => {
|
||||
const { navigator, document } = window;
|
||||
const filename = 'shlink-servers.csv';
|
||||
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||
const blob = new Blob([ csv ], { type: 'text/csv;charset=utf-8;' });
|
||||
|
||||
// IE10 and IE11
|
||||
if (navigator.msSaveBlob) {
|
||||
navigator.msSaveBlob(blob, filename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Modern browsers
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
@@ -36,15 +38,18 @@ export class ServersExporter {
|
||||
|
||||
try {
|
||||
const csv = this.csvjson.toCSV(servers, {
|
||||
headers: keys(head(servers)).join(',')
|
||||
headers: keys(head(servers)).join(','),
|
||||
});
|
||||
|
||||
saveCsv(this.window, csv);
|
||||
} catch (e) {
|
||||
// FIXME Handle error
|
||||
/* eslint no-console: "off" */
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const serverExporter = new ServersExporter(serversService, global.window, csvjson);
|
||||
|
||||
export default serverExporter;
|
||||
|
||||
@@ -16,8 +16,9 @@ export class ServersImporter {
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
return new Promise(resolve => {
|
||||
reader.addEventListener('loadend', e => {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
reader.addEventListener('loadend', (e) => {
|
||||
const content = e.target.result;
|
||||
const servers = this.csvjson.toObject(content);
|
||||
|
||||
@@ -29,4 +30,5 @@ export class ServersImporter {
|
||||
}
|
||||
|
||||
const serversImporter = new ServersImporter(csvjson);
|
||||
|
||||
export default serversImporter;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Storage from '../../utils/Storage';
|
||||
import { assoc, dissoc, reduce } from 'ramda';
|
||||
import storage from '../../utils/Storage';
|
||||
|
||||
const SERVERS_STORAGE_KEY = 'servers';
|
||||
|
||||
@@ -10,25 +10,27 @@ export class ServersService {
|
||||
|
||||
listServers = () => this.storage.get(SERVERS_STORAGE_KEY) || {};
|
||||
|
||||
findServerById = serverId => this.listServers()[serverId];
|
||||
findServerById = (serverId) => this.listServers()[serverId];
|
||||
|
||||
createServer = server => this.createServers([server]);
|
||||
createServer = (server) => this.createServers([ server ]);
|
||||
|
||||
createServers = servers => {
|
||||
createServers = (servers) => {
|
||||
const allServers = reduce(
|
||||
(serversObj, server) => assoc(server.id, server, serversObj),
|
||||
this.listServers(),
|
||||
servers
|
||||
);
|
||||
|
||||
this.storage.set(SERVERS_STORAGE_KEY, allServers);
|
||||
};
|
||||
|
||||
deleteServer = server =>
|
||||
deleteServer = (server) =>
|
||||
this.storage.set(
|
||||
SERVERS_STORAGE_KEY,
|
||||
dissoc(server.id, this.listServers())
|
||||
);
|
||||
}
|
||||
|
||||
const serversService = new ServersService(Storage);
|
||||
const serversService = new ServersService(storage);
|
||||
|
||||
export default serversService;
|
||||
|
||||
Reference in New Issue
Block a user