From b008c37a5bfcd47f477745356d7d95960c9900e9 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 16 Jun 2018 11:24:42 +0200 Subject: [PATCH] Implemented first elements of short codes list --- package.json | 2 + src/index.scss | 4 ++ src/servers/reducers/selectedServer.js | 12 +----- src/servers/services/index.js | 7 ++-- src/short-urls/ShortUrlsList.js | 50 ++++++++++++++++++++---- src/short-urls/reducers/shortUrlsList.js | 6 +-- src/utils/ColorGenerator.js | 32 +++++++++++++++ src/utils/Storage.js | 17 ++++++++ src/utils/Tag.js | 21 ++++++++++ src/utils/Tag.scss | 7 ++++ 10 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 src/utils/ColorGenerator.js create mode 100644 src/utils/Storage.js create mode 100644 src/utils/Tag.js create mode 100644 src/utils/Tag.scss diff --git a/package.json b/package.json index 91bdad98..df7f336f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "fs-extra": "3.0.1", "html-webpack-plugin": "2.29.0", "jest": "20.0.4", + "moment": "^2.22.2", "object-assign": "4.1.1", "postcss-flexbugs-fixes": "3.2.0", "postcss-loader": "2.0.8", @@ -43,6 +44,7 @@ "react": "^16.3.2", "react-dev-utils": "^5.0.1", "react-dom": "^16.3.2", + "react-moment": "^0.7.6", "react-redux": "^5.0.7", "react-router-dom": "^4.2.2", "reactstrap": "^6.0.1", diff --git a/src/index.scss b/src/index.scss index e6afb95a..ae72fb45 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,3 +1,7 @@ * { outline: none !important; } + +.nowrap { + white-space: nowrap; +} diff --git a/src/servers/reducers/selectedServer.js b/src/servers/reducers/selectedServer.js index 8b637049..7bb4e10f 100644 --- a/src/servers/reducers/selectedServer.js +++ b/src/servers/reducers/selectedServer.js @@ -1,18 +1,10 @@ -import ServersService from '../services'; -import { LOAD_SERVER } from '../../reducers/types'; +import { LIST_SHORT_URLS } from '../../reducers/types'; export default function selectedServerReducer(state = null, action) { switch (action.type) { - case LOAD_SERVER: + case LIST_SHORT_URLS: return action.selectedServer; default: return state; } } - -export const loadServer = serverId => { - return { - type: LOAD_SERVER, - selectedServer: ServersService.findServerById(serverId), - }; -}; diff --git a/src/servers/services/index.js b/src/servers/services/index.js index bfbae11e..60dd30e0 100644 --- a/src/servers/services/index.js +++ b/src/servers/services/index.js @@ -1,5 +1,4 @@ -const PREFIX = 'shlink'; -const buildPath = path => `${PREFIX}.${path}`; +import Storage from '../../utils/Storage'; export class ServersService { constructor(storage) { @@ -7,7 +6,7 @@ export class ServersService { } listServers = () => { - return JSON.parse(this.storage.getItem(buildPath('servers')) || '{}'); + return this.storage.get('servers'); }; findServerById = serverId => { @@ -16,4 +15,4 @@ export class ServersService { } } -export default new ServersService(localStorage); +export default new ServersService(Storage); diff --git a/src/short-urls/ShortUrlsList.js b/src/short-urls/ShortUrlsList.js index e4907a67..8f3e7cd9 100644 --- a/src/short-urls/ShortUrlsList.js +++ b/src/short-urls/ShortUrlsList.js @@ -1,34 +1,70 @@ import React from 'react'; +import Moment from 'react-moment'; import { connect } from 'react-redux'; +import Tag from '../utils/Tag'; import { listShortUrls } from './reducers/shortUrlsList'; import { isEmpty } from 'ramda'; export class ShortUrlsList extends React.Component { componentDidMount() { const { match } = this.props; - this.props.listShortUrls(match.params.serverId); + this.props.listShortUrls(match.params.serverId, { page: match.params.page }); } render() { return ( - + + + + + + + + + + + + + {this.renderShortUrls()} + +
Created atShort URLOriginal URLTagsVisits -
); } renderShortUrls() { - const { shortUrlsList } = this.props; + const { shortUrlsList, selectedServer } = this.props; if (isEmpty(shortUrlsList)) { return
  • Loading...
  • ; } return shortUrlsList.map(shortUrl => ( -
  • {`${shortUrl.shortCode}`}
  • + + {shortUrl.dateCreated} + + + {`${selectedServer.url}/${shortUrl.shortCode}`} + + + + {shortUrl.originalUrl} + + {ShortUrlsList.renderTags(shortUrl.tags)} + {shortUrl.visitsCount} + + )); } + + static renderTags(tags) { + if (isEmpty(tags)) { + return No tags; + } + + return tags.map(tag => ); + } } export default connect(state => ({ - shortUrlsList: state.shortUrlsList + shortUrlsList: state.shortUrlsList, + selectedServer: state.selectedServer, }), { listShortUrls })(ShortUrlsList); diff --git a/src/short-urls/reducers/shortUrlsList.js b/src/short-urls/reducers/shortUrlsList.js index f739b0b6..4e218192 100644 --- a/src/short-urls/reducers/shortUrlsList.js +++ b/src/short-urls/reducers/shortUrlsList.js @@ -11,12 +11,12 @@ export default function shortUrlsListReducer(state = [], action) { } } -export const listShortUrls = (serverId) => { +export const listShortUrls = (serverId, params = {}) => { return async dispatch => { const selectedServer = ServersService.findServerById(serverId); ShlinkApiClient.setConfig(selectedServer); - const shortUrls = await ShlinkApiClient.listShortUrls(); - dispatch({ type: LIST_SHORT_URLS, shortUrls }); + const shortUrls = await ShlinkApiClient.listShortUrls(params); + dispatch({ type: LIST_SHORT_URLS, shortUrls, selectedServer }); }; }; diff --git a/src/utils/ColorGenerator.js b/src/utils/ColorGenerator.js new file mode 100644 index 00000000..30e041a8 --- /dev/null +++ b/src/utils/ColorGenerator.js @@ -0,0 +1,32 @@ +import Storage from './Storage'; + +const buildRandomColor = () => { + const letters = '0123456789ABCDEF'; + let color = '#'; + for (let i = 0; i < 6; i++ ) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +}; + +export class ColorGenerator { + constructor(storage) { + this.storage = storage; + this.colors = this.storage.get('colors') || {}; + } + + getColorForKey = key => { + let color = this.colors[key]; + if (color) { + return color; + } + + // If a color has not been set yet, generate a random one and save it + color = buildRandomColor(); + this.colors[key] = color; + this.storage.set('colors', this.colors); + return color; + }; +} + +export default new ColorGenerator(Storage); diff --git a/src/utils/Storage.js b/src/utils/Storage.js new file mode 100644 index 00000000..ae58110a --- /dev/null +++ b/src/utils/Storage.js @@ -0,0 +1,17 @@ +const PREFIX = 'shlink'; +const buildPath = path => `${PREFIX}.${path}`; + +export class Storage { + constructor(localStorage) { + this.localStorage = localStorage; + } + + get = key => { + const item = this.localStorage.getItem(buildPath(key)); + return item ? JSON.parse(item) : undefined; + }; + + set = (key, value) => this.localStorage.setItem(buildPath(key), JSON.stringify(value)); +} + +export default new Storage(localStorage); diff --git a/src/utils/Tag.js b/src/utils/Tag.js new file mode 100644 index 00000000..4e483e1b --- /dev/null +++ b/src/utils/Tag.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import ColorGenerator from '../utils/ColorGenerator'; +import './Tag.scss'; + +export class Tag extends React.Component { + constructor(props) { + super(props); + this.colorGenerator = props.ColorGenerator; + } + + render() { + return ( + + {this.props.text} + + ); + } +} + +export default connect(state => ({ ColorGenerator }))(Tag); diff --git a/src/utils/Tag.scss b/src/utils/Tag.scss new file mode 100644 index 00000000..2c7a1f73 --- /dev/null +++ b/src/utils/Tag.scss @@ -0,0 +1,7 @@ +.tag { + color: #fff; +} + +.tag:not(:last-child) { + margin-right: 3px; +}