1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
.DS_Store
|
||||
|
||||
env
|
||||
shows.db
|
||||
|
||||
yarn.lock
|
||||
*/yarn.lock
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import Notifications, {notify} from 'react-notify-toast';
|
||||
|
||||
// StyleComponents
|
||||
import movieStyle from './styles/movieObjectStyle.jsx';
|
||||
|
||||
var MediaQuery = require('react-responsive');
|
||||
|
||||
import RequestButton from './buttons/request_button.jsx';
|
||||
|
||||
import { fetchJSON } from './http.jsx';
|
||||
|
||||
class MovieObject {
|
||||
constructor(object) {
|
||||
this.id = object.id;
|
||||
this.title = object.title;
|
||||
this.year = object.year;
|
||||
this.type = object.type;
|
||||
// Check if object.poster != undefined
|
||||
this.rating = object.rating;
|
||||
this.poster = object.poster;
|
||||
this.background = object.background;
|
||||
this.matchedInPlex = object.matchedInPlex;
|
||||
this.summary = object.summary;
|
||||
}
|
||||
|
||||
requestExisting(movie) {
|
||||
console.log('Exists', movie);
|
||||
}
|
||||
|
||||
requestMovie() {
|
||||
// fetch('http://localhost:31459/api/v1/plex/request/' + this.id + '?type='+this.type, {
|
||||
// fetch('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, {
|
||||
// method: 'POST'
|
||||
// });
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, 'POST')
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
})
|
||||
|
||||
notify.show(this.title + ' requested!', 'success', 3000);
|
||||
}
|
||||
|
||||
getElement(index) {
|
||||
const element_key = index + this.id;
|
||||
|
||||
// TODO set the poster image async by updating the dom after this is returned
|
||||
if (this.poster == null || this.poster == undefined) {
|
||||
var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png'
|
||||
} else {
|
||||
var posterPath = 'https://image.tmdb.org/t/p/w300' + this.poster;
|
||||
}
|
||||
var backgroundPath = 'https://image.tmdb.org/t/p/w640_and_h360_bestv2/' + this.background;
|
||||
|
||||
var foundInPlex;
|
||||
if (this.matchedInPlex) {
|
||||
foundInPlex = <button onClick={() => {this.requestExisting(this)}}
|
||||
style={movieStyle.requestButton}><span>Request Anyway</span></button>;
|
||||
} else {
|
||||
foundInPlex = <button onClick={() => {this.requestMovie()}}
|
||||
style={movieStyle.requestButton}><span>+ Request</span></button>;
|
||||
}
|
||||
|
||||
if (this.type === 'movie')
|
||||
var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id
|
||||
else if (this.type === 'show')
|
||||
var themoviedbLink = 'https://www.themoviedb.org/tv/' + this.id
|
||||
|
||||
|
||||
|
||||
// TODO add request button class
|
||||
return (
|
||||
<div key={element_key}>
|
||||
<Notifications />
|
||||
<div style={movieStyle.resultItem} key={this.id}>
|
||||
<MediaQuery minWidth={600}>
|
||||
<div style={movieStyle.resultPoster}>
|
||||
<img style={movieStyle.resultPosterImg} id='poster' src={posterPath}></img>
|
||||
</div>
|
||||
</MediaQuery>
|
||||
<div>
|
||||
<MediaQuery minWidth={600}>
|
||||
<span style={movieStyle.resultTitleLarge}>{this.title}</span>
|
||||
<br></br>
|
||||
<span style={movieStyle.yearRatingLarge}>Released: { this.year } | Rating: {this.rating}</span>
|
||||
<br></br>
|
||||
<span style={movieStyle.summary}>{this.summary}</span>
|
||||
<br></br>
|
||||
</MediaQuery>
|
||||
|
||||
|
||||
<MediaQuery maxWidth={600}>
|
||||
<img src={ backgroundPath } style={movieStyle.background}></img>
|
||||
<span style={movieStyle.resultTitleSmall}>{this.title}</span>
|
||||
<br></br>
|
||||
<span style={movieStyle.yearRatingSmall}>Released: {this.year} | Rating: {this.rating}</span>
|
||||
</MediaQuery>
|
||||
|
||||
|
||||
<span className='imdbLogo'>
|
||||
|
||||
</span>
|
||||
|
||||
<div style={movieStyle.buttons}>
|
||||
{foundInPlex}
|
||||
<a href={themoviedbLink}>
|
||||
<button style={movieStyle.tmdbButton}><span>Info</span></button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MediaQuery maxWidth={600}>
|
||||
<br></br>
|
||||
</MediaQuery>
|
||||
<div style={movieStyle.row}>
|
||||
<div style={movieStyle.itemDivider}></div>
|
||||
</div>
|
||||
</div>)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default MovieObject;
|
||||
137
client/app/components/SearchObject.jsx
Normal file
137
client/app/components/SearchObject.jsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
|
||||
import Notifications, {notify} from 'react-notify-toast';
|
||||
|
||||
// StyleComponents
|
||||
import searchObjectCSS from './styles/searchObject.jsx';
|
||||
import buttonsCSS from './styles/buttons.jsx';
|
||||
|
||||
var MediaQuery = require('react-responsive');
|
||||
|
||||
import { fetchJSON } from './http.jsx';
|
||||
|
||||
import Interactive from 'react-interactive';
|
||||
|
||||
|
||||
class SearchObject {
|
||||
constructor(object) {
|
||||
this.id = object.id;
|
||||
this.title = object.title;
|
||||
this.year = object.year;
|
||||
this.type = object.type;
|
||||
this.rating = object.rating;
|
||||
this.poster = object.poster;
|
||||
this.background = object.background;
|
||||
this.matchedInPlex = object.matchedInPlex;
|
||||
this.summary = object.summary;
|
||||
}
|
||||
|
||||
requestExisting(movie) {
|
||||
console.log('Exists', movie);
|
||||
}
|
||||
|
||||
requestMovie() {
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + this.id + '?type='+this.type, 'POST')
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
notify.show(this.title + ' requested!', 'success', 3000);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Request movie fetch went wrong: '+ e);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
getElement(index) {
|
||||
const element_key = index + this.id;
|
||||
|
||||
if (this.poster == null || this.poster == undefined) {
|
||||
var posterPath = 'https://openclipart.org/image/2400px/svg_to_png/211479/Simple-Image-Not-Found-Icon.png'
|
||||
} else {
|
||||
var posterPath = 'https://image.tmdb.org/t/p/w300' + this.poster;
|
||||
}
|
||||
var backgroundPath = 'https://image.tmdb.org/t/p/w640_and_h360_bestv2/' + this.background;
|
||||
|
||||
var foundInPlex;
|
||||
if (this.matchedInPlex) {
|
||||
foundInPlex = <Interactive
|
||||
as='button'
|
||||
onClick={() => {this.requestExisting(this)}}
|
||||
style={buttonsCSS.submit}
|
||||
focus={buttonsCSS.submit_hover}
|
||||
hover={buttonsCSS.submit_hover}>
|
||||
|
||||
<span>Request Anyway</span>
|
||||
</Interactive>;
|
||||
} else {
|
||||
foundInPlex = <Interactive
|
||||
as='button'
|
||||
onClick={() => {this.requestMovie()}}
|
||||
style={buttonsCSS.submit}
|
||||
focus={buttonsCSS.submit_hover}
|
||||
hover={buttonsCSS.submit_hover}>
|
||||
|
||||
<span>+ Request</span>
|
||||
</Interactive>;
|
||||
}
|
||||
|
||||
if (this.type === 'movie')
|
||||
var themoviedbLink = 'https://www.themoviedb.org/movie/' + this.id
|
||||
else if (this.type === 'show')
|
||||
var themoviedbLink = 'https://www.themoviedb.org/tv/' + this.id
|
||||
|
||||
// TODO go away from using mediaQuery, and create custom resizer
|
||||
return (
|
||||
<div key={element_key}>
|
||||
<Notifications />
|
||||
|
||||
<div style={searchObjectCSS.container} key={this.id}>
|
||||
<MediaQuery minWidth={600}>
|
||||
<div style={searchObjectCSS.posterContainer}>
|
||||
<img style={searchObjectCSS.posterImage} id='poster' src={posterPath}></img>
|
||||
</div>
|
||||
<span style={searchObjectCSS.title_large}>{this.title}</span>
|
||||
<br></br>
|
||||
<span style={searchObjectCSS.stats_large}>Released: { this.year } | Rating: {this.rating}</span>
|
||||
<br></br>
|
||||
<span style={searchObjectCSS.summary}>{this.summary}</span>
|
||||
<br></br>
|
||||
</MediaQuery>
|
||||
|
||||
<MediaQuery maxWidth={600}>
|
||||
<img src={ backgroundPath } style={searchObjectCSS.backgroundImage}></img>
|
||||
<span style={searchObjectCSS.title_small}>{this.title}</span>
|
||||
<br></br>
|
||||
<span style={searchObjectCSS.stats_small}>Released: {this.year} | Rating: {this.rating}</span>
|
||||
</MediaQuery>
|
||||
|
||||
<div style={searchObjectCSS.buttons}>
|
||||
{foundInPlex}
|
||||
|
||||
<a href={themoviedbLink}>
|
||||
<Interactive
|
||||
as='button'
|
||||
hover={buttonsCSS.info_hover}
|
||||
focus={buttonsCSS.info_hover}
|
||||
style={buttonsCSS.info}>
|
||||
|
||||
<span>Info</span>
|
||||
</Interactive>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MediaQuery maxWidth={600}>
|
||||
<br />
|
||||
</MediaQuery>
|
||||
|
||||
<div style={searchObjectCSS.dividerRow}>
|
||||
<div style={searchObjectCSS.itemDivider}></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchObject;
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import MovieObject from './MovieObject.jsx';
|
||||
|
||||
// StyleComponents
|
||||
import searchStyle from './styles/searchRequestStyle.jsx';
|
||||
import movieStyle from './styles/movieObjectStyle.jsx';
|
||||
|
||||
import URI from 'urijs';
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
|
||||
// StyleComponents
|
||||
import searchRequestCSS from './styles/searchRequestStyle.jsx';
|
||||
|
||||
import SearchObject from './SearchObject.jsx';
|
||||
import Loading from './images/loading.jsx'
|
||||
|
||||
import { fetchJSON } from './http.jsx';
|
||||
import { getCookie } from './Cookie.jsx';
|
||||
|
||||
@@ -29,7 +29,8 @@ class SearchRequest extends React.Component {
|
||||
page: 1,
|
||||
resultHeader: '',
|
||||
loadResults: false,
|
||||
scrollHasMore: true
|
||||
scrollHasMore: true,
|
||||
loading: false,
|
||||
}
|
||||
|
||||
this.allowedListTypes = ['discover', 'popular', 'nowplaying', 'upcoming']
|
||||
@@ -88,9 +89,9 @@ class SearchRequest extends React.Component {
|
||||
this.state.page = 1;
|
||||
}
|
||||
|
||||
writeLoading() {
|
||||
setLoading(value) {
|
||||
this.setState({
|
||||
responseMovieList: 'Loading...'
|
||||
loading: value
|
||||
});
|
||||
}
|
||||
|
||||
@@ -122,10 +123,7 @@ class SearchRequest extends React.Component {
|
||||
|
||||
// Here we first call api for a search with the input uri, handle any errors
|
||||
// and fill the reponseData from api into the state of reponseMovieList as movieObjects
|
||||
callSearchFillMovieList(uri) {
|
||||
// Write loading animation
|
||||
// this.writeLoading();
|
||||
|
||||
callSearchFillMovieList(uri) {
|
||||
Promise.resolve()
|
||||
.then(() => this.callURI(uri, 'GET'))
|
||||
.then(response => {
|
||||
@@ -152,7 +150,7 @@ class SearchRequest extends React.Component {
|
||||
}
|
||||
|
||||
// Convert to json and update the state of responseMovieList with the results of the api call
|
||||
// mapped as a movieObject.
|
||||
// mapped as a SearchObject.
|
||||
response.json()
|
||||
.then(responseData => {
|
||||
if (this.state.page === 1) {
|
||||
@@ -180,7 +178,6 @@ class SearchRequest extends React.Component {
|
||||
|
||||
callListFillMovieList(uri) {
|
||||
// Write loading animation
|
||||
// this.writeLoading();
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => this.callURI(uri, 'GET', undefined))
|
||||
@@ -198,7 +195,7 @@ class SearchRequest extends React.Component {
|
||||
}
|
||||
|
||||
// Convert to json and update the state of responseMovieList with the results of the api call
|
||||
// mapped as a movieObject.
|
||||
// mapped as a SearchObject.
|
||||
response.json()
|
||||
.then(responseData => {
|
||||
if (this.state.page === 1) {
|
||||
@@ -218,6 +215,7 @@ class SearchRequest extends React.Component {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('Something went wrong when fetching query.', error)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -287,10 +285,10 @@ class SearchRequest extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
// When called passes the variable to MovieObject and calls it's interal function for
|
||||
// When called passes the variable to SearchObject and calls it's interal function for
|
||||
// generating the wanted HTML
|
||||
createMovieObjects(item, index) {
|
||||
let movie = new MovieObject(item);
|
||||
let movie = new SearchObject(item);
|
||||
return movie.getElement(index);
|
||||
}
|
||||
|
||||
@@ -345,12 +343,12 @@ class SearchRequest extends React.Component {
|
||||
|
||||
movieToggle() {
|
||||
if (this.state.movieFilter)
|
||||
return <span style={searchStyle.searchFilterActive}
|
||||
return <span style={searchRequestCSS.searchFilterActive}
|
||||
className="search_category hvrUnderlineFromCenter"
|
||||
onClick={() => {this.toggleFilter('movies')}}
|
||||
id="category_active">Movies</span>
|
||||
else
|
||||
return <span style={searchStyle.searchFilterNotActive}
|
||||
return <span style={searchRequestCSS.searchFilterNotActive}
|
||||
className="search_category hvrUnderlineFromCenter"
|
||||
onClick={() => {this.toggleFilter('movies')}}
|
||||
id="category_active">Movies</span>
|
||||
@@ -358,12 +356,12 @@ class SearchRequest extends React.Component {
|
||||
|
||||
showToggle() {
|
||||
if (this.state.showFilter)
|
||||
return <span style={searchStyle.searchFilterActive}
|
||||
return <span style={searchRequestCSS.searchFilterActive}
|
||||
className="search_category hvrUnderlineFromCenter"
|
||||
onClick={() => {this.toggleFilter('shows')}}
|
||||
id="category_active">TV Shows</span>
|
||||
else
|
||||
return <span style={searchStyle.searchFilterNotActive}
|
||||
return <span style={searchRequestCSS.searchFilterNotActive}
|
||||
className="search_category hvrUnderlineFromCenter"
|
||||
onClick={() => {this.toggleFilter('shows')}}
|
||||
id="category_active">TV Shows</span>
|
||||
@@ -379,21 +377,21 @@ class SearchRequest extends React.Component {
|
||||
pageStart={0}
|
||||
loadMore={this.pageForwards.bind(this)}
|
||||
hasMore={this.state.scrollHasMore}
|
||||
loader={loader}
|
||||
loader={<Loading />}
|
||||
initialLoad={this.state.loadResults}>
|
||||
|
||||
<MediaQuery minWidth={600}>
|
||||
<div style={searchStyle.body}>
|
||||
<div className='backgroundHeader' style={searchStyle.backgroundLargeHeader}>
|
||||
<div className='pageTitle' style={searchStyle.pageTitle}>
|
||||
<span style={searchStyle.pageTitleLargeSpan}>Request new content</span>
|
||||
<div style={searchRequestCSS.body}>
|
||||
<div className='backgroundHeader' style={searchRequestCSS.backgroundLargeHeader}>
|
||||
<div className='pageTitle' style={searchRequestCSS.pageTitle}>
|
||||
<span style={searchRequestCSS.pageTitleLargeSpan}>Request new content</span>
|
||||
</div>
|
||||
|
||||
<div className='box' style={searchStyle.box}>
|
||||
<div style={searchStyle.searchLargeContainer}>
|
||||
<span style={searchStyle.searchIcon}><i className="fa fa-search"></i></span>
|
||||
<div className='box' style={searchRequestCSS.box}>
|
||||
<div style={searchRequestCSS.searchLargeContainer}>
|
||||
<span style={searchRequestCSS.searchIcon}><i className="fa fa-search"></i></span>
|
||||
|
||||
<input style={searchStyle.searchLargeBar} type="text" id="search" placeholder="Search for new content..."
|
||||
<input style={searchRequestCSS.searchLargeBar} type="text" id="search" placeholder="Search for new content..."
|
||||
onKeyPress={(event) => this._handleQueryKeyPress(event)}
|
||||
onChange={event => this.updateQueryState(event)}
|
||||
value={this.state.searchQuery}/>
|
||||
@@ -402,27 +400,27 @@ class SearchRequest extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='requestMovieList' ref='requestMovieList' style={searchStyle.requestWrapper}>
|
||||
<span style={searchStyle.resultLargeHeader}>{this.state.resultHeader}</span>
|
||||
<br></br><br></br>
|
||||
|
||||
{this.state.responseMovieList}
|
||||
<div id='requestMovieList' ref='requestMovieList' style={searchRequestCSS.requestWrapper}>
|
||||
<span style={searchRequestCSS.resultLargeHeader}>{this.state.resultHeader}</span>
|
||||
<br></br><br></br>
|
||||
|
||||
{this.state.responseMovieList}
|
||||
</div>
|
||||
</div>
|
||||
</MediaQuery>
|
||||
|
||||
<MediaQuery maxWidth={600}>
|
||||
<div style={searchStyle.body}>
|
||||
<div className='backgroundHeader' style={searchStyle.backgroundSmallHeader}>
|
||||
<div className='pageTitle' style={searchStyle.pageTitle}>
|
||||
<span style={searchStyle.pageTitleSmallSpan}>Request new content</span>
|
||||
<div style={searchRequestCSS.body}>
|
||||
<div className='backgroundHeader' style={searchRequestCSS.backgroundSmallHeader}>
|
||||
<div className='pageTitle' style={searchRequestCSS.pageTitle}>
|
||||
<span style={searchRequestCSS.pageTitleSmallSpan}>Request new content</span>
|
||||
</div>
|
||||
|
||||
<div className='box' style={searchStyle.box}>
|
||||
<div style={searchStyle.searchSmallContainer}>
|
||||
<span style={searchStyle.searchIcon}><i className="fa fa-search"></i></span>
|
||||
<div className='box' style={searchRequestCSS.box}>
|
||||
<div style={searchRequestCSS.searchSmallContainer}>
|
||||
<span style={searchRequestCSS.searchIcon}><i className="fa fa-search"></i></span>
|
||||
|
||||
<input style={searchStyle.searchSmallBar} type="text" id="search" placeholder="Search for new content..."
|
||||
<input style={searchRequestCSS.searchSmallBar} type="text" id="search" placeholder="Search for new content..."
|
||||
onKeyPress={(event) => this._handleQueryKeyPress(event)}
|
||||
onChange={event => this.updateQueryState(event)}
|
||||
value={this.state.searchQuery}/>
|
||||
@@ -431,11 +429,11 @@ class SearchRequest extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='requestMovieList' ref='requestMovieList' style={searchStyle.requestWrapper}>
|
||||
<span style={searchStyle.resultSmallHeader}>{this.state.resultHeader}</span>
|
||||
<div id='requestMovieList' ref='requestMovieList' style={searchRequestCSS.requestWrapper}>
|
||||
<span style={searchRequestCSS.resultSmallHeader}>{this.state.resultHeader}</span>
|
||||
<br></br><br></br>
|
||||
|
||||
{this.state.responseMovieList}
|
||||
{this.state.responseMovieList}
|
||||
</div>
|
||||
</div>
|
||||
</MediaQuery>
|
||||
@@ -446,4 +444,4 @@ class SearchRequest extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
export default SearchRequest;
|
||||
export default SearchRequest;
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
/*
|
||||
./app/components/App.jsx
|
||||
|
||||
<FetchData url={"https://apollo.kevinmidboe.com/api/v1/plex/playing"} />
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HashRouter as Router, Route, Switch, IndexRoute } from 'react-router-dom';
|
||||
|
||||
import LoginForm from './LoginForm/LoginForm.jsx';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -16,6 +11,8 @@ import { fetchJSON } from '../http.jsx';
|
||||
import Sidebar from './Sidebar.jsx';
|
||||
import AdminRequestInfo from './AdminRequestInfo.jsx';
|
||||
|
||||
import adminCSS from '../styles/adminComponent.jsx'
|
||||
|
||||
|
||||
class AdminComponent extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -25,6 +22,7 @@ class AdminComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetches all requested elements and updates the state with response
|
||||
componentWillMount() {
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/requests/all', 'GET')
|
||||
.then(result => {
|
||||
@@ -34,16 +32,9 @@ class AdminComponent extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
// Displays loginform if not logged in and passes response from
|
||||
// api call to sidebar and infoPanel through props
|
||||
verifyLoggedIn() {
|
||||
let adminComponentStyle = {
|
||||
sidebar: {
|
||||
float: 'left',
|
||||
},
|
||||
selectedObjectPanel: {
|
||||
float: 'left',
|
||||
}
|
||||
}
|
||||
|
||||
const logged_in = getCookie('logged_in');
|
||||
if (!logged_in) {
|
||||
return <LoginForm />
|
||||
@@ -53,20 +44,21 @@ class AdminComponent extends React.Component {
|
||||
let listItemSelected = undefined;
|
||||
|
||||
const requestParam = this.props.match.params.request;
|
||||
|
||||
if (requestParam && this.state.requested_objects !== '') {
|
||||
selectedRequest = this.state.requested_objects[requestParam]
|
||||
listItemSelected = requestParam
|
||||
listItemSelected = requestParam;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={adminComponentStyle.sidebar}>
|
||||
<div style={adminCSS.sidebar}>
|
||||
<Sidebar
|
||||
requested_objects={this.state.requested_objects}
|
||||
listItemSelected={listItemSelected}
|
||||
style={adminComponentStyle.sidebar} />
|
||||
/>
|
||||
</div>
|
||||
<div style={adminComponentStyle.selectedObjectPanel}>
|
||||
<div style={adminCSS.selectedObjectPanel}>
|
||||
<AdminRequestInfo
|
||||
selectedRequest={selectedRequest}
|
||||
listItemSelected={listItemSelected}
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { fetchJSON } from '../http.jsx';
|
||||
|
||||
import PirateSearch from './PirateSearch.jsx'
|
||||
|
||||
// Stylesheets
|
||||
import requestInfoCSS from '../styles/adminRequestInfo.jsx'
|
||||
import buttonsCSS from '../styles/buttons.jsx';
|
||||
|
||||
// Interactive button
|
||||
import Interactive from 'react-interactive';
|
||||
|
||||
class AdminRequestInfo extends Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
statusValue: '',
|
||||
}
|
||||
|
||||
this.requestInfo = '';
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
this.requestInfo = props.selectedRequest;
|
||||
this.state.statusValue = this.requestInfo.status;
|
||||
}
|
||||
|
||||
userAgent(agent) {
|
||||
@@ -19,6 +40,35 @@ class AdminRequestInfo extends Component {
|
||||
return '';
|
||||
}
|
||||
|
||||
generateStatusDropdown() {
|
||||
return (
|
||||
<select onChange={ event => this.updateRequestStatus(event) } value={this.state.statusValue}>
|
||||
<option value='requested'>Requested</option>
|
||||
<option value='downloading'>Downloading</option>
|
||||
<option value='downloaded'>Downloaded</option>
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
updateRequestStatus(event) {
|
||||
const eventValue = event.target.value;
|
||||
const itemID = this.requestInfo.id;
|
||||
|
||||
const apiData = {
|
||||
type: this.requestInfo.type,
|
||||
status: eventValue,
|
||||
}
|
||||
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/plex/request/' + itemID, 'PUT', apiData)
|
||||
.then((response) => {
|
||||
console.log('Response, updateRequestStatus: ', response)
|
||||
})
|
||||
|
||||
this.setState({
|
||||
statusValue: eventValue
|
||||
})
|
||||
}
|
||||
|
||||
requested_by_user(request_user) {
|
||||
if (request_user === 'NULL')
|
||||
return undefined
|
||||
@@ -28,57 +78,52 @@ class AdminRequestInfo extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
displayInfo() {
|
||||
let adminIndexStyle = {
|
||||
wrapper: {
|
||||
width: '100%',
|
||||
},
|
||||
headerWrapper: {
|
||||
width: '100%',
|
||||
},
|
||||
poster: {
|
||||
float: 'left',
|
||||
minHeight: '450px',
|
||||
},
|
||||
info: {
|
||||
float: 'left',
|
||||
minHeight: '450px',
|
||||
}
|
||||
}
|
||||
const request = this.props.selectedRequest;
|
||||
displayInfo() {
|
||||
const request = this.props.selectedRequest;
|
||||
|
||||
if (request) {
|
||||
return (
|
||||
<div style={adminIndexStyle.wrapper}>
|
||||
<div style={adminIndexStyle.headerWrapper}>
|
||||
<span>{request.name} </span>
|
||||
<span>{request.year}</span>
|
||||
</div>
|
||||
<div style={adminIndexStyle.poster}>
|
||||
<img src={'https://image.tmdb.org/t/p/w300/' + request.image_path} />
|
||||
</div>
|
||||
<div style={adminIndexStyle.info}>
|
||||
<span>type: {request.type}</span><br />
|
||||
<span>status: {request.status}</span><br />
|
||||
<span>ip: {request.ip}</span><br />
|
||||
<span>user_agent: {this.userAgent(request.user_agent)}</span><br />
|
||||
<span>request_date: {request.requested_date}</span><br />
|
||||
{ this.requested_by_user(request.requested_by) }
|
||||
</div>
|
||||
|
||||
<PirateSearch
|
||||
name={request.name} />
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
if (request) {
|
||||
return (
|
||||
<div style={requestInfoCSS.wrapper}>
|
||||
<div style={requestInfoCSS.headerWrapper}>
|
||||
<span>{request.name} </span>
|
||||
<span>{request.year}</span>
|
||||
</div>
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>{this.displayInfo()}</div>
|
||||
);
|
||||
}
|
||||
<div style={requestInfoCSS.info}>
|
||||
<span>type: {request.type}</span><br />
|
||||
|
||||
{this.generateStatusDropdown()}<br />
|
||||
|
||||
<span>status: {request.status}</span><br />
|
||||
<span>ip: {request.ip}</span><br />
|
||||
<span>user_agent: {this.userAgent(request.user_agent)}</span><br />
|
||||
<span>request_date: {request.requested_date}</span><br />
|
||||
{ this.requested_by_user(request.requested_by) }
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Interactive
|
||||
as='button'
|
||||
onClick={() => {}}
|
||||
style={buttonsCSS.edit}
|
||||
focus={buttonsCSS.edit_hover}
|
||||
hover={buttonsCSS.edit_hover}>
|
||||
|
||||
<span>Show info</span>
|
||||
</Interactive>
|
||||
|
||||
<PirateSearch name={request.name} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>{this.displayInfo()}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminRequestInfo;
|
||||
@@ -1,18 +1,25 @@
|
||||
import React, { Component } from 'react';
|
||||
import { fetchJSON } from '../http.jsx';
|
||||
|
||||
// Stylesheets
|
||||
import btnStylesheet from '../styles/buttons.jsx';
|
||||
|
||||
// Interactive button
|
||||
import Interactive from 'react-interactive';
|
||||
|
||||
import Loading from '../images/loading.jsx'
|
||||
|
||||
class PirateSearch extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
response: [],
|
||||
name: '',
|
||||
loading: '',
|
||||
}
|
||||
}
|
||||
|
||||
sendToDownload(torrent) {
|
||||
console.log(torrent.magnet)
|
||||
|
||||
let data = {magnet: torrent.magnet}
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/add', 'POST', data)
|
||||
.then((response) => {
|
||||
@@ -24,10 +31,15 @@ class PirateSearch extends Component {
|
||||
const query = this.props.name;
|
||||
const type = this.props.type;
|
||||
|
||||
this.setState({
|
||||
loading: <Loading />
|
||||
})
|
||||
|
||||
fetchJSON('https://apollo.kevinmidboe.com/api/v1/pirate/search?query='+query+'&type='+type, 'GET')
|
||||
.then((response) => {
|
||||
console.log(response.torrents)
|
||||
this.setState({
|
||||
loading: '',
|
||||
response: response.torrents.map((torrent, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
@@ -46,8 +58,20 @@ class PirateSearch extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<span>{this.props.name}</span>
|
||||
<button onClick={() => {this.searchTheBay(this)}}>Load shit</button>
|
||||
<div>
|
||||
<Interactive
|
||||
as='button'
|
||||
onClick={() => {this.searchTheBay()}}
|
||||
style={btnStylesheet.submit}
|
||||
focus={btnStylesheet.submit_hover}
|
||||
hover={btnStylesheet.submit_hover}>
|
||||
|
||||
<span style={{whiteSpace: 'nowrap'}}>Search for torrents</span>
|
||||
</Interactive>
|
||||
</div>
|
||||
|
||||
{ this.state.loading }
|
||||
|
||||
<span>{this.state.response}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,57 +1,213 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Interactive from 'react-interactive';
|
||||
|
||||
import sidebarCSS from '../styles/adminSidebar.jsx'
|
||||
|
||||
class SidebarComponent extends Component {
|
||||
|
||||
generateListElements(index, item) {
|
||||
if (index == this.props.listItemSelected)
|
||||
return (
|
||||
<td>{item.name}</td>
|
||||
)
|
||||
else
|
||||
return (
|
||||
<td><Link to={{ pathname: '/admin/'+String(index)}}>{item.name}</Link></td>
|
||||
)
|
||||
}
|
||||
|
||||
displayRequestedElementsInfo() {
|
||||
if (this.props.requested_objects) {
|
||||
let requestedElement = this.props.requested_objects.map((item, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
{ this.generateListElements(index, item) }
|
||||
<td>{item.status}</td>
|
||||
<td>{item.requested_date}</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<table key='requestedTable'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><b>Name</b></th>
|
||||
<th><b>Status</b></th>
|
||||
<th><b>Date</b></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{requestedElement}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
constructor(props){
|
||||
super(props)
|
||||
// Constructor with states holding the search query and the element of reponse.
|
||||
this.state = {
|
||||
filterValue: '',
|
||||
filterQuery: '',
|
||||
requestItemsToBeDisplayed: [],
|
||||
listItemSelected: '',
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log('sidebar: ', this.props.requested_objects)
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello from the sidebar: </h1>
|
||||
<span>{ this.displayRequestedElementsInfo() }</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// Where we wait for api response to be delivered from parent through props
|
||||
componentWillReceiveProps(props) {
|
||||
this.state.listItemSelected = props.listItemSelected;
|
||||
this.displayRequestedElementsInfo(props.requested_objects);
|
||||
}
|
||||
|
||||
// Inputs a date and returns a text string that matches how long it was since
|
||||
convertDateToDaysSince(date) {
|
||||
var oneDay = 24*60*60*1000;
|
||||
var firstDate = new Date(date);
|
||||
var secondDate = new Date();
|
||||
|
||||
var diffDays = Math.round(Math.abs((firstDate.getTime() - secondDate.getTime()) / oneDay));
|
||||
|
||||
switch (diffDays) {
|
||||
case 0:
|
||||
return 'Today';
|
||||
case 1:
|
||||
return '1 day ago'
|
||||
default:
|
||||
return diffDays + ' days ago'
|
||||
}
|
||||
}
|
||||
|
||||
// Called from our dropdown, receives a filter string and checks it with status field
|
||||
// of our request objects.
|
||||
filterItems(filterValue) {
|
||||
let filteredRequestElements = this.props.requested_objects.map((item, index) => {
|
||||
if (item.status === filterValue || filterValue === 'all')
|
||||
return this.generateListElements(index, item);
|
||||
})
|
||||
|
||||
this.setState({
|
||||
requestItemsToBeDisplayed: filteredRequestElements,
|
||||
filterValue: filterValue,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Updates the internal state of the query filter and updates the list to only
|
||||
// display names matching the query. This is real-time filtering.
|
||||
updateFilterQuery(event) {
|
||||
const query = event.target.value;
|
||||
|
||||
let filteredByQuery = this.props.requested_objects.map((item, index) => {
|
||||
if (item.name.toLowerCase().indexOf(query.toLowerCase()) != -1)
|
||||
return this.generateListElements(index, item);
|
||||
})
|
||||
|
||||
this.setState({
|
||||
requestItemsToBeDisplayed: filteredByQuery,
|
||||
filterQuery: query,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
generateFilterDropdown() {
|
||||
return (
|
||||
<select onChange={ event => this.filterItems(event.target.value) } value={this.state.filterValue}>
|
||||
<option value='all'>All</option>
|
||||
<option value='requested'>Requested</option>
|
||||
<option value='downloading'>Downloading</option>
|
||||
<option value='downloaded'>Downloaded</option>
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
generateFilterSearchbar() {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
id="search"
|
||||
placeholder="Filter by name..."
|
||||
onChange={event => this.updateFilterQuery(event)}
|
||||
value={this.state.filterQuery}/>
|
||||
)
|
||||
}
|
||||
|
||||
// A colored bar indicating the status of a item by color.
|
||||
generateRequestIndicator(status) {
|
||||
let statusColor;
|
||||
|
||||
switch (status) {
|
||||
case 'requested':
|
||||
// Yellow
|
||||
statusColor = '#ffe14d';
|
||||
break;
|
||||
case 'downloading':
|
||||
// Blue
|
||||
statusColor = '#3fc3f3';
|
||||
break;
|
||||
case 'downloaded':
|
||||
// Green
|
||||
statusColor = '#6be682';
|
||||
break;
|
||||
default:
|
||||
statusColor = 'grey';
|
||||
}
|
||||
|
||||
const indicatorCSS = {
|
||||
width: '100%',
|
||||
height: '4px',
|
||||
marginTop: '3px',
|
||||
backgroundColor: statusColor,
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={indicatorCSS}></div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
generateListElements(index, item) {
|
||||
if (index == this.state.listItemSelected) {
|
||||
return (
|
||||
<div style={sidebarCSS.parentElement_selected}>
|
||||
<div style={sidebarCSS.contentContainer}>
|
||||
<span style={sidebarCSS.title}> {item.name } </span>
|
||||
<div style={sidebarCSS.rightContainer}>
|
||||
<span>{ this.convertDateToDaysSince(item.requested_date) }</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>Status: { item.status }</span>
|
||||
<br/>
|
||||
<span>Matches found: 0</span>
|
||||
{ this.generateRequestIndicator(item.status) }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
else
|
||||
return (
|
||||
<Link style={sidebarCSS.link} to={{ pathname: '/admin/'+String(index)}}>
|
||||
<Interactive
|
||||
key={index}
|
||||
style={sidebarCSS.parentElement}
|
||||
as='div'
|
||||
hover={sidebarCSS.parentElement_hover}
|
||||
focus={sidebarCSS.parentElement_hover}
|
||||
active={sidebarCSS.parentElement_active}>
|
||||
|
||||
<span style={sidebarCSS.title}> {item.name } </span>
|
||||
<div style={sidebarCSS.rightContainer}>
|
||||
<span>{ this.convertDateToDaysSince(item.requested_date) }</span>
|
||||
</div>
|
||||
<br/>
|
||||
{ this.generateRequestIndicator(item.status) }
|
||||
</Interactive>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
// This is our main loader that gets called when we receive api response through props from parent
|
||||
displayRequestedElementsInfo(requested_objects) {
|
||||
let requestedElement = requested_objects.map((item, index) => {
|
||||
if (['requested', 'downloading', 'downloaded'].indexOf(this.state.filterValue) != -1) {
|
||||
if (item.status === this.state.filterValue){
|
||||
return this.generateListElements(index, item);
|
||||
}
|
||||
}
|
||||
|
||||
else if (this.state.filterQuery !== '') {
|
||||
if (item.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) != -1)
|
||||
return this.generateListElements(index, item);
|
||||
}
|
||||
|
||||
else
|
||||
return this.generateListElements(index, item);
|
||||
})
|
||||
|
||||
this.setState({
|
||||
requestItemsToBeDisplayed: requestedElement,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
let bodyCSS = sidebarCSS.body;
|
||||
if (typeof InstallTrigger !== 'undefined')
|
||||
bodyCSS.width = '-moz-min-content';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello from the sidebar: </h1>
|
||||
{ this.generateFilterDropdown() }
|
||||
{ this.generateFilterSearchbar() }
|
||||
<div key='requestedTable' style={bodyCSS}>
|
||||
{ this.state.requestItemsToBeDisplayed }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SidebarComponent;
|
||||
34
client/app/components/images/loading.jsx
Normal file
34
client/app/components/images/loading.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
function Loading() {
|
||||
return (
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<svg version="1.1"
|
||||
style={{height: '75px'}}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 80 80">
|
||||
<path
|
||||
fill="#e9a131"
|
||||
d="M40,72C22.4,72,8,57.6,8,40C8,22.4,
|
||||
22.4,8,40,8c17.6,0,32,14.4,32,32c0,1.1-0.9,2-2,2
|
||||
s-2-0.9-2-2c0-15.4-12.6-28-28-28S12,24.6,12,40s12.6,
|
||||
28,28,28c1.1,0,2,0.9,2,2S41.1,72,40,72z">
|
||||
|
||||
<animateTransform
|
||||
attributeType="xml"
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 40 40"
|
||||
to="360 40 40"
|
||||
dur="1.0s"
|
||||
repeatCount="indefinite"/>
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Loading;
|
||||
8
client/app/components/styles/adminComponent.jsx
Normal file
8
client/app/components/styles/adminComponent.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
sidebar: {
|
||||
float: 'left',
|
||||
},
|
||||
selectedObjectPanel: {
|
||||
float: 'left',
|
||||
}
|
||||
}
|
||||
11
client/app/components/styles/adminRequestInfo.jsx
Normal file
11
client/app/components/styles/adminRequestInfo.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export default {
|
||||
wrapper: {
|
||||
width: '100%',
|
||||
},
|
||||
headerWrapper: {
|
||||
width: '100%',
|
||||
},
|
||||
poster: {
|
||||
minHeight: '450px',
|
||||
},
|
||||
}
|
||||
50
client/app/components/styles/adminSidebar.jsx
Normal file
50
client/app/components/styles/adminSidebar.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
export default {
|
||||
body: {
|
||||
backgroundColor: 'white',
|
||||
width: 'min-content',
|
||||
},
|
||||
|
||||
parentElement: {
|
||||
display: 'inline-block',
|
||||
width: '300px',
|
||||
border: '1px solid black',
|
||||
borderRadius: '2px',
|
||||
padding: '4px',
|
||||
margin: '4px',
|
||||
marginLeft: '4px',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
|
||||
parentElement_hover: {
|
||||
marginLeft: '10px',
|
||||
},
|
||||
|
||||
parentElement_active: {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
|
||||
parentElement_selected: {
|
||||
display: 'inline-block',
|
||||
width: '300px',
|
||||
border: '1px solid black',
|
||||
borderRadius: '2px',
|
||||
padding: '4px',
|
||||
margin: '4px 0px 4px 4px',
|
||||
marginLeft: '10px',
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
|
||||
title: {
|
||||
maxWidth: '70%',
|
||||
display: 'inline-flex',
|
||||
},
|
||||
|
||||
link: {
|
||||
color: 'black',
|
||||
textDecoration: 'none',
|
||||
},
|
||||
|
||||
rightContainer: {
|
||||
float: 'right',
|
||||
},
|
||||
}
|
||||
80
client/app/components/styles/buttons.jsx
Normal file
80
client/app/components/styles/buttons.jsx
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
export default {
|
||||
|
||||
submit: {
|
||||
color: '#e9a131',
|
||||
marginRight: '10px',
|
||||
backgroundColor: 'white',
|
||||
border: '#e9a131 2px solid',
|
||||
borderColor: '#e9a131',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '10px',
|
||||
minWidth: '100px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
submit_hover: {
|
||||
backgroundColor: '#e9a131',
|
||||
color: 'white',
|
||||
},
|
||||
|
||||
info: {
|
||||
color: '#00d17c',
|
||||
marginRight: '10px',
|
||||
backgroundColor: 'white',
|
||||
border: '#00d17c 2px solid',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '10px',
|
||||
minWidth: '100px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
info_hover: {
|
||||
backgroundColor: '#00d17c',
|
||||
color: 'white',
|
||||
},
|
||||
|
||||
edit: {
|
||||
color: '#4a95da',
|
||||
marginRight: '10px',
|
||||
backgroundColor: 'white',
|
||||
border: '#4a95da 2px solid',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '10px',
|
||||
minWidth: '100px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
edit_small: {
|
||||
color: '#4a95da',
|
||||
marginRight: '10px',
|
||||
backgroundColor: 'white',
|
||||
border: '#4a95da 2px solid',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '4px',
|
||||
minWidth: '50px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
|
||||
edit_hover: {
|
||||
backgroundColor: '#4a95da',
|
||||
color: 'white',
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
|
||||
export default {
|
||||
resultItem: {
|
||||
maxWidth: '95%',
|
||||
margin: '0 auto',
|
||||
minHeight: '230px'
|
||||
},
|
||||
|
||||
movie_content: {
|
||||
marginLeft: '15px'
|
||||
},
|
||||
|
||||
resultTitleLarge: {
|
||||
color: 'black',
|
||||
fontSize: '2em',
|
||||
},
|
||||
|
||||
resultTitleSmall: {
|
||||
color: 'black',
|
||||
fontSize: '22px',
|
||||
},
|
||||
|
||||
yearRatingLarge: {
|
||||
fontSize: '0.8em'
|
||||
},
|
||||
|
||||
resultPoster: {
|
||||
float: 'left',
|
||||
zIndex: '3',
|
||||
position: 'relative',
|
||||
marginRight: '30px'
|
||||
},
|
||||
|
||||
background: {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
yearRatingSmall: {
|
||||
marginTop: '5px',
|
||||
fontSize: '0.8em'
|
||||
},
|
||||
|
||||
resultPosterImg: {
|
||||
border: '2px none',
|
||||
borderRadius: '2px',
|
||||
width: '150px'
|
||||
},
|
||||
|
||||
cornerRibbon: {
|
||||
position: 'absolute',
|
||||
width: '450px',
|
||||
},
|
||||
|
||||
summary: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
|
||||
buttons: {
|
||||
paddingTop: '20px'
|
||||
},
|
||||
|
||||
requestButton: {
|
||||
color: '#e9a131',
|
||||
marginRight: '10px',
|
||||
background: 'white',
|
||||
border: '#e9a131 2px solid',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '10px',
|
||||
minWidth: '100px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
|
||||
tmdbButton: {
|
||||
color: '#00d17c',
|
||||
marginRight: '10px',
|
||||
background: 'white',
|
||||
border: '#00d17c 2px solid',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
padding: '10px',
|
||||
minWidth: '100px',
|
||||
float: 'left',
|
||||
fontSize: '13px',
|
||||
fontWeight: '800',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
|
||||
row: {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
itemDivider: {
|
||||
width: '90%',
|
||||
borderBottom: '1px solid grey',
|
||||
margin: '2rem auto'
|
||||
}
|
||||
}
|
||||
58
client/app/components/styles/searchObject.jsx
Normal file
58
client/app/components/styles/searchObject.jsx
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
export default {
|
||||
container: {
|
||||
maxWidth: '95%',
|
||||
margin: '0 auto',
|
||||
minHeight: '230px'
|
||||
},
|
||||
|
||||
title_large: {
|
||||
color: 'black',
|
||||
fontSize: '2em',
|
||||
},
|
||||
|
||||
title_small: {
|
||||
color: 'black',
|
||||
fontSize: '22px',
|
||||
},
|
||||
|
||||
stats_large: {
|
||||
fontSize: '0.8em'
|
||||
},
|
||||
|
||||
stats_small: {
|
||||
marginTop: '5px',
|
||||
fontSize: '0.8em'
|
||||
},
|
||||
|
||||
posterContainer: {
|
||||
float: 'left',
|
||||
zIndex: '3',
|
||||
position: 'relative',
|
||||
marginRight: '30px'
|
||||
},
|
||||
|
||||
posterImage: {
|
||||
border: '2px none',
|
||||
borderRadius: '2px',
|
||||
width: '150px'
|
||||
},
|
||||
|
||||
backgroundImage: {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
summary: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
|
||||
dividerRow: {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
itemDivider: {
|
||||
width: '90%',
|
||||
borderBottom: '1px solid grey',
|
||||
margin: '2rem auto'
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,13 @@ export default {
|
||||
backgroundLargeHeader: {
|
||||
width: '100%',
|
||||
minHeight: '400px',
|
||||
backgroundColor: '#011c23',
|
||||
backgroundColor: 'rgb(1, 28, 35)',
|
||||
// backgroundImage: 'radial-gradient(circle, #004c67 0, #005771 120%)',
|
||||
zIndex: 1,
|
||||
marginBottom: '-100px'
|
||||
},
|
||||
|
||||
backgroundSmallHeader: {
|
||||
backgroundSmallHeader: {
|
||||
width: '100%',
|
||||
minHeight: '300px',
|
||||
backgroundColor: '#011c23',
|
||||
@@ -31,7 +32,7 @@ export default {
|
||||
backgroundColor: 'white',
|
||||
position: 'relative',
|
||||
zIndex: '10',
|
||||
boxShadow: '0 4px 2px black'
|
||||
boxShadow: '0 1px 2px grey',
|
||||
},
|
||||
|
||||
pageTitle: {
|
||||
@@ -43,7 +44,7 @@ export default {
|
||||
|
||||
pageTitleLargeSpan: {
|
||||
color: 'white',
|
||||
fontSize: '3em',
|
||||
fontSize: '4em',
|
||||
marginTop: '4vh',
|
||||
marginBottom: '6vh'
|
||||
},
|
||||
@@ -133,70 +134,4 @@ export default {
|
||||
color: 'black',
|
||||
fontSize: '1.4em',
|
||||
},
|
||||
|
||||
row: {
|
||||
width: '100%'
|
||||
},
|
||||
|
||||
itemDivider: {
|
||||
width: '90%',
|
||||
borderBottom: '1px solid grey',
|
||||
margin: '1rem auto'
|
||||
},
|
||||
|
||||
|
||||
pageNavigationBar: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
pageNavigationButton: {
|
||||
margin: '0 auto',
|
||||
},
|
||||
|
||||
hvrUnderlineFromCenter: {
|
||||
color: 'white',
|
||||
fontSize: '1em',
|
||||
paddingTop: '12px',
|
||||
marginBottom: '12px',
|
||||
marginLeft: '10px',
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
WebkitTransform: 'perspective(1px) translateZ(0)',
|
||||
transform: 'perspective(1px) translateZ(0)',
|
||||
boxShadow: '0 0 1px transparent',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
':before': {
|
||||
content: "",
|
||||
position: 'absolute',
|
||||
zIndex: '-1',
|
||||
left: '50%',
|
||||
right: '50%',
|
||||
bottom: '0',
|
||||
background: '#00d17c',
|
||||
height: '2px',
|
||||
WebkitTransitionProperty: 'left, right',
|
||||
transitionProperty: 'left, right',
|
||||
WebkitTransitionDuration: '0.3s',
|
||||
transitionDuration: '0.3s',
|
||||
WebkitTransitionTimingFunction: 'ease-out',
|
||||
transitionTimingFunction: 'ease-out'
|
||||
},
|
||||
':hover:before': {
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
'focus:before': {
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
'active:before': {
|
||||
left: 0,
|
||||
right: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0, user-scalable=0">
|
||||
<title>seasoned Shows</title>
|
||||
</head>
|
||||
<body style='margin: 0'>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"react-burger-menu": "^2.1.6",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-infinite-scroller": "^1.0.15",
|
||||
"react-interactive": "^0.8.1",
|
||||
"react-notify-toast": "^0.3.2",
|
||||
"react-redux": "^5.0.6",
|
||||
"react-responsive": "^1.3.4",
|
||||
|
||||
@@ -21,8 +21,7 @@ module.exports = {
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
|
||||
{ test: /\.jsx$/, loader: 'babel-loader', exclude: /node_modules/ },
|
||||
{ test: /\.(js|jsx)$/, loader: 'babel-loader', exclude: /node_modules/ },
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user