| @@ -1,74 +1,154 @@ | ||
| import React, { Component } from "react"; | ||
| import { Scrollbars } from "react-custom-scrollbars"; | ||
| import Collapsible from "react-collapsible"; | ||
| import Issue2 from "./Issue2"; | ||
| import DropZone from "./DropZone"; | ||
| import { Button } from "react-bootstrap"; | ||
|
|
||
| import { Link } from "react-router"; | ||
| import { push } from "react-router-redux"; | ||
|
|
||
| const renameStatus = status => { | ||
| switch (status) { | ||
| case "new": | ||
| return "New"; | ||
| case "inProgress": | ||
| return "In progress"; | ||
| case "done": | ||
| return "Done"; | ||
| default: | ||
| return status; | ||
| } | ||
| }; | ||
|
|
||
| const formatDate = date => { | ||
| return new Date(date).toLocaleString(); | ||
| }; | ||
|
|
||
| const arrows = { | ||
| Hightest: <p style={{ color: "red" }}><span><b>↑</b> </span>Hightest</p>, | ||
| Hight: <p style={{ color: "orange" }}><span><b>↗</b> </span>Hight</p>, | ||
| Low: <p style={{ color: "blue" }}><span><b>→</b></span> Low</p>, | ||
| Lowest: <p style={{ color: "black" }}><span><b>↓</b></span> Lowest</p> | ||
| }; | ||
|
|
||
| class IssueInfo extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| render() { | ||
| return ( | ||
| <div> | ||
| {this.props.issues.map(issue => ( | ||
| <div key={issue.id} className="issueinfo"> | ||
| <div> | ||
| <div> | ||
| <div> | ||
| <h5> | ||
| Projectname / | ||
| {" "} | ||
| <Link to={`/task/${issue.id}`}>TASK-{issue.id}</Link> | ||
| </h5> | ||
| <p>Item creation</p> | ||
| </div> | ||
| </div> | ||
| <Scrollbars autoHide style={{ height: 830, width: 500 }}> | ||
| <ul style={{ margin: "0 20px" }}> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Title" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Status: </p> | ||
| <p>Priority: </p> | ||
| <p>Component/s: </p> | ||
| <p>Labels: </p> | ||
| <p>Affects Version/s: </p> | ||
| <p>Fix Version/s: </p> | ||
| <p>Epic link: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{renameStatus(issue.status)}</p> | ||
| {arrows[issue.priority]} | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 People" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Reporter: </p> | ||
| <p>Assignee: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>Not code yet</p> | ||
| <p> | ||
| <img | ||
| className="avatarImage" | ||
| src={ | ||
| issue.assignee | ||
| ? issue.assignee.avatarURL | ||
| : "https://scontent.fhan2-2.fna.fbcdn.net/v/t1.0-9/11224574_1178319498849092_1271740388789995686_n.jpg?oh=a2a319a10cf4e95454e595ce0e962a19&oe=592DE2F6" | ||
| } | ||
| alt={issue.assignee.name} | ||
| /> | ||
| {issue.assignee.name} | ||
| </p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Dates" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Created date: </p> | ||
| <p>Updated date: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{formatDate(issue.createdAt)}</p> | ||
| <p>{formatDate(issue.updatedAt)}</p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Description" />} | ||
| > | ||
| <div className="name" /> | ||
| <div className="value"> | ||
| <p>Item creation feature </p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Comments" />} | ||
| > | ||
| <div className="value"> | ||
| {issue.comment | ||
| ? issue.comment | ||
| : "There are no comments yet on this issue"} | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Attachments" />} | ||
| > | ||
| <div> | ||
| <DropZone /> | ||
| </div> | ||
| </Collapsible> | ||
| </ul> | ||
| </Scrollbars> | ||
| </div> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export default IssueInfo; |
| @@ -0,0 +1,73 @@ | ||
| import React, { Component } from 'react'; | ||
| import { Navbar, Nav, NavItem, MenuItem, NavDropdown } from 'react-bootstrap'; | ||
| import { Link } from 'react-router'; | ||
| import { push } from 'react-router-redux'; | ||
| import { connect } from 'react-redux'; | ||
| import { requestIssues } from '../actions'; | ||
| import { ActionTypes } from '../constants/index'; | ||
|
|
||
| class NavBar extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
|
|
||
| onClickLogo = e => { | ||
| e.preventDefault(); | ||
| if (this.props.isLoggedIn) { | ||
| window.location.reload(); | ||
| } else { | ||
| this.props.dispatch(push(e.currentTarget.getAttribute('href'))); | ||
| } | ||
| }; | ||
| logout = e => { | ||
| localStorage.setItem('isLoggedIn', false); | ||
| this.props.dispatch({ type: ActionTypes.USER_LOGOUT_SUCCESS }); | ||
| }; | ||
| render() { | ||
| return ( | ||
| <Navbar inverse collapseOnSelect> | ||
| <Navbar.Header> | ||
| <Navbar.Brand> | ||
| <a href="/dashboard" onClick={this.onClickLogo}>REACT JIRA</a> | ||
| </Navbar.Brand> | ||
| <Navbar.Toggle /> | ||
| </Navbar.Header> | ||
| <Navbar.Collapse> | ||
| {/*<Nav> | ||
| <NavItem eventKey={1} href="#">Link</NavItem> | ||
| <NavItem eventKey={2} href="#">Link</NavItem> | ||
| <NavDropdown eventKey={3} title="Dropdown" id="basic-nav-dropdown"> | ||
| <MenuItem eventKey={3.1}>Action</MenuItem> | ||
| <MenuItem eventKey={3.2}>Another action</MenuItem> | ||
| <MenuItem eventKey={3.3}>Something else here</MenuItem> | ||
| <MenuItem divider /> | ||
| <MenuItem eventKey={3.3}>Separated link</MenuItem> | ||
| </NavDropdown> | ||
| </Nav>*/} | ||
| <Nav pullRight> | ||
| <NavItem> | ||
| {this.props.isLoggedIn | ||
| ? <p> | ||
| Hello user, | ||
| {' '} | ||
| <Link onClick={this.logout} to="/login"> | ||
| click hear to logout | ||
| </Link> | ||
| </p> | ||
| : <Link to="/login">Log in</Link>} | ||
| </NavItem> | ||
| </Nav> | ||
| </Navbar.Collapse> | ||
| </Navbar> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const mapStateToProps = (state, ownProps) => { | ||
| return { | ||
| isLoggedIn: state.user.isLoggedIn, | ||
| username: state.user.username | ||
| }; | ||
| }; | ||
|
|
||
| export default connect(mapStateToProps)(NavBar); |
| @@ -1,127 +1,171 @@ | ||
| import React, { Component } from "react"; | ||
| import { Scrollbars } from "react-custom-scrollbars"; | ||
| import Collapsible from "react-collapsible"; | ||
| import Issue2 from "./Issue2"; | ||
| import DropZone from "./DropZone"; | ||
| import { Link } from "react-router"; | ||
| import { push } from "react-router-redux"; | ||
|
|
||
| const renameStatus = status => { | ||
| switch (status) { | ||
| case "new": | ||
| return "New"; | ||
| case "inProgress": | ||
| return "In progress"; | ||
| case "done": | ||
| return "Done"; | ||
| default: | ||
| return status; | ||
| } | ||
| }; | ||
|
|
||
| const formatDate = date => { | ||
| return new Date(date).toLocaleString(); | ||
| }; | ||
|
|
||
| const arrows = { | ||
| Hightest: <p style={{ color: "red" }}><span><b>↑</b> </span>Hightest</p>, | ||
| Hight: <p style={{ color: "orange" }}><span><b>↗</b> </span>Hight</p>, | ||
| Low: <p style={{ color: "blue" }}><span><b>→</b></span> Low</p>, | ||
| Lowest: <p style={{ color: "black" }}><span><b>↓</b></span> Lowest</p> | ||
| }; | ||
|
|
||
| class ParentIssueInfo extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| render() { | ||
| return ( | ||
| <div> | ||
| {this.props.issues.map(issue => ( | ||
| <div key={issue.id} className="issueinfo"> | ||
| <div> | ||
| <div> | ||
| <div> | ||
| <h5> | ||
| Projectname / | ||
| {" "} | ||
| <Link to={`/task/${issue.id}`}>TASK-{issue.id}</Link> | ||
| </h5> | ||
| <p>Item creation</p> | ||
| </div> | ||
| </div> | ||
| <Scrollbars autoHide style={{ height: 830, width: 500 }}> | ||
| <ul style={{ margin: "0 20px" }}> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Title" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Status: </p> | ||
| <p>Priority: </p> | ||
| <p>Component/s: </p> | ||
| <p>Labels: </p> | ||
| <p>Affects Version/s: </p> | ||
| <p>Fix Version/s: </p> | ||
| <p>Epic link: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{renameStatus(issue.status)}</p> | ||
| {arrows[issue.priority]} | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 People" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Reporter: </p> | ||
| <p>Assignee: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>Not code yet</p> | ||
| <p> | ||
| <img | ||
| className="avatarImage" | ||
| src={ | ||
| issue.assignee | ||
| ? issue.assignee.avatarURL | ||
| : "https://scontent.fhan2-2.fna.fbcdn.net/v/t1.0-9/11224574_1178319498849092_1271740388789995686_n.jpg?oh=a2a319a10cf4e95454e595ce0e962a19&oe=592DE2F6" | ||
| } | ||
| alt={issue.assignee.name} | ||
| /> | ||
| {issue.assignee.name} | ||
| </p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Dates" />} | ||
| > | ||
| <div className="name"> | ||
| <p>Created date: </p> | ||
| <p>Updated date: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{formatDate(issue.createdAt)}</p> | ||
| <p>{formatDate(issue.updatedAt)}</p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Description" />} | ||
| > | ||
| <div className="name" /> | ||
| <div className="value"> | ||
| <p>Item creation feature </p> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Comments" />} | ||
| > | ||
| <div className="value"> | ||
| {issue.comment | ||
| ? issue.comment | ||
| : "There are no comments yet on this issue"} | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Attachments" />} | ||
| > | ||
| <div> | ||
| <DropZone /> | ||
| </div> | ||
| </Collapsible> | ||
| <Collapsible | ||
| open={true} | ||
| trigger={<hr className="style1 Sub-tasks" />} | ||
| > | ||
| {this.props.childIssues.map( | ||
| ( | ||
| issue // <Link to={`/task/${issue.id}`}> | ||
| ) => ( | ||
| <Issue2 | ||
| onClick={() => { | ||
| this.props.dispatch(push(`/task/${issue.id}`)); | ||
| }} | ||
| key={issue.id} | ||
| {...issue} | ||
| /> | ||
| ) | ||
| // </Link> | ||
| )} | ||
| </Collapsible> | ||
| </ul> | ||
| </Scrollbars> | ||
| </div> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export default ParentIssueInfo; |
| @@ -1,21 +1,21 @@ | ||
| import React, { Component } from "react"; | ||
| import { selectIssue } from "../actions"; | ||
|
|
||
| class Test extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| buttonLogger = () => { | ||
| console.log("wtf" + this.props.route); | ||
| }; | ||
| render() { | ||
| return ( | ||
| <div> | ||
| <p>{this.props.params.id}</p> | ||
| <button onClick={this.buttonLogger}>Click me please</button> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export default Test; |
| @@ -1,51 +1,40 @@ | ||
| import { connect } from 'react-redux'; | ||
| import AllIssues from '../components/AllIssues'; | ||
| import assert from 'assert'; | ||
|
|
||
| const deepEqual = (object1, object2) => { | ||
| try { | ||
| assert.deepEqual(object1, object2); | ||
| } catch (err) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }; | ||
|
|
||
| const getParentIssues = issues => { | ||
| const parentIssues = []; | ||
| issues.forEach(issue => { | ||
| if (issue.hasOwnProperty('parent')) { | ||
| let unique = true; | ||
| parentIssues.map(i => { | ||
| if (deepEqual(i, issue.parent)) { | ||
| unique = false; | ||
| } | ||
| }); | ||
| if (unique) { | ||
| parentIssues.push(issue.parent); | ||
| } | ||
| } | ||
| }); | ||
| return parentIssues; | ||
| }; | ||
|
|
||
| const mapStateToProps = state => { | ||
| return { | ||
| parentIssues: getParentIssues(state.issue.issues), | ||
| onChangeGroupId: state.issue.onChangeGroupId, | ||
| isLoading: state.issue.isLoading | ||
| }; | ||
| }; | ||
|
|
||
| export default connect(mapStateToProps)(AllIssues); |
| @@ -1,13 +1,36 @@ | ||
| import React from "react"; | ||
| import NavBar from "../components/NavBar"; | ||
| import { connect } from "react-redux"; | ||
| import { push } from "react-router-redux"; | ||
|
|
||
| class App extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| componentDidUpdate(prevProps) { | ||
| const { dispatch, redirectUrl } = this.props; | ||
| const isLoggingOut = prevProps.isLoggedIn && !this.props.isLoggedIn; | ||
| const isLoggingIn = !prevProps.isLoggedIn && this.props.isLoggedIn; | ||
|
|
||
| if (isLoggingIn) { | ||
| dispatch(push(redirectUrl)); | ||
| } else if (isLoggingOut) { | ||
| // do any kind of cleanup or post-logout redirection here | ||
| } | ||
| } | ||
| render() { | ||
| return ( | ||
| <div> | ||
| <NavBar /> | ||
| {this.props.children} | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
| function mapStateToProps(state) { | ||
| return { | ||
| isLoggedIn: state.user.isLoggedIn, | ||
| redirectUrl: state.app.redirectUrl | ||
| }; | ||
| } | ||
| export default connect(mapStateToProps)(App); |
| @@ -0,0 +1,29 @@ | ||
| import React, { Component } from 'react'; | ||
| import { connect } from 'react-redux'; | ||
| import { push } from 'react-router-redux'; | ||
| import { setRedirectUrl } from '../actions'; | ||
|
|
||
| class EnsureLoggedInContainer extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| componentDidMount() { | ||
| const { dispatch, location } = this.props; | ||
|
|
||
| if (localStorage.isLoggedIn == false) { | ||
| dispatch(setRedirectUrl(location.pathname)); | ||
| dispatch(push('/login')); | ||
| } | ||
| } | ||
|
|
||
| render() { | ||
| return localStorage.isLoggedIn == true ? this.props.children : null; | ||
| } | ||
| } | ||
|
|
||
| const mapStateToProps = (state, ownProps) => { | ||
| return { | ||
| isLoggedIn: state.user.isLoggedIn | ||
| }; | ||
| }; | ||
| export default connect(mapStateToProps)(EnsureLoggedInContainer); |
| @@ -0,0 +1,78 @@ | ||
| import React from 'react'; | ||
| import { ActionTypes } from '../constants/index'; | ||
| import { connect } from 'react-redux'; | ||
| const Login = React.createClass({ | ||
| propTypes: { | ||
| options: React.PropTypes.object, | ||
| onChange: React.PropTypes.func, | ||
| onSubmit: React.PropTypes.func | ||
| }, | ||
| onChange(e) { | ||
| if (this.props.onChange) { | ||
| this.props.onChange(e.target, e.target.value, e); | ||
| } | ||
| }, | ||
| onSubmit(e) { | ||
| localStorage.setItem('isLoggedIn', true); | ||
| this.props.dispatch({ type: ActionTypes.USER_LOGIN_SUCCESS }); | ||
| }, | ||
| render() { | ||
| let options = { | ||
| email: { | ||
| label: 'Email ddress', | ||
| placeholder: 'Email' | ||
| }, | ||
| password: { | ||
| label: 'Password', | ||
| placeholder: 'Password' | ||
| }, | ||
| checkbox: { | ||
| text: 'Check me out' | ||
| }, | ||
| submitButton: { | ||
| text: 'Submit' | ||
| } | ||
| }; | ||
| options = Object.assign(options, this.props.options || {}); | ||
| return ( | ||
| <div> | ||
| <form> | ||
| <div className="form-group"> | ||
| <label>{options.email.label}</label> | ||
| <input | ||
| type="email" | ||
| onChange={this.onChange} | ||
| className="form-control" | ||
| placeholder={options.email.placeholder} | ||
| /> | ||
| </div> | ||
| <div className="form-group"> | ||
| <label>{options.password.label}</label> | ||
| <input | ||
| type="password" | ||
| onChange={this.onChange} | ||
| className="form-control" | ||
| placeholder={options.password.placeholder} | ||
| /> | ||
| </div> | ||
| <div className="checkbox"> | ||
| <label> | ||
| <input type="checkbox" onChange={this.onChange} /> | ||
| {' '} | ||
| {options.checkbox.text} | ||
| </label> | ||
| </div> | ||
| <button | ||
| type="submit" | ||
| onClick={this.onSubmit} | ||
| className="btn btn-default" | ||
| > | ||
| {options.submitButton.text} | ||
| </button> | ||
| </form> | ||
| </div> | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| export default connect()(Login); |
| @@ -0,0 +1,107 @@ | ||
| import React, { Component } from 'react'; | ||
| import { connect } from 'react-redux'; | ||
| import { requestSingleIssue } from '../actions'; | ||
| import DropZone from '../components/DropZone'; | ||
| import { ActionTypes } from 'constants/index'; | ||
| import { push } from 'react-router-redux'; | ||
|
|
||
| const renameStatus = status => { | ||
| switch (status) { | ||
| case 'new': | ||
| return 'New'; | ||
| case 'inProgress': | ||
| return 'In progress'; | ||
| case 'done': | ||
| return 'Done'; | ||
| default: | ||
| return status; | ||
| } | ||
| }; | ||
|
|
||
| const formatDate = date => { | ||
| return new Date(date).toLocaleString(); | ||
| }; | ||
|
|
||
| class TaskFullInfo extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| } | ||
| componentDidMount() { | ||
| this.props.dispatch(requestSingleIssue(this.props.params.id)); | ||
| } | ||
|
|
||
| deleteTask = e => { | ||
| if (confirm('Delete this task')) { | ||
| this.props.dispatch({ | ||
| type: ActionTypes.DELETE_ISSUE, | ||
| payload: this.props.params.id | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| render() { | ||
| const { issue } = this.props; | ||
|
|
||
| return Object.keys(issue).length > 0 | ||
| ? <div style={{ margin: '0 20px' }}> | ||
| <h3>TASK DETAIL</h3> | ||
| <div className="sup"> | ||
| <div style={{ flex: 2, minWidth: '500px' }}> | ||
| <hr className="style1 Details" /> | ||
| <div style={{ display: 'flex' }}> | ||
| <div className="name" style={{ marginRight: 20 }}> | ||
| <p>Status: </p> | ||
| <p>Priority: </p> | ||
| <p>Component/s: </p> | ||
| <p>Labels: </p> | ||
| <p>Affects Version/s: </p> | ||
| <p>Fix Version/s: </p> | ||
| <p>Epic link: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{renameStatus(issue.status)}</p> | ||
| <p>{issue.priority}</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| <p>None</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div style={{ flex: 1, minWidth: '250px' }}> | ||
| <hr className="style1 People" /> | ||
| <div style={{ display: 'flex' }}> | ||
| <div className="name" style={{ marginRight: 20 }}> | ||
| <p>Reporter: </p> | ||
| <p>Assignee: </p> | ||
| </div> | ||
| <div className="value"> | ||
| <p>{issue.reporter ? issue.reporter.name : 'None'}</p> | ||
| <p>{issue.assignee ? issue.assignee.name : 'None'}</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div> | ||
| <hr className="style1 Description" /> | ||
| <p>{issue.description ? issue.description : 'None'}</p> | ||
| </div> | ||
| <div> | ||
| <hr className="style1 Attachments" /> | ||
| <DropZone /> | ||
| </div> | ||
| <button onClick={this.deleteTask} className="btn btn-danger"> | ||
| Delete this task | ||
| </button> | ||
| </div> | ||
| : <p>NOT FOUND THIS ISSUE</p>; | ||
| } | ||
| } | ||
|
|
||
| const mapStateToProps = (state, ownProps) => { | ||
| return { | ||
| issue: state.issue.issue | ||
| }; | ||
| }; | ||
| export default connect(mapStateToProps)(TaskFullInfo); |
| @@ -1,39 +1,41 @@ | ||
| // Polyfills | ||
| import "core-js/shim"; | ||
| import "isomorphic-fetch"; | ||
| import "classlist-polyfill"; | ||
| import "vendor/polyfills"; | ||
|
|
||
| import React from "react"; | ||
| import ReactDOM from "react-dom"; | ||
| import Root from "containers/Root"; | ||
| import { browserHistory } from "react-router"; | ||
| import { AppContainer } from "react-hot-loader"; | ||
| import { syncHistoryWithStore } from "react-router-redux"; | ||
|
|
||
| import store from "store"; | ||
| import "../styles/main.scss"; | ||
|
|
||
| function renderApp(RootComponent) { | ||
| const target = document.getElementById("react"); | ||
|
|
||
| /* istanbul ignore if */ | ||
| if (target) { | ||
| ReactDOM.render( | ||
| <AppContainer> | ||
| <RootComponent | ||
| store={store} | ||
| history={syncHistoryWithStore(browserHistory, store)} | ||
| /> | ||
| </AppContainer>, | ||
| target | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| renderApp(Root); | ||
|
|
||
| /* istanbul ignore next */ | ||
| if (module.hot) { | ||
| module.hot.accept("containers/Root", () => | ||
| renderApp(require("containers/Root")) | ||
| ); | ||
| } |
| @@ -1,36 +1,54 @@ | ||
| import { createReducer } from "utils/helpers"; | ||
|
|
||
| import { ActionTypes } from "constants/index"; | ||
|
|
||
| export const issueState = { | ||
| issues: [], | ||
| isLoading: false, | ||
| isLoadingSuccess: true, | ||
| onChangeGroupId: null, | ||
| issue: {} | ||
| }; | ||
|
|
||
| export default { | ||
| issue: createReducer(issueState, { | ||
| [ActionTypes.FETCH_ISSUE_REQUEST](state) { | ||
| return { ...state, isLoading: true }; | ||
| }, | ||
| [ActionTypes.FETCH_ISSUE_SUCCESS](state, action) { | ||
| return { ...state, issues: action.payload, isLoading: false }; | ||
| }, | ||
| [ActionTypes.FETCH_ISSUE_FAILURE](state) { | ||
| return { ...state, isLoading: false, isLoadingSuccess: false }; | ||
| }, | ||
| [ActionTypes.FETCH_SINGLE_ISSUE_REQUEST](state) { | ||
| return { ...state, isLoading: true }; | ||
| }, | ||
| [ActionTypes.FETCH_SINGLE_ISSUE_SUCCESS](state, action) { | ||
| return { ...state, issue: action.payload, isLoading: false }; | ||
| }, | ||
| [ActionTypes.FETCH_SINGLE_ISSUE_FAILURE](state) { | ||
| return { ...state, isLoading: false, isLoadingSuccess: false }; | ||
| }, | ||
| [ActionTypes.SELECT_ISSUE](state, action) { | ||
| return { | ||
| ...state, | ||
| selectedIssue: action.payload, | ||
| selectedParentIssue: null | ||
| }; | ||
| }, | ||
| [ActionTypes.SELECT_PARENT_ISSUE](state, action) { | ||
| return { | ||
| ...state, | ||
| selectedParentIssue: action.payload, | ||
| selectedIssue: null | ||
| }; | ||
| }, | ||
| [ActionTypes.ON_DRAG](state, action) { | ||
| return { ...state, onChangeGroupId: action.payload }; | ||
| }, | ||
| [ActionTypes.END_DRAG](state) { | ||
| return { ...state, onChangeGroupId: null }; | ||
| } | ||
| }) | ||
| }; |
| @@ -1,25 +1,23 @@ | ||
| import { REHYDRATE } from "redux-persist/constants"; | ||
| import { createReducer } from "utils/helpers"; | ||
|
|
||
| import { ActionTypes } from "constants/index"; | ||
|
|
||
| export const userState = { | ||
| isLoggedIn: false, | ||
| rehydrated: false | ||
| }; | ||
|
|
||
| export default { | ||
| user: createReducer(userState, { | ||
| [ActionTypes.USER_LOGIN_SUCCESS](state) { | ||
| return { ...state, isLoggedIn: true }; | ||
| }, | ||
| [ActionTypes.USER_LOGOUT_SUCCESS](state) { | ||
| return { ...state, isLoggedIn: false }; | ||
| }, | ||
| ["FAKE_LOGIN"](state) { | ||
| return { ...state, isLoggedIn: true }; | ||
| } | ||
| }) | ||
| }; |
| @@ -1,28 +1,69 @@ | ||
| import { takeEvery, takeLatest, delay } from 'redux-saga'; | ||
| import { fork, take, call, put, cancel, select } from 'redux-saga/effects'; | ||
|
|
||
| import { ActionTypes } from 'constants/index'; | ||
| import { create } from 'apisauce'; | ||
| import { push } from 'react-router-redux'; | ||
|
|
||
| const api = create({ | ||
| baseURL: 'http://localhost:1337', | ||
| headers: { Accept: 'application/vnd.github.v3+json' } | ||
| }); | ||
|
|
||
| export function* fetchIssues() { | ||
| yield call(delay, 1000); | ||
| const response = yield call(api.get, '/issues'); | ||
| if (response.ok) { | ||
| yield put({ type: 'FETCH_ISSUE_SUCCESS', payload: response.data }); | ||
| } else { | ||
| yield put({ type: 'FETCH_ISSUE_FAILURE' }); | ||
| } | ||
| } | ||
|
|
||
| export function* fetchSingleIssue(action) { | ||
| yield call(delay, 1000); | ||
| const response = yield call(api.get, '/issues/' + action.payload); | ||
| yield call(console.log, response); | ||
| if (response.ok) { | ||
| yield put({ type: 'FETCH_SINGLE_ISSUE_SUCCESS', payload: response.data }); | ||
| } else { | ||
| yield put({ type: 'FETCH_SINGLE_ISSUE_FAILURE' }); | ||
| } | ||
| } | ||
|
|
||
| export function* changeStatus(action) { | ||
| try { | ||
| const response = yield call(api.put, `/issues/${action.id}`, { | ||
| status: `${action.status}` | ||
| }); | ||
| if (response.ok) { | ||
| yield call(console.log, 'change thanh con'); | ||
| } | ||
| } catch (e) { | ||
| yield call(console.log, 'Change status have an error'); | ||
| } | ||
| const changestatus = (id, tostatus) => { | ||
| api | ||
| .put(`/issues/${id}`, { status: `${tostatus}` }) | ||
| .then(rs => console.log(rs)); | ||
| }; | ||
| } | ||
|
|
||
| export function* deleteStatus(action) { | ||
| const response = yield call(api.delete, '/issues/' + action.payload); | ||
| if (response.ok) { | ||
| yield call(alert, 'Xoa thanh cong'); | ||
| yield put(push('/dashboard')); | ||
| } else { | ||
| yield call(alert, 'Xoa that bai'); | ||
| } | ||
| } | ||
|
|
||
| export default function* app() { | ||
| yield [ | ||
| takeEvery(ActionTypes.FETCH_SINGLE_ISSUE_REQUEST, fetchSingleIssue), | ||
| takeLatest(ActionTypes.FETCH_ISSUE_REQUEST, fetchIssues), | ||
| takeEvery(ActionTypes.CHANGE_ISSUE_STATUS, changeStatus), | ||
| takeLatest(ActionTypes.DELETE_ISSUE, deleteStatus) | ||
| ]; | ||
| } |
| @@ -1,5 +1,5 @@ | ||
| .dropzone{ | ||
| width: 100%; | ||
| height: 100px; | ||
| border: 2px dashed black; | ||
| } | ||
| @@ -1,34 +1,3 @@ | ||
| @import 'layout/global'; | ||
| @import 'components/issue'; | ||
| @import 'components/dropzone'; |
| @@ -44,4 +44,4 @@ | ||
| // @import '~bootstrap/scss/carousel'; | ||
|
|
||
| // Utility classes | ||
| @import '~bootstrap/scss/utilities'; | ||
| @@ -0,0 +1,2 @@ | ||
| [0315/003103:ERROR:tcp_listen_socket.cc(76)] Could not bind socket to 127.0.0.1:6004 | ||
| [0315/003103:ERROR:node_debugger.cc(86)] Cannot start debugger server |