@@ -1,74 +1,154 @@
import React, { Component } from 'react'
import Affix from 'react-overlays/lib/Affix'
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";

const renameStatus = (status) => {
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';
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 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()
{
render() {
return (
<div>
{this.props.issues.map(issue =>
{this.props.issues.map(issue => (
<div key={issue.id} className="issueinfo">
<div>
<Affix container={this}>
<div className="issueinfo-header">
<h5>Projectname / <a href="#">TASK-{issue.id}</a></h5>
<div>
<div>
<h5>
Projectname /
{" "}
<Link to={`/task/${issue.id}`}>TASK-{issue.id}</Link>
</h5>
<p>Item creation</p>
</div>
</Affix>
<div>
<ul className="inline">
<li>
<h5>ID :</h5> <p>{issue.id}</p>
</li>
<li>
<h5>Name :</h5> <p>{issue.name}</p>
</li>
<li>
<h5>Issue Title :</h5> <p>{issue.title}</p>
</li>
<li>
<h5>Assignee :</h5> <p>{issue.assignee.name}</p>
</li>
<li>
<h5>Priority :</h5> <p>{issue.priority}</p>
</li>
<li>
<h5>Status :</h5> <p>{renameStatus(issue.status)}</p>
</li>
<li>
<h5>Created at :</h5> <p>{formatDate(issue.createdAt)}</p>
</li>
<li>
<h5>Updated at :</h5> <p>{formatDate(issue.updatedAt)}</p>
</li>
</ul>
</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
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 Affix from 'react-overlays/lib/Affix'
import { Scrollbars } from 'react-custom-scrollbars';
import Collapsible from 'react-collapsible';
import Issue2 from './Issue2'
import DropZone from './DropZone'
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 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 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>
}
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 (
constructor(props) {
super(props);
}
render() {
return (
<div>
{this.props.issues.map(issue => (
<div key={issue.id} className="issueinfo">
<div>
{this.props.issues.map(issue =>
<div key={issue.id} className="issueinfo">
<div>
<div>
<div>
<h5>Projectname / <a href="#">TASK-{issue.id}</a></h5>
<p>Item creation</p>
</div>
</div>
<Scrollbars style={{ height: 600, width: 380 }}>
<ul>
<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>
<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 => <Issue2 key={issue.id} {...issue} />)}
</Collapsible>
</ul>
</Scrollbars>
</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
export default ParentIssueInfo;
@@ -1,21 +1,21 @@
import React, { Component } from 'react'
import { selectIssue } from '../actions'
import React, { Component } from "react";
import { selectIssue } from "../actions";

class Test extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>AAAAAAAAAAAAAAAAAAA</p>
<p style={{visibility:'hidden'}}>dddddddddddddddddd</p>
<p>FFFFFFFFFFFFFFFFFFF</p>
<a href="jira">
AAAAAAAAAAAA</a>
</div>
);
}
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
export default Test;
@@ -8,14 +8,20 @@ export const ActionTypes = keyMirror({
FETCH_ISSUE_REQUEST: undefined,
FETCH_ISSUE_SUCCESS: undefined,
FETCH_ISSUE_FAILURE: undefined,
FETCH_SINGLE_ISSUE_REQUEST: undefined,
FETCH_SINGLE_ISSUE_SUCCESS: undefined,
FETCH_SINGLE_ISSUE_FAILURE: undefined,
CHANGE_ISSUE_STATUS: undefined,
USER_LOGIN_REQUEST: undefined,
USER_LOGIN_SUCCESS: undefined,
USER_LOGIN_FAILURE: undefined,
USER_LOGOUT_REQUEST: undefined,
USER_LOGOUT_SUCCESS: undefined,
USER_LOGOUT_FAILURE: undefined,
SET_REDIRECT_URL: undefined,
SHOW_ALERT: undefined,
HIDE_ALERT: undefined
HIDE_ALERT: undefined,
DELETE_ISSUE: undefined
});

/**
@@ -1,51 +1,40 @@
import { connect } from 'react-redux'
import AllIssues from '../components/AllIssues'
import assert from 'assert'
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;
}
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);
}
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;
}
})
return parentIssues;
}

//orther issue ko co thuoc tinh parent va ko nam trong parrentIssues
// const isContain = (issue, issues) => {
// var isContain = false;
// issues.forEach(i => {
// if (deepEqual(i, issue))
// isContain = true;
// })
// return isContain;
// }

const mapStateToProps = (state) => {
return {
parentIssues: getParentIssues(state.issue.issues),
// ortherIssues: state.issue.issues.filter(issue => !issue.hasOwnProperty('parent') && !isContain(issue, getParentIssues(state.issue.issues)))
onChangeGroupId: state.issue.onChangeGroupId,
isLoading: state.issue.isLoading
});
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)
export default connect(mapStateToProps)(AllIssues);
@@ -1,13 +1,36 @@
import React from 'react';
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 {
static propTypes = {
children: React.PropTypes.object.isRequired
};
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 this.props.children;
return (
<div>
<NavBar />
{this.props.children}
</div>
);
}
}

export default App;
function mapStateToProps(state) {
return {
isLoggedIn: state.user.isLoggedIn,
redirectUrl: state.app.redirectUrl
};
}
export default connect(mapStateToProps)(App);
@@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import React from "react";
import { connect } from "react-redux";

import Header from 'components/Header';
import Footer from 'components/Footer';
import SystemNotifications from 'components/SystemNotifications';
import Header from "components/Header";
import Footer from "components/Footer";
import SystemNotifications from "components/SystemNotifications";

export class AppPrivate extends React.Component {
constructor(props) {
@@ -1,6 +1,6 @@
import React from 'react';
import React from "react";

import SystemNotifications from 'components/SystemNotifications';
import SystemNotifications from "components/SystemNotifications";

export default class AppPublic extends React.Component {
static propTypes = {
@@ -18,4 +18,3 @@ export default class AppPublic extends React.Component {
);
}
}

@@ -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);
@@ -1,9 +1,10 @@
import React from 'react';
import Test from './Test'
import AllIssues from './AllIssues'
import IssueInfo from './IssueInfo'
import ParentIssueInfo from './ParentIssueInfo'
import CuteLoading from './CuteLoading'
import Test from './Test';
import AllIssues from './AllIssues';
import IssueInfo from './IssueInfo';
import ParentIssueInfo from './ParentIssueInfo';
import EmptyIssueInfo from '../components/EmptyIssueInfo';
import CuteLoading from './CuteLoading';

class Jira extends React.Component {
render() {
@@ -26,4 +27,4 @@ class Jira extends React.Component {
}
}

export default Jira;
export default Jira;
@@ -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 "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 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';
import store from "store";
import "../styles/main.scss";

function renderApp(RootComponent) {
const target = document.getElementById('react');
const target = document.getElementById("react");

/* istanbul ignore if */
if (target) {
ReactDOM.render(
<AppContainer>
<RootComponent store={store} history={syncHistoryWithStore(browserHistory, store)} />
</AppContainer>,
target
);
}
/* 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'))
);
module.hot.accept("containers/Root", () =>
renderApp(require("containers/Root"))
);
}
@@ -1,26 +1,20 @@
import { REHYDRATE } from 'redux-persist/constants';
import { createReducer } from 'utils/helpers';
import { REHYDRATE } from "redux-persist/constants";
import { createReducer } from "utils/helpers";

import { ActionTypes } from 'constants/index';
import { ActionTypes } from "constants/index";

export const appState = {
notifications: {
visible: false,
message: '',
status: '',
message: "",
status: "",
withTimeout: true
},
rehydrated: false
redirectUrl: "/dashboard"
};

export default {
app: createReducer(appState, {
[REHYDRATE](state, action) {
return Object.assign({}, state, action.payload.app, {
notifications: appState.notifications,
rehydrated: true
});
},
[ActionTypes.SHOW_ALERT](state, action) {
const notifications = {
...state.notifications,
@@ -32,6 +26,9 @@ export default {

return { ...state, notifications };
},
[ActionTypes.SET_REDIRECT_URL](state, action) {
return { ...state, redirectUrl: action.payload };
},
[ActionTypes.HIDE_ALERT](state) {
const notifications = {
...state.notifications,
@@ -1,36 +1,54 @@
import { REHYDRATE } from 'redux-persist/constants';
import { createReducer } from 'utils/helpers';
import { createReducer } from "utils/helpers";

import { ActionTypes } from 'constants/index';
import { ActionTypes } from "constants/index";

export const issueState = {
issues: [],
isLoading: false,
onChangeGroupId: null
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 };
},
[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 };
}
})
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 { REHYDRATE } from "redux-persist/constants";
import { createReducer } from "utils/helpers";

import { ActionTypes } from 'constants/index';
import { ActionTypes } from "constants/index";

export const userState = {
logged: false,
isLoggedIn: false,
rehydrated: false
};

export default {
user: createReducer(userState, {
[REHYDRATE](state, action) {
return Object.assign({}, state, action.payload.user, {
rehydrated: true
});
},
[ActionTypes.USER_LOGIN_SUCCESS](state) {
return { ...state, logged: true };
return { ...state, isLoggedIn: true };
},
[ActionTypes.USER_LOGOUT_SUCCESS](state) {
return { ...state, logged: false };
return { ...state, isLoggedIn: false };
},
["FAKE_LOGIN"](state) {
return { ...state, isLoggedIn: true };
}
})
};
@@ -8,19 +8,26 @@ import AppPrivate from 'containers/AppPrivate';

import Home from 'containers/Home';
import Logged from 'containers/Logged';
import TaskFullInfo from 'containers/TaskFullInfo';
import EnsureLoggedInContainer from 'containers/EnsureLoggedInContainer';
import Login from 'containers/Login';
import NotFound from 'containers/NotFound';

import Jira from 'containers/Jira';
import Test from 'containers/Test';
import store from 'store';
//import 'bootstrap/dist/bootstrap.min.css';

export default function createRoutes() {
return (
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="jira" component={Jira} />
<Route path="login" component={Login} />
<Route path="Test" component={Test} />
<Route path="*" component={NotFound} />
<Route path="/dashboard" component={Jira} />
<Route path="/task/:id" component={TaskFullInfo} />
</Route>
<Route path="*" component={NotFound} />
</Router>
);
}
@@ -1,28 +1,69 @@
import { takeEvery, delay } from 'redux-saga';
import { put, call, fork } from 'redux-saga/effects';
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 { create } from 'apisauce';
import { push } from 'react-router-redux';

const api = create({
baseURL: 'http://localhost:1337',
headers: { 'Accept': 'application/vnd.github.v3+json' }
})
headers: { Accept: 'application/vnd.github.v3+json' }
});

export function* fetchIssues() {
yield call(delay, 1000);
const response = yield call(api.get, '/issues')
const response = yield call(api.get, '/issues');
if (response.ok) {
yield put({ type: 'FETCH_ISSUE_SUCCESS', payload: response.data })
yield put({ type: 'FETCH_ISSUE_SUCCESS', payload: response.data });
} else {
yield put({ type: 'FETCH_ISSUE_FAILURE' })
yield put({ type: 'FETCH_ISSUE_FAILURE' });
}
}

function* watchFetchIssues() {
yield* takeEvery(ActionTypes.FETCH_ISSUE_REQUEST, fetchIssues);
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 fork(watchFetchIssues);
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,17 +1,21 @@
import { applyMiddleware, createStore, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import { browserHistory } from 'react-router';
import { routerMiddleware, routerReducer } from 'react-router-redux';
import { applyMiddleware, createStore, combineReducers } from "redux";
import thunk from "redux-thunk";
import createSagaMiddleware from "redux-saga";
import { browserHistory } from "react-router";
import { routerMiddleware, routerReducer } from "react-router-redux";

import rootSagas from 'sagas';
import rootReducer from 'reducers';
import rootSagas from "sagas";
import rootReducer from "reducers";

const reducer = combineReducers({ ...rootReducer, routing: routerReducer });
const sagaMiddleware = createSagaMiddleware();

export default (initialState = {}) => {
const createStoreWithMiddleware = applyMiddleware(thunk, sagaMiddleware, routerMiddleware(browserHistory))(createStore);
const createStoreWithMiddleware = applyMiddleware(
thunk,
sagaMiddleware,
routerMiddleware(browserHistory)
)(createStore);
const store = createStoreWithMiddleware(reducer, initialState);
sagaMiddleware.run(rootSagas);

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,5 +1,5 @@
.dropzone{
width: 300px;
width: 100%;
height: 100px;
border: 2px dashed black;
}

This file was deleted.

This file was deleted.

@@ -1,4 +1,4 @@
.issuecard{
.issuecard {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
@@ -8,7 +8,7 @@
margin: 6px auto;
}

.issuecard2{
.issuecard2 {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
@@ -17,48 +17,51 @@
background: white;
margin-left: 20px;
}
.issueTitle{
margin-top:5px;
margin-left: 10px;
}

.avatarImage{
width:40px;
height:40px;
object-fit: fill;
border-radius: 50%;
}

.priority{
margin-top: 10px;
margin-left: 10px;
}
.idavatar{
margin-right: 10px;
margin-bottom: 10px;
.avatar {
.avatarImage{
width:20px;
height:20px;
object-fit: fill;
border-radius: 50%;
}
}
}
.idstatus{
margin-left: 10px;
margin-right: 40%;
.issueTitle {
margin-top: 5px;
margin-left: 10px;
}

.avatarImage {
width: 40px;
height: 40px;
object-fit: fill;
border-radius: 50%;
}

.priority {
margin-top: 10px;
margin-left: 10px;
}

.idavatar {
margin-right: 10px;
margin-bottom: 10px;
.avatar {
.avatarImage {
width: 20px;
height: 20px;
object-fit: fill;
border-radius: 50%;
}
}
}

.idstatus {
margin-left: 10px;
margin-right: 40%;
}

.idstatusavatar{
.avatar {
margin-top:10px;
.idstatusavatar {
.avatar {
margin-top: 10px;
margin-right: 10px;
.avatarImage{
width:30px;
height:30px;
object-fit: fill;
border-radius: 50%;
.avatarImage {
width: 30px;
height: 30px;
object-fit: fill;
border-radius: 50%;
}
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

@@ -1,34 +1,3 @@
// *** Vendor
@import '~sass-mediaqueries/media-queries';

// *** Base
@import 'base/variables';
@import 'base/icons';
@import 'base/typography';

// *** Utilities
@import 'utilities/functions';
@import 'utilities/mixins';
@import 'utilities/grid';

// *** Vendor
@import 'vendor/bootstrap';
@import 'vendor/bootstrap-theme';

// *** Layout
@import 'layout/global';
@import 'layout/app';

// *** Components
@import 'components/footer';
@import 'components/header';
@import 'components/loader';
@import 'components/logo';
@import 'components/system-notifications';
@import 'components/issue';
@import 'components/dropzone';

// *** Containers
@import 'containers/home';
@import 'containers/logged';
@import 'containers/not-found';
@import 'components/dropzone';
@@ -44,4 +44,4 @@
// @import '~bootstrap/scss/carousel';

// Utility classes
@import '~bootstrap/scss/utilities';
@import '~bootstrap/scss/utilities';
@@ -27,7 +27,8 @@ var config = {
},
devtool: '#inline-source-map',
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// new webpack.NoErrorsPlugin(),
new webpack.LoaderOptionsPlugin({
options: {
context: '/',
@@ -39,7 +39,7 @@ if (args[0] && args[0] === 'test:ui') {
} else {
envPlugin = new BrowserSyncPlugin({
host: getIPAddress(),
port: 3000,
port: 9000,
notify: true,
logPrefix: 'sia',
proxy: 'http://localhost:3030'
@@ -52,7 +52,7 @@ var config = merge.smart(webpackConfig, {
cache: true,
output: {
filename: '[name].js',
publicPath: 'http://localhost:' + (args[0] === 'test:ui' ? 3030 : 3000) + '/'
publicPath: 'http://localhost:' + (args[0] === 'test:ui' ? 3030 : 9000) + '/'
},
entry: {
bundle: [
@@ -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
@@ -25,7 +25,7 @@
"modernizr": "^3.3",
"moment": "^2.16",
"react": "^15.3",
"react-bootstrap": "^0.30.7",
"react-bootstrap": "^0.30.10",
"react-collapsible": "^1.3.0",
"react-custom-scrollbars": "^4.0.2",
"react-dom": "^15.3",
@@ -125,7 +125,7 @@
},
"scripts": {
"start": "node config/webpack.server.js",
"build": "NODE_ENV=production webpack --config config/webpack.prod.js",
"build": "SET NODE_ENV=production & webpack --config config/webpack.prod.js",
"build:pages": "NODE_ENV=production webpack --config config/webpack.gh-pages.js",
"analyze": "NODE_ENV=production webpack --config config/webpack.prod.js --profile --json > webpack.stats.json",
"lint": "eslint --ext .js --ext .jsx app/scripts config test",
@@ -146,6 +146,7 @@
},
"jest": {
"transform": {

".*": "<rootDir>/node_modules/babel-jest"
},
"moduleFileExtensions": [