diff --git a/components/404.jsx b/components/404.jsx new file mode 100644 index 0000000..c005254 --- /dev/null +++ b/components/404.jsx @@ -0,0 +1,16 @@ +var React = require('react'); + + +var PageNotFound = React.createClass({ + render: function() { + return ( +
+
+ ) + } +}); + +module.exports = PageNotFound; diff --git a/components/contact.jsx b/components/contact.jsx new file mode 100644 index 0000000..734136c --- /dev/null +++ b/components/contact.jsx @@ -0,0 +1,143 @@ +var React = require('react'); +var Formsy = require('formsy-react'); +var Textarea = require('react-textarea-autosize'); + +var Nav = require('./nav'); +var Footer = require('./footer'); + + +var ContactPage = React.createClass({ + getInitialState: function() { + return { + canSubmit: false + }; + }, + + enableButton: function () { + this.setState({ + canSubmit: true + }); + }, + + disableButton: function () { + this.setState({ + canSubmit: false + }); + }, + + resetForm: function() { + this.refs.form.reset(); + this.refs.form.inputs.emailContent.setValue(''); + }, + + handleSubmit: function(data) { + var data = JSON.stringify({ + name: data.fullName, + email: data.email, + content: data.emailContent + }); + + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + xhr.setRequestHeader("X-CSRFToken", Django.csrf_token()); + xhr.setRequestHeader("Content-Type", 'application/json'); + } + }); + + $.ajax({ + type: 'POST', + url: '/api/public/contact/', + data: data, + success: function(data) { this.resetForm() }.bind(this), + error: function(error) { } + }); + + }, + + render: function() { + return ( +
+
+ ) + } +}); + +var FullNameInput = React.createClass({ + mixins: [Formsy.Mixin], + + changeValue: function (event) { + this.setValue(event.currentTarget.value); + }, + + render: function () { + var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; + + var errorMessage = this.getErrorMessage(); + + return ( +
+ + {errorMessage} +
+ ); + } +}); + +var EmailInput = React.createClass({ + mixins: [Formsy.Mixin], + + changeValue: function (event) { + this.setValue(event.currentTarget.value); + }, + + render: function () { + var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; + + var errorMessage = this.getErrorMessage(); + + return ( +
+ + {errorMessage} +
+ ); + } +}); + +var ContentTextarea = React.createClass({ + mixins: [Formsy.Mixin], + + changeValue: function (event) { + this.setValue(event.currentTarget.value); + }, + + render: function () { + var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; + + var errorMessage = this.getErrorMessage(); + + return ( +
+ + {errorMessage} +
+ ); + } +}); + +module.exports = ContactPage; diff --git a/components/cub.jsx b/components/cub.jsx index 00ff9ff..5c31198 100644 --- a/components/cub.jsx +++ b/components/cub.jsx @@ -1,650 +1,25 @@ var React = require('react'); -var ReactDOM = require('react-dom'); - -var Formsy = require('formsy-react'); var History = require('history'); -var Link = require('react-router').Link; +var ReactDOM = require('react-dom'); var Router = require('react-router').Router; var Route = require('react-router').Route; -var Redirect = require('react-router').Redirect; -var ReactPaginate = require('react-paginate'); -var Textarea = require('react-textarea-autosize'); - -const history = History.createHistory(); -const per_page = 10; - - -const MAX_PROGRESS = 1000 - - -var Repo = React.createClass({ - render: function() { - var repo_score = this.props.repo.watchers_count * PointsList.watch; - repo_score += this.props.repo.stargazers_count * PointsList.star; - - return ( -
-
-
{repo_score}
-
Points
-
-
- {this.props.repo.name} -
-
{this.props.repo.description}
- -
- ) - } -}) - - -var RepoList = React.createClass({ - getInitialState: function() { - return { - repos: [], - offset: 0, - pageNum: 1, - nextOffset: per_page, - previousOffset: 0 - } - }, - - getRepoList: function() { - var offset = this.state.offset; - var nextOffset = offset + per_page; - var previousOffset = (offset - per_page < 1) ? 0 : offset - per_page; - var url = '/api/v1/repository/?offset=' + offset + '&limit=' + per_page; - - $.get(url, function(res) { - this.setState({ - repos: res.objects, - nextOffset: nextOffset, - previousOffset: previousOffset, - pageNum: Math.ceil(res.meta.total_count / per_page )}) - }.bind(this)); - }, - - componentDidMount: function() { - if (this.props.username !== 'undefined' && this.isMounted()) { - this.getRepoList(); - - } else { - // Load demo page - this.setState({ - repos : {}, - offset : null, - pageNum : null - }); - } - }, - - handleClick: function(event) { - var pageSelected = event.selected; - - this.setState({ - offset: pageSelected * per_page - }, () => { - this.getRepoList(); - }); - }, - - render: function() { - return ( -
-

Your score

- -

Your contributions

- { this.state.repos.map(function(repo, i) { - return () - }, this)} - - ...} - pageNum={this.state.pageNum} - marginPagesDisplayed={2} - pageRangeDisplayed={5} - clickCallback={this.handleClick} - containerClassName={"pagination"} - subContainerClassName={"pagination-pages"} - activeClassName={"active"} /> -
- ); - } -}); - - -var PointsList = React.createClass({ - statics: { - watch : 10, - star : 20 - }, - - render: function() { - return ( -
-

Points:

- -
- ); - } -}); - - -var ProfileStats = React.createClass({ - getInitialState: function() { - return { - repos_count: null, - repos_score: null, - repos_top_name: null, - repos_top_score: null, - progress: 0, - login_message: null - } - }, - - componentDidMount: function() { - if (typeof username === 'undefined') { - if (typeof this.props.username === 'undefined') { - // User not authenticated, looking at default page - // Show demo data - this.setState({ - repos_count: '###', - repos_score: '###', - repos_top_name: '###', - repos_top_score: '__', - progress: 15, - login_message: 'You need to login to see your own metrics.' - }); - - } else { - // User is not authenticated, looking at someone - // Show some real data - this.setState({ - repos_count: '###', - repos_score: '###', - repos_top_name: '###', - repos_top_score: '__', - progress: 0, - login_message: 'You need to login to see more metrics.' - }); - } - - } else { - if (typeof this.props.username === 'undefined') { - // User is authenticated, looking at default page - this.setState(this.state); - - } else if (this.props.username == username) { - // User is authenticated, looking at own profile - var offset = 0; - var per_page = 999; - var url = '/api/v1/repository/?offset=' + offset + '&limit=' + per_page; - - $.get(url, function(res) { - if (this.isMounted()) { - - // Compute total repo score and max repo - var total_score = 0; - var max_score = 0; - var max_repo = ''; - for (var i in res.objects) { - var score = 0; - score += res.objects[i].watchers_count * PointsList.watch; - score += res.objects[i].stargazers_count * PointsList.star; - - if (score > max_score) { max_score = score; max_repo = res.objects[i].name } - total_score += score; - } - - this.setState({ - repos_count: res.objects.length, - repos_score: total_score, - repos_top_name: max_repo, - repos_top_score: max_score, - progress: 100 * total_score / MAX_PROGRESS - }); - } - }.bind(this)); - - } else { - // User is authenticated, looking at someone - this.setState({ - repos_count: null, - repos_score: null, - repos_top_name: null, - repos_top_score: null, - progress: 0 - }); - } - } - }, - - render: function() { - return( -
-
- Noob - Master -
-
- {this.state.progress + '%'} -
-
- Rockstar -
- -
-
Most important repo
-
{this.state.repos_top_name}
-
{this.state.repos_top_score + " points"}
-
-
-
Total score
-
{this.state.repos_score}
-
for all repos
-
-
-
Own contributions count
-
{this.state.repos_count}
-
repositories
-
- - { this.state.login_message ? -
- {this.state.login_message} -
: ''} -
- ); - } -}); - -var Profile = React.createClass({ - getInitialState: function() { - return { - avatar_url : null, - name : null, - username : null, - email : null, - progress : 70, - } - }, - - componentDidMount: function() { - if (typeof username === 'undefined') { - if (typeof this.props.username === 'undefined') { - // User not authenticated, looking at default page - // Show demo data - this.setState({ - avatar_url : 'http://bestmarketinfo.com/images/blank-avatar.png', - name : 'Your name', - username : 'username', - email : 'username@cub.com', - }); - - } else { - // User is not authenticated, looking at someone - // Show some real data - var url = '/api/public/account/?username=' + this.props.username; - - $.get(url, function(res) { - if (this.isMounted()) { - this.setState({ - avatar_url : res.objects[0].avatar_url, - name : res.objects[0].name, - username : res.objects[0].username - }); - } - } - .bind(this)) - .fail(function(err) { - if (err.status == 400 ) { - // User not found, redirect to 404 page - window.location.href = "/404/"; - } - }); - } - - } else { - if (typeof this.props.username === 'undefined') { - // User is authenticated, looking at default page - // Redirect to own profile - window.location.href = '/profile/' + username; - - } else if (this.props.username == username) { - // User is authenticated, looking at own profile - $.get('/api/v1/account/', function(res) { - if (this.isMounted()) { - this.setState({ - avatar_url : res.objects[0].avatar_url, - name : res.objects[0].name, - username : res.objects[0].username, - email : res.objects[0].email - }); - } - }.bind(this)); - - } else { - // User is authenticated, looking at someone - $.get('/api/v1/account/?username=' + this.props.username, function(res) { - if (this.isMounted()) { - this.setState({ - avatar_url : res.objects[0].avatar_url, - name : res.objects[0].name, - username : res.objects[0].username, - email : res.objects[0].email - }); - } - } - .bind(this)) - .fail(function(err) { - if (err.status == 400 ) { - // User not found, redirect to 404 page - window.location.href = "/404/"; - } - }); - } - } - }, - - render: function() { - if (this.state.avatar_url) { - return ( -
-
- -
-
-
{this.state.name}
- -
{this.state.email}
-
-
- ); - } else { - return(
); - } - } -}) - - -var Nav = React.createClass({ - render: function() { - var user = '' - if (typeof username !== 'undefined') - user = username - - return ( - - ) - }, - - render_links: function() { - if (typeof user !== 'undefined') { - return (
  • Logout
  • ) - } else { - return (
  • Login
  • ) - } - } -}) - - -var RepoPage = React.createClass({ - render: function() { - return ( -
    -
    - ) - } -}) - - -var FullNameInput = React.createClass({ - mixins: [Formsy.Mixin], - - changeValue: function (event) { - this.setValue(event.currentTarget.value); - }, - render: function () { - var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; - - var errorMessage = this.getErrorMessage(); - - return ( -
    - - {errorMessage} -
    - ); - } - }) - - var EmailInput = React.createClass({ - mixins: [Formsy.Mixin], - - changeValue: function (event) { - this.setValue(event.currentTarget.value); - }, - render: function () { - var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; - - var errorMessage = this.getErrorMessage(); - - return ( -
    - - {errorMessage} -
    - ); - } - }) - - var ContentTextarea = React.createClass({ - mixins: [Formsy.Mixin], - - changeValue: function (event) { - this.setValue(event.currentTarget.value); - }, - render: function () { - var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; - - var errorMessage = this.getErrorMessage(); - - return ( -
    - - {errorMessage} -
    - ); - } - }) - -var ContactPage = React.createClass({ - - getInitialState: function() { - return { - canSubmit: false - }; - }, - - enableButton: function () { - this.setState({ - canSubmit: true - }); - }, - - disableButton: function () { - this.setState({ - canSubmit: false - }); - }, - - resetForm: function() { - this.refs.form.reset(); - this.refs.form.inputs.emailContent.setValue(''); - }, - - handleSubmit: function(data) { - var data = JSON.stringify({ - name: data.fullName, - email: data.email, - content: data.emailContent - }); - - $.ajaxSetup({ - beforeSend: function(xhr, settings) { - xhr.setRequestHeader("X-CSRFToken", Django.csrf_token()); - xhr.setRequestHeader("Content-Type", 'application/json'); - } - }); - - $.ajax({ - type: 'POST', - url: '/api/public/contact/', - data: data, - success: function(data) { this.resetForm() }.bind(this), - error: function(error) { } - }); - - }, - - render: function() { - return ( -
    -
    - ) - } -}) - - -var ProfilePage = React.createClass({ - render: function() { - return ( -
    -
    - ) - } -}) - - -var IndexPage = React.createClass({ - statics: { - onEnter(next, transition) { - // Redirect to profile if user is logged in - if (typeof username !== 'undefined') { - return transition(null, '/profile/' + username); - } - }, - }, - - render: function() { - return ( -
    -
    - ) - }, - - render_links: function() { - // OBSOLETE, will be removed - // User will be redirected to profile page if he is logged in, so we don't - // need to show /profile link anymore - if (typeof user !== 'undefined') - return (Profile) - else - return (Login) - } -}) - - -var NotFoundPage = React.createClass({ - render: function() { - return ( -
    -
    - ) - } -}) - - -var Footer = React.createClass({ - render: function() { - return ( - - ) - } -}) +var IndexPage = require('./index'); +var ProfilePage = require('./profile'); +var RepoPage = require('./repos'); +var ContactPage = require('./contact'); +var PointsList = require('./settings'); +var PageNotFound = require('./404'); ReactDOM.render(( - + - + ), document.getElementById("main")) diff --git a/components/footer.jsx b/components/footer.jsx new file mode 100644 index 0000000..189ce7b --- /dev/null +++ b/components/footer.jsx @@ -0,0 +1,18 @@ +var React = require('react'); + + +var Footer = React.createClass({ + render: function() { + return ( + + ) + } +}); + +module.exports = Footer; diff --git a/components/index.jsx b/components/index.jsx new file mode 100644 index 0000000..ad45592 --- /dev/null +++ b/components/index.jsx @@ -0,0 +1,40 @@ +var React = require('react'); + + +var IndexPage = React.createClass({ + statics: { + onEnter(next, transition) { + // Redirect to profile if user is logged in + if (typeof username !== 'undefined') { + return transition(null, '/profile/' + username); + } + }, + }, + + render: function() { + return ( +
    +
    + ) + }, + + render_links: function() { + // OBSOLETE, will be removed + // User will be redirected to profile page if he is logged in, so we don't + // need to show /profile link anymore + if (typeof user !== 'undefined') + return (Profile) + else + return (Login) + } +}); + +module.exports = IndexPage; diff --git a/components/nav.jsx b/components/nav.jsx new file mode 100644 index 0000000..2d30e36 --- /dev/null +++ b/components/nav.jsx @@ -0,0 +1,36 @@ +var React = require('react'); +var Link = require('react-router').Link; + +var Nav = React.createClass({ + render: function() { + var user = '' + if (typeof username !== 'undefined') + user = username + + return ( + + ) + }, + + render_links: function() { + if (typeof user !== 'undefined') { + return (
  • Logout
  • ) + } else { + return (
  • Login
  • ) + } + } +}) + +module.exports = Nav; diff --git a/components/profile.jsx b/components/profile.jsx new file mode 100644 index 0000000..5102f61 --- /dev/null +++ b/components/profile.jsx @@ -0,0 +1,262 @@ +var React = require('react'); + +var Nav = require('./nav'); +var PointsList = require('./settings'); +var Footer = require('./footer'); + +const MAX_PROGRESS = 1000; + + +var ProfilePage = React.createClass({ + render: function() { + return ( +
    +
    + ) + } +}); + +var ProfileStats = React.createClass({ + getInitialState: function() { + return { + repos_count: null, + repos_score: null, + repos_top_name: null, + repos_top_score: null, + progress: 0, + login_message: null + } + }, + + componentDidMount: function() { + if (typeof username === 'undefined') { + if (typeof this.props.username === 'undefined') { + // User not authenticated, looking at default page + // Show demo data + this.setState({ + repos_count: '###', + repos_score: '###', + repos_top_name: '###', + repos_top_score: '__', + progress: 15, + login_message: 'You need to login to see your own metrics.' + }); + + } else { + // User is not authenticated, looking at someone + // Show some real data + this.setState({ + repos_count: '###', + repos_score: '###', + repos_top_name: '###', + repos_top_score: '__', + progress: 0, + login_message: 'You need to login to see more metrics.' + }); + } + + } else { + if (typeof this.props.username === 'undefined') { + // User is authenticated, looking at default page + this.setState(this.state); + + } else if (this.props.username == username) { + // User is authenticated, looking at own profile + var offset = 0; + var per_page = 999; + var url = '/api/v1/repository/?offset=' + offset + '&limit=' + per_page; + + $.get(url, function(res) { + if (this.isMounted()) { + + // Compute total repo score and max repo + var total_score = 0; + var max_score = 0; + var max_repo = ''; + for (var i in res.objects) { + var score = 0; + score += res.objects[i].watchers_count * PointsList.watch; + score += res.objects[i].stargazers_count * PointsList.star; + + if (score > max_score) { max_score = score; max_repo = res.objects[i].name } + total_score += score; + } + + this.setState({ + repos_count: res.objects.length, + repos_score: total_score, + repos_top_name: max_repo, + repos_top_score: max_score, + progress: 100 * total_score / MAX_PROGRESS + }); + } + }.bind(this)); + + } else { + // User is authenticated, looking at someone + this.setState({ + repos_count: null, + repos_score: null, + repos_top_name: null, + repos_top_score: null, + progress: 0 + }); + } + } + }, + + render: function() { + return( +
    +
    + Noob + Master +
    +
    + {this.state.progress + '%'} +
    +
    + Rockstar +
    + +
    +
    Most important repo
    +
    {this.state.repos_top_name}
    +
    {this.state.repos_top_score + " points"}
    +
    +
    +
    Total score
    +
    {this.state.repos_score}
    +
    for all repos
    +
    +
    +
    Own contributions count
    +
    {this.state.repos_count}
    +
    repositories
    +
    + + { this.state.login_message ? +
    + {this.state.login_message} +
    : ''} +
    + ); + } +}); + +var Profile = React.createClass({ + getInitialState: function() { + return { + avatar_url : null, + name : null, + username : null, + email : null, + progress : 70, + } + }, + + componentDidMount: function() { + if (typeof username === 'undefined') { + if (typeof this.props.username === 'undefined') { + // User not authenticated, looking at default page + // Show demo data + this.setState({ + avatar_url : '/static/images/generic_avatar.png', + name : 'Your name', + username : 'username', + email : 'username@cub.com', + }); + + } else { + // User is not authenticated, looking at someone + // Show some real data + var url = '/api/public/account/?username=' + this.props.username; + + $.get(url, function(res) { + if (this.isMounted()) { + this.setState({ + avatar_url : res.objects[0].avatar_url, + name : res.objects[0].name, + username : res.objects[0].username + }); + } + } + .bind(this)) + .fail(function(err) { + if (err.status == 400 ) { + // User not found, redirect to 404 page + window.location.href = "/404/"; + } + }); + } + + } else { + if (typeof this.props.username === 'undefined') { + // User is authenticated, looking at default page + // Redirect to own profile + window.location.href = '/profile/' + username; + + } else if (this.props.username == username) { + // User is authenticated, looking at own profile + $.get('/api/v1/myaccount/', function(res) { + if (this.isMounted()) { + this.setState({ + avatar_url : res.objects[0].avatar_url, + name : res.objects[0].name, + username : res.objects[0].username, + email : res.objects[0].email + }); + } + }.bind(this)); + + } else { + // User is authenticated, looking at someone + $.get('/api/v1/account/?username=' + this.props.username, function(res) { + if (this.isMounted()) { + this.setState({ + avatar_url : res.objects[0].avatar_url, + name : res.objects[0].name, + username : res.objects[0].username, + email : res.objects[0].email + }); + } + } + .bind(this)) + .fail(function(err) { + if (err.status == 400 ) { + // User not found, redirect to 404 page + window.location.href = "/404/"; + } + }); + } + } + }, + + render: function() { + if (this.state.avatar_url) { + return ( +
    +
    + +
    +
    +
    {this.state.name}
    + +
    {this.state.email}
    +
    +
    + ); + } else { + return(
    ); + } + } +}); + +module.exports = ProfilePage; diff --git a/components/repos.jsx b/components/repos.jsx new file mode 100644 index 0000000..e499365 --- /dev/null +++ b/components/repos.jsx @@ -0,0 +1,124 @@ +var React = require('react'); +var ReactPaginate = require('react-paginate'); + +var Nav = require('./nav'); +var PointsList = require('./settings'); +var Footer = require('./footer'); + +const per_page = 10; + + +var RepoPage = React.createClass({ + render: function() { + return ( +
    +
    + ) + } +}); + +var RepoList = React.createClass({ + getInitialState: function() { + return { + repos: [], + offset: 0, + pageNum: 1, + nextOffset: per_page, + previousOffset: 0 + } + }, + + getRepoList: function() { + var offset = this.state.offset; + var nextOffset = offset + per_page; + var previousOffset = (offset - per_page < 1) ? 0 : offset - per_page; + var url = '/api/v1/repository/?offset=' + offset + '&limit=' + per_page; + + $.get(url, function(res) { + this.setState({ + repos: res.objects, + nextOffset: nextOffset, + previousOffset: previousOffset, + pageNum: Math.ceil(res.meta.total_count / per_page )}) + }.bind(this)); + }, + + componentDidMount: function() { + if (this.props.username !== 'undefined' && this.isMounted()) { + this.getRepoList(); + + } else { + // Load demo page + this.setState({ + repos : {}, + offset : null, + pageNum : null + }); + } + }, + + handleClick: function(event) { + var pageSelected = event.selected; + + this.setState({ + offset: pageSelected * per_page + }, () => { + this.getRepoList(); + }); + }, + + render: function() { + return ( +
    +

    Your score

    + +

    Your contributions

    + { this.state.repos.map(function(repo, i) { + return () + }, this)} + + ...} + pageNum={this.state.pageNum} + marginPagesDisplayed={2} + pageRangeDisplayed={5} + clickCallback={this.handleClick} + containerClassName={"pagination"} + subContainerClassName={"pagination-pages"} + activeClassName={"active"} /> +
    + ); + } +}); + +var Repo = React.createClass({ + render: function() { + var repo_score = this.props.repo.watchers_count * PointsList.watch; + repo_score += this.props.repo.stargazers_count * PointsList.star; + + return ( +
    +
    +
    {repo_score}
    +
    Points
    +
    +
    + {this.props.repo.name} +
    +
    {this.props.repo.description}
    + +
    + ) + } +}) + +module.exports = RepoPage; diff --git a/components/settings.jsx b/components/settings.jsx new file mode 100644 index 0000000..bdc81df --- /dev/null +++ b/components/settings.jsx @@ -0,0 +1,22 @@ +var React = require('react'); + +var PointsList = React.createClass({ + statics: { + watch : 10, + star : 20 + }, + + render: function() { + return ( +
    +

    Points:

    + +
    + ); + } +}); + +module.exports = PointsList; diff --git a/static/images/generic_avatar.png b/static/images/generic_avatar.png new file mode 100644 index 0000000..e41ec14 Binary files /dev/null and b/static/images/generic_avatar.png differ