Merge branch 'dev/torrents' into 'master'
Torrents See merge request !32
This commit is contained in:
commit
68285f165a
@ -15,6 +15,7 @@
|
|||||||
"jwt-decode": "^2.1.0",
|
"jwt-decode": "^2.1.0",
|
||||||
"react": "^15.3.2",
|
"react": "^15.3.2",
|
||||||
"react-bootstrap": "^0.30.6",
|
"react-bootstrap": "^0.30.6",
|
||||||
|
"react-bootstrap-sweetalert": "^3.0.0",
|
||||||
"react-dom": "^15.3.2",
|
"react-dom": "^15.3.2",
|
||||||
"react-loading": "^0.0.9",
|
"react-loading": "^0.0.9",
|
||||||
"react-redux": "^4.4.6",
|
"react-redux": "^4.4.6",
|
||||||
|
@ -87,7 +87,7 @@ func main() {
|
|||||||
env.Handle("/shows/refresh", extmedias.RefreshShows).WithRole(users.UserRole).Methods("POST")
|
env.Handle("/shows/refresh", extmedias.RefreshShows).WithRole(users.UserRole).Methods("POST")
|
||||||
env.Handle("/shows/explore", extmedias.ExploreShows).WithRole(users.UserRole).Methods("GET")
|
env.Handle("/shows/explore", extmedias.ExploreShows).WithRole(users.UserRole).Methods("GET")
|
||||||
env.Handle("/shows/search", shows.SearchShow).WithRole(users.UserRole).Methods("POST")
|
env.Handle("/shows/search", shows.SearchShow).WithRole(users.UserRole).Methods("POST")
|
||||||
env.Handle("/download", torrents.DownloadHandler).WithRole(users.UserRole).Methods("POST")
|
env.Handle("/torrents", torrents.DownloadHandler).WithRole(users.UserRole).Methods("POST")
|
||||||
|
|
||||||
n := negroni.Classic()
|
n := negroni.Classic()
|
||||||
n.Use(authMiddleware)
|
n.Use(authMiddleware)
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
import { configureAxios, request } from '../requests'
|
import { configureAxios, request } from '../requests'
|
||||||
|
|
||||||
// ======================
|
// ======================
|
||||||
// Errors
|
// Alerts
|
||||||
// ======================
|
// ======================
|
||||||
|
|
||||||
export function addError(message) {
|
export function addAlertError(message) {
|
||||||
return {
|
return {
|
||||||
type: 'ADD_ERROR',
|
type: 'ADD_ALERT_ERROR',
|
||||||
payload: {
|
payload: {
|
||||||
message,
|
message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dismissError() {
|
export function addAlertOk(message) {
|
||||||
return {
|
return {
|
||||||
type: 'DISMISS_ERROR',
|
type: 'ADD_ALERT_OK',
|
||||||
|
payload: {
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dismissAlert() {
|
||||||
|
return {
|
||||||
|
type: 'DISMISS_ALERT',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +53,15 @@ export function loginUser(username, password) {
|
|||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateUser(config) {
|
export function updateUser(config) {
|
||||||
return request(
|
return request(
|
||||||
'USER_UPDATE',
|
'USER_UPDATE',
|
||||||
configureAxios().post('/users/edit', config)
|
configureAxios().post('/users/edit', config),
|
||||||
|
"User updated",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,3 +142,17 @@ export function selectShow(imdbId) {
|
|||||||
imdbId
|
imdbId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======================
|
||||||
|
// AddTorrent
|
||||||
|
// ======================
|
||||||
|
|
||||||
|
export function addTorrent(url) {
|
||||||
|
return request(
|
||||||
|
'ADD_TORRENT',
|
||||||
|
configureAxios().post('/torrents', {
|
||||||
|
url: url,
|
||||||
|
}),
|
||||||
|
"Torrent added",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -27,7 +27,7 @@ import store, { history } from './store'
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import NavBar from './components/navbar'
|
import NavBar from './components/navbar'
|
||||||
import Error from './components/errors'
|
import Alert from './components/alerts/alert'
|
||||||
import MovieList from './components/movies/list'
|
import MovieList from './components/movies/list'
|
||||||
import ShowList from './components/shows/list'
|
import ShowList from './components/shows/list'
|
||||||
import ShowDetails from './components/shows/details'
|
import ShowDetails from './components/shows/details'
|
||||||
@ -43,7 +43,7 @@ class Main extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<NavBar {...this.props}/>
|
<NavBar {...this.props}/>
|
||||||
<Error {...this.props}/>
|
<Alert {...this.props}/>
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
{React.cloneElement(this.props.children, this.props)}
|
{React.cloneElement(this.props.children, this.props)}
|
||||||
</div>
|
</div>
|
||||||
@ -57,7 +57,7 @@ function mapStateToProps(state) {
|
|||||||
movieStore: state.movieStore,
|
movieStore: state.movieStore,
|
||||||
showStore: state.showStore,
|
showStore: state.showStore,
|
||||||
userStore: state.userStore,
|
userStore: state.userStore,
|
||||||
errors: state.errors,
|
alerts: state.alerts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
src/public/js/components/alerts/alert.js
Normal file
17
src/public/js/components/alerts/alert.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import SweetAlert from 'react-bootstrap-sweetalert';
|
||||||
|
|
||||||
|
export default function Alert(props) {
|
||||||
|
if (!props.alerts.show) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SweetAlert
|
||||||
|
type={props.alerts.type}
|
||||||
|
onConfirm={props.dismissAlert}
|
||||||
|
title={props.alerts.message}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
export default function Error(props) {
|
|
||||||
if (!props.errors.message) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-md-6 col-md-offset-3 col-xs-12">
|
|
||||||
<div className="alert alert-warning">
|
|
||||||
<button type="button" className="close" onClick={props.dismissError}>
|
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
<p>{props.errors.message}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -13,11 +13,27 @@ export default function ListPosters(props) {
|
|||||||
extract: (el) => el.title
|
extract: (el) => el.title
|
||||||
});
|
});
|
||||||
elmts = filtered.map((el) => el.original);
|
elmts = filtered.map((el) => el.original);
|
||||||
|
} else {
|
||||||
|
// Get the page number if defined
|
||||||
|
let page = 1;
|
||||||
|
let perPage = props.perPage;
|
||||||
|
if (props.params && props.params.page) {
|
||||||
|
page = parseInt(props.params.page);
|
||||||
|
}
|
||||||
|
|
||||||
|
let from = 0;
|
||||||
|
let to = perPage - 1;
|
||||||
|
if (page > 1) {
|
||||||
|
from = ((page - 1) * perPage) - 1;
|
||||||
|
to = from + perPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit the number of results
|
// Limit the number of results
|
||||||
if (elmts.length > props.perPage) {
|
if ((from + perPage) > elmts.length) {
|
||||||
elmts = elmts.slice(0, props.perPage);
|
elmts = elmts.slice(from);
|
||||||
|
} else {
|
||||||
|
elmts = elmts.slice(from, to);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import TorrentsButton from './torrents'
|
||||||
import ListPosters from '../list/posters'
|
import ListPosters from '../list/posters'
|
||||||
import ListDetails from '../list/details'
|
import ListDetails from '../list/details'
|
||||||
import Loader from '../loader/loader'
|
import Loader from '../loader/loader'
|
||||||
@ -37,13 +38,14 @@ class MovieButtons extends React.Component {
|
|||||||
<i className="fa fa-download"></i> Download
|
<i className="fa fa-download"></i> Download
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
{this.props.movie.torrents && this.props.movie.torrents.map(function(torrent, index) {
|
|
||||||
return (
|
{this.props.movie.torrents &&
|
||||||
<a key={torrent.url} type="button" className="btn btn-primary btn-sm" href={torrent.url}>
|
<TorrentsButton
|
||||||
<i className="fa fa-download"></i> {torrent.quality} Torrent
|
torrents={this.props.movie.torrents}
|
||||||
</a>
|
addTorrent={this.props.addTorrent}
|
||||||
)}
|
/>
|
||||||
)}
|
}
|
||||||
|
|
||||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||||
<i className="fa fa-external-link"></i> IMDB
|
<i className="fa fa-external-link"></i> IMDB
|
||||||
</a>
|
</a>
|
||||||
@ -98,6 +100,7 @@ export default class MovieList extends React.Component {
|
|||||||
filter={this.props.movieStore.filter}
|
filter={this.props.movieStore.filter}
|
||||||
perPage={this.props.movieStore.perPage}
|
perPage={this.props.movieStore.perPage}
|
||||||
onClick={this.props.selectMovie}
|
onClick={this.props.selectMovie}
|
||||||
|
params={this.props.params}
|
||||||
/>
|
/>
|
||||||
{selectedMovie &&
|
{selectedMovie &&
|
||||||
<ListDetails data={selectedMovie}>
|
<ListDetails data={selectedMovie}>
|
||||||
@ -105,6 +108,7 @@ export default class MovieList extends React.Component {
|
|||||||
movie={selectedMovie}
|
movie={selectedMovie}
|
||||||
fetching={this.props.movieStore.fetchingDetails}
|
fetching={this.props.movieStore.fetchingDetails}
|
||||||
getMovieDetails={this.props.getMovieDetails}
|
getMovieDetails={this.props.getMovieDetails}
|
||||||
|
addTorrent={this.props.addTorrent}
|
||||||
/>
|
/>
|
||||||
</ListDetails>
|
</ListDetails>
|
||||||
}
|
}
|
||||||
|
81
src/public/js/components/movies/torrents.js
Normal file
81
src/public/js/components/movies/torrents.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { DropdownButton, MenuItem } from 'react-bootstrap'
|
||||||
|
|
||||||
|
export default class TorrentsButton extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
handleClick(e, url) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.addTorrent(url);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const entries = buildMenuItems(this.props.torrents);
|
||||||
|
return (
|
||||||
|
<DropdownButton className="btn btn-default btn-sm" title="Torrents" id="download-torrents-button" dropup>
|
||||||
|
{entries.map(function(e, index) {
|
||||||
|
switch (e.type) {
|
||||||
|
case 'header':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} className="text-warning" header>
|
||||||
|
{e.value}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
case 'divider':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} divider></MenuItem>
|
||||||
|
);
|
||||||
|
case 'entry':
|
||||||
|
return (
|
||||||
|
<MenuItem key={index} href={e.url} onClick={(event) => this.handleClick(event, e.url)}>
|
||||||
|
{e.quality}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, this)}
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMenuItems(torrents) {
|
||||||
|
// Organise by source
|
||||||
|
let sources = {}
|
||||||
|
for (let torrent of torrents) {
|
||||||
|
if (!sources[torrent.source]) {
|
||||||
|
sources[torrent.source] = [];
|
||||||
|
}
|
||||||
|
sources[torrent.source].push(torrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the array of entries
|
||||||
|
let entries = [];
|
||||||
|
let sourceNames = Object.keys(sources);
|
||||||
|
let dividerCount = sourceNames.length - 1;
|
||||||
|
for (let source of sourceNames) {
|
||||||
|
// Push the title
|
||||||
|
entries.push({
|
||||||
|
type: "header",
|
||||||
|
value: source,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Push the torrents
|
||||||
|
for (let torrent of sources[source]) {
|
||||||
|
entries.push({
|
||||||
|
type: "entry",
|
||||||
|
quality: torrent.quality,
|
||||||
|
url: torrent.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the divider
|
||||||
|
if (dividerCount > 0) {
|
||||||
|
dividerCount--;
|
||||||
|
entries.push({ type: "divider" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
@ -13,7 +13,10 @@ export default class ShowDetails extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="row" id="container">
|
<div className="row" id="container">
|
||||||
<Header data={this.props.showStore.show} />
|
<Header data={this.props.showStore.show} />
|
||||||
<SeasonsList data={this.props.showStore.show} />
|
<SeasonsList
|
||||||
|
data={this.props.showStore.show}
|
||||||
|
addTorrent={this.props.addTorrent}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -35,7 +38,8 @@ function Header(props){
|
|||||||
function HeaderThumbnail(props){
|
function HeaderThumbnail(props){
|
||||||
return (
|
return (
|
||||||
<div className="col-xs-12 col-sm-2 text-center">
|
<div className="col-xs-12 col-sm-2 text-center">
|
||||||
<img src={props.data.poster_url} className="show-thumbnail thumbnail-selected img-thumbnail img-responsive"/>
|
<img src={props.data.poster_url}
|
||||||
|
className="show-thumbnail thumbnail-selected img-thumbnail img-responsive"/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -70,7 +74,10 @@ function SeasonsList(props){
|
|||||||
{props.data.seasons.length > 0 && props.data.seasons.map(function(season, index) {
|
{props.data.seasons.length > 0 && props.data.seasons.map(function(season, index) {
|
||||||
return (
|
return (
|
||||||
<div className="col-xs-12 col-sm-10 col-sm-offset-1 col-md-10 col-md-offset-1" key={index}>
|
<div className="col-xs-12 col-sm-10 col-sm-offset-1 col-md-10 col-md-offset-1" key={index}>
|
||||||
<Season data={season} />
|
<Season
|
||||||
|
data={season}
|
||||||
|
addTorrent={props.addTorrent}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@ -109,9 +116,13 @@ class Season extends React.Component {
|
|||||||
{this.props.data.episodes.map(function(episode, index) {
|
{this.props.data.episodes.map(function(episode, index) {
|
||||||
let key = `${episode.season}-${episode.episode}`;
|
let key = `${episode.season}-${episode.episode}`;
|
||||||
return (
|
return (
|
||||||
<Episode key={key} data={episode} />
|
<Episode
|
||||||
|
key={key}
|
||||||
|
data={episode}
|
||||||
|
addTorrent={this.props.addTorrent}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
})}
|
}, this)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
}
|
}
|
||||||
@ -130,7 +141,11 @@ function Episode(props) {
|
|||||||
{props.data.torrents && props.data.torrents.map(function(torrent, index) {
|
{props.data.torrents && props.data.torrents.map(function(torrent, index) {
|
||||||
let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`;
|
let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`;
|
||||||
return (
|
return (
|
||||||
<Torrent data={torrent} key={key} />
|
<Torrent
|
||||||
|
data={torrent}
|
||||||
|
key={key}
|
||||||
|
addTorrent={props.addTorrent}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
@ -139,12 +154,25 @@ function Episode(props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Torrent(props) {
|
class Torrent extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
handleClick(e, url) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.addTorrent(url);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
<span className="episode-button">
|
<span className="episode-button">
|
||||||
<a type="button" className="btn btn-primary btn-xs" href={props.data.url}>
|
<a type="button"
|
||||||
<i className="fa fa-download"></i> {props.data.quality}
|
className="btn btn-primary btn-xs"
|
||||||
|
onClick={(e) => this.handleClick(e, this.props.data.url)}
|
||||||
|
href={this.props.data.url} >
|
||||||
|
<i className="fa fa-download"></i> {this.props.data.quality}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
30
src/public/js/reducers/alerts.js
Normal file
30
src/public/js/reducers/alerts.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
const defaultState = {
|
||||||
|
show: false,
|
||||||
|
message: "",
|
||||||
|
type: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Alert(state = defaultState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'ADD_ALERT_ERROR':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
message: action.payload.message,
|
||||||
|
show: true,
|
||||||
|
type: "error",
|
||||||
|
})
|
||||||
|
case 'ADD_ALERT_OK':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
message: action.payload.message,
|
||||||
|
show: true,
|
||||||
|
type: "success",
|
||||||
|
})
|
||||||
|
case 'DISMISS_ALERT':
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
message: "",
|
||||||
|
show: false,
|
||||||
|
type: "",
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
export default function error(state = {}, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'ADD_ERROR':
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
message: action.payload.message,
|
|
||||||
})
|
|
||||||
case 'DISMISS_ERROR':
|
|
||||||
return {};
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ import { routerReducer } from 'react-router-redux'
|
|||||||
import movieStore from './movies'
|
import movieStore from './movies'
|
||||||
import showStore from './shows'
|
import showStore from './shows'
|
||||||
import userStore from './users'
|
import userStore from './users'
|
||||||
import errors from './errors'
|
import alerts from './alerts'
|
||||||
|
|
||||||
// Use combine form form react-redux-form, it's a thin wrapper arround the
|
// Use combine form form react-redux-form, it's a thin wrapper arround the
|
||||||
// default combinedReducers provided with React. It allows the forms to be
|
// default combinedReducers provided with React. It allows the forms to be
|
||||||
@ -14,7 +14,7 @@ const rootReducer = combineForms({
|
|||||||
movieStore,
|
movieStore,
|
||||||
showStore,
|
showStore,
|
||||||
userStore,
|
userStore,
|
||||||
errors,
|
alerts,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
@ -16,7 +16,7 @@ export function configureAxios(headers = {}) {
|
|||||||
|
|
||||||
// This function takes en event prefix to dispatch evens during the life of the
|
// This function takes en event prefix to dispatch evens during the life of the
|
||||||
// request, it also take a promise (axios request)
|
// request, it also take a promise (axios request)
|
||||||
export function request(eventPrefix, promise) {
|
export function request(eventPrefix, promise, successMessage = null) {
|
||||||
// Events
|
// Events
|
||||||
const pending = `${eventPrefix}_PENDING`;
|
const pending = `${eventPrefix}_PENDING`;
|
||||||
const fulfilled = `${eventPrefix}_FULFILLED`;
|
const fulfilled = `${eventPrefix}_FULFILLED`;
|
||||||
@ -30,7 +30,7 @@ export function request(eventPrefix, promise) {
|
|||||||
if (response.data.status === 'error')
|
if (response.data.status === 'error')
|
||||||
{
|
{
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'ADD_ERROR',
|
type: 'ADD_ALERT_ERROR',
|
||||||
payload: {
|
payload: {
|
||||||
message: response.data.message,
|
message: response.data.message,
|
||||||
}
|
}
|
||||||
@ -40,6 +40,14 @@ export function request(eventPrefix, promise) {
|
|||||||
type: fulfilled,
|
type: fulfilled,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
})
|
})
|
||||||
|
if (successMessage) {
|
||||||
|
dispatch({
|
||||||
|
type: 'ADD_ALERT_OK',
|
||||||
|
payload: {
|
||||||
|
message: successMessage,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// Unauthorized
|
// Unauthorized
|
||||||
@ -49,7 +57,7 @@ export function request(eventPrefix, promise) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'ADD_ERROR',
|
type: 'ADD_ALERT_ERROR',
|
||||||
payload: {
|
payload: {
|
||||||
message: error.response.data,
|
message: error.response.data,
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,19 @@ body {
|
|||||||
|
|
||||||
.list-details-buttons {
|
.list-details-buttons {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 1%;
|
bottom: 0px;
|
||||||
right: 1%;
|
padding-top: 5px;
|
||||||
|
background-color: @body-bg;
|
||||||
|
|
||||||
|
@media (min-width: @screen-xs-max) {
|
||||||
|
right: 0px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.btn,
|
||||||
|
div.btn-group {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-filter {
|
.list-filter {
|
||||||
@ -42,3 +53,7 @@ body {
|
|||||||
.navbar {
|
.navbar {
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.sweet-alert > h2 {
|
||||||
|
color: @body-bg;
|
||||||
|
}
|
||||||
|
@ -3077,6 +3077,12 @@ react-bootstrap:
|
|||||||
uncontrollable "^4.0.1"
|
uncontrollable "^4.0.1"
|
||||||
warning "^3.0.0"
|
warning "^3.0.0"
|
||||||
|
|
||||||
|
react-bootstrap-sweetalert:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-bootstrap-sweetalert/-/react-bootstrap-sweetalert-3.0.0.tgz#65378a42f37845676acf98e8c43ce4a61f23306f"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
|
||||||
react-dom@^15.3.2:
|
react-dom@^15.3.2:
|
||||||
version "15.3.2"
|
version "15.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user