Skip to content

Commit dbf3b48

Browse files
authored
Merge pull request #6 from lucassabreu/trello
Trello
2 parents 1929390 + 906edb5 commit dbf3b48

File tree

9 files changed

+252
-9
lines changed

9 files changed

+252
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88

9+
## [0.1.4] - 2018-02-25
10+
11+
### Added
12+
- Support for custom labels at the component
13+
- Trello boards setup
14+
915
## [0.1.3] - 2018-02-25
1016

1117
### Fixed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"babel-runtime": "6.26.0",
1313
"case-sensitive-paths-webpack-plugin": "2.1.1",
1414
"chalk": "1.1.3",
15+
"colour-proximity": "^0.0.2",
1516
"css-loader": "0.28.7",
1617
"dotenv": "4.0.0",
1718
"dotenv-expand": "4.0.1",

src/App.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Home from './Home'
77
import Footer from './Footer';
88
import GitHub from './Origins/GitHub';
99
import GitLab from './Origins/GitLab';
10+
import Trello from './Origins/Trello';
1011

1112
const App = () => (
1213
<div className="CWSPApp">
@@ -21,6 +22,7 @@ const App = () => (
2122
<Route exact path="/" component={Home} />
2223
<Route exact path="/github" component={GitHub} />
2324
<Route exact path="/gitlab" component={GitLab} />
25+
<Route exact path="/trello" component={Trello} />
2426
</Switch>
2527
</div>
2628
<Footer />

src/Components/ProjectListApplyer.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const ProjectListApplyer = ({
3535
applyedLabels,
3636
applyingStatus,
3737
onApply,
38+
labelsToAdd,
3839
labelsToRemove,
3940
alert
4041
}) => (
@@ -61,7 +62,7 @@ const ProjectListApplyer = ({
6162
<LabelList
6263
header="Labels to Add:"
6364
className="labels-to-add"
64-
labels={LABELS_TO_ADD}
65+
labels={labelsToAdd}
6566
applyedLabels={applyedLabels}
6667
/>
6768
{labelsToRemove.length === 0 ? null :
@@ -76,6 +77,11 @@ const ProjectListApplyer = ({
7677
</div>
7778
);
7879

80+
const labelShape = PropTypes.shape({
81+
name: PropTypes.string.isRequired,
82+
color: PropTypes.string.isRequired,
83+
});
84+
7985
ProjectListApplyer.propTypes = {
8086
className: PropTypes.string,
8187

@@ -91,17 +97,16 @@ ProjectListApplyer.propTypes = {
9197
projects: PropTypes.arrayOf(projectShape).isRequired,
9298
selectedPreject: projectShape,
9399

94-
labelsToRemove: PropTypes.arrayOf(PropTypes.shape({
95-
name: PropTypes.string.isRequired,
96-
color: PropTypes.string.isRequired,
97-
})).isRequired,
100+
labelsToAdd: PropTypes.arrayOf(labelShape),
101+
labelsToRemove: PropTypes.arrayOf(labelShape).isRequired,
98102
}
99103

100104
ProjectListApplyer.defaultProps = {
101105
className: "",
102106
applying: false,
103107
applyedLabels: [],
104108
labelsToRemove: [],
109+
labelsToAdd: LABELS_TO_ADD,
105110
}
106111

107112
export default ProjectListApplyer

src/Home.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import gitlabLogo from './assets/images/gitlab.svg'
66
import trelloLogo from './assets/images/trello.svg'
77
import Authenticator from 'netlify-auth-providers';
88
import { UncontrolledTooltip } from 'reactstrap';
9+
import { API_KEY as TRELLO_API_KEY } from './Origins/trelloConstants'
910
import './Home.css'
1011

11-
const nullCallback = (event) => event.preventDefault();
1212
const auth = new Authenticator({ site_id: "cwps.lucassabreu.net.br" });
1313

1414
const Home = ({ history }) => {
@@ -48,8 +48,11 @@ const Home = ({ history }) => {
4848
{
4949
name: "Trello",
5050
logo: trelloLogo,
51-
enabled: false,
52-
callback: nullCallback,
51+
enabled: true,
52+
callback: (event) => {
53+
const callbackUrl = `${window.location.href}trello`
54+
window.location = `https://trello.com/1/authorize?callback_method=fragment&return_url=${callbackUrl}&scope=read,write&expiration=1day&name=Coderockr Way Project Setup&key=${TRELLO_API_KEY}`
55+
},
5356
},
5457
];
5558

src/Origins/Trello.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React from 'react'
2+
import LABELS_TO_ADD from '../Labels';
3+
import ProjectListApplyer from '../Components/ProjectListApplyer';
4+
import TrelloLogo from '../assets/images/trello.svg'
5+
import { API_KEY } from './trelloConstants'
6+
import Loading from '../Components/Loading'
7+
import { proximity } from 'colour-proximity'
8+
import './helper.css'
9+
10+
const COLORS = [
11+
{ name: 'yellow', color: 'f2d600' },
12+
{ name: 'green', color: '61bd4f' },
13+
{ name: 'purple', color: 'c377e0' },
14+
{ name: 'blue', color: '0079bf' },
15+
{ name: 'red', color: 'eb5a46' },
16+
{ name: 'orange', color: 'ffab4a' },
17+
{ name: 'black', color: '4d4d4d' },
18+
{ name: 'sky', color: '00c2e0' },
19+
{ name: 'pink', color: 'ff80ce' },
20+
{ name: 'lime', color: '51e898' },
21+
]
22+
23+
window.p = proximity;
24+
window.cs = COLORS
25+
26+
const mustNear = (color) => {
27+
return COLORS
28+
.map(c => Object.assign({}, c, { proximity: proximity('#' + color, '#' + c.color) }))
29+
.sort((a, b) => a.proximity < b.proximity ? 1 : -1)
30+
.pop()
31+
}
32+
33+
const TRELLO_LABELS = LABELS_TO_ADD.map(l => {
34+
const color = mustNear(l.color)
35+
return Object.assign({}, l, { color: color.color, trelloColor: color.name })
36+
})
37+
38+
class Trello extends React.Component {
39+
40+
constructor(props) {
41+
super(props);
42+
43+
const { location } = props
44+
const token = new URLSearchParams(location.hash.substr(1)).get('token')
45+
46+
this.state = {
47+
token,
48+
loading: true,
49+
boards: [],
50+
selectedOption: null,
51+
applying: false,
52+
applyedLabels: [],
53+
applyingStatus: null,
54+
alert: null,
55+
}
56+
}
57+
58+
fetch({ path, method, query, body }) {
59+
const queryStr = (query && Object.keys(query).reduce((c, key) => `${c}&${key}=${query[key]}`, '')) || ''
60+
return fetch(`https://api.trello.com/1/${path}?key=${API_KEY}&token=${this.state.token}${queryStr}`, {
61+
method: method || 'GET',
62+
body: body,
63+
})
64+
}
65+
66+
async componentDidMount() {
67+
const resp = await this.fetch({
68+
path: 'members/me/boards',
69+
query: { filter: 'open', organizations: true },
70+
});
71+
const boards = await resp.json();
72+
73+
this.setState({
74+
loading: false,
75+
boards: boards,
76+
})
77+
78+
boards.map(b => b.idOrganization)
79+
.reduce((c, id) => c.indexOf(id) > -1 ? c : [...c, id], [])
80+
.filter(id => id !== null)
81+
.map(id => this.showOrganizationName(id));
82+
}
83+
84+
async showOrganizationName(id) {
85+
const resp = await this.fetch({ path: `organizations/${id}`, query: { fields: 'id,displayName' } })
86+
const org = await resp.json()
87+
88+
this.setState(prevState => ({
89+
boards: prevState.boards.map(board => {
90+
if (board.idOrganization !== id) {
91+
return board;
92+
}
93+
94+
return Object.assign(board, {
95+
organization: org,
96+
name: `${board.name} (${org.displayName})`
97+
})
98+
})
99+
}))
100+
}
101+
102+
handleApply(selectedOption) {
103+
this.setState({ selectedOption, applying: true })
104+
this.applyChangesToBoard(selectedOption.value)
105+
}
106+
107+
async applyChangesToBoard(boardId) {
108+
this.setState({
109+
applyedLabels: [],
110+
alert: null
111+
})
112+
113+
114+
try {
115+
const currentLabels = (await (await this.fetch({ path: `boards/${boardId}/labels`, query: { fields: 'name' } })).json())
116+
const createLabelsPromices = TRELLO_LABELS.map(l => this.createLabel(boardId, l, currentLabels))
117+
118+
await Promise.all(createLabelsPromices)
119+
this.setState({
120+
applying: false,
121+
alert: { type: 'success', message: 'Setup completed !' }
122+
});
123+
} catch (error) {
124+
this.setState({
125+
applying: false,
126+
alert: { type: 'danger', message: error.message }
127+
});
128+
}
129+
}
130+
131+
async createLabel(boardId, { name, trelloColor }, currentLabels) {
132+
if (currentLabels.find(cl => cl.name === name)) {
133+
this.addApplyedLabel(name);
134+
return;
135+
}
136+
137+
let formData = new FormData()
138+
formData.append('idBoard', boardId);
139+
formData.append('name', name);
140+
formData.append('color', trelloColor);
141+
142+
const resp = await this.fetch({ path: `labels`, method: 'POST', body: formData });
143+
144+
if (resp.status !== 200) {
145+
const content = await resp.json();
146+
throw new Error(content.message);
147+
}
148+
149+
this.addApplyedLabel(name);
150+
}
151+
152+
addApplyedLabel(name) {
153+
this.setState(({ applyedLabels }) => ({
154+
applyedLabels: [...applyedLabels, name],
155+
applyingStatus: `${name} created`,
156+
}))
157+
}
158+
159+
async removeLabel(boardId, { name }) {
160+
await this.fetch(
161+
`https://Trello.com/api/v4/boards/${boardId}/labels?name=${name}`,
162+
'DELETE'
163+
);
164+
165+
this.setState(({ applyedLabels }) => ({
166+
applyedLabels: [...applyedLabels, name],
167+
applyingStatus: `${name} removed`,
168+
}))
169+
}
170+
171+
render() {
172+
const { loading, boards, selectedOption, applyedLabels, applying, applyingStatus, alert } = this.state;
173+
174+
if (loading) {
175+
return <section>
176+
<h2>Loading Trello...</h2>
177+
<Loading />
178+
</section>
179+
}
180+
return (
181+
<div className="Trello">
182+
<section className="origin-header">
183+
<h1>
184+
<span className="origin-logo"><TrelloLogo /></span>
185+
<span className="origin-name">Trello</span>
186+
</h1>
187+
</section>
188+
<ProjectListApplyer
189+
projects={boards.map(r => Object.assign(r, {
190+
value: r.id,
191+
label: r.name,
192+
}))}
193+
selectedPreject={selectedOption}
194+
195+
labelsToAdd={TRELLO_LABELS}
196+
197+
onApply={(selected) => this.handleApply(selected)}
198+
199+
applyedLabels={applyedLabels}
200+
applying={applying}
201+
applyingStatus={applyingStatus}
202+
alert={alert}
203+
/>
204+
</div>
205+
)
206+
}
207+
}
208+
209+
export default Trello

src/Origins/trelloConstants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const API_KEY = '8986ad2816e480952b9715eed4c5ed2a'

src/assets/images/trello.svg

Lines changed: 1 addition & 1 deletion
Loading

yarn.lock

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,6 +1389,10 @@ code-point-at@^1.0.0:
13891389
version "1.1.0"
13901390
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
13911391

1392+
1393+
version "0.2.1"
1394+
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.2.1.tgz#363cab23c94b31a0d64db71048b8c6a940f8c68c"
1395+
13921396
color-convert@^1.3.0, color-convert@^1.9.0:
13931397
version "1.9.1"
13941398
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
@@ -1405,6 +1409,12 @@ color-string@^0.3.0:
14051409
dependencies:
14061410
color-name "^1.0.0"
14071411

1412+
color-string@~0.1.2:
1413+
version "0.1.3"
1414+
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.1.3.tgz#e865d2e3e59f665c3af0de14383f6bf0705685f3"
1415+
dependencies:
1416+
color-convert "0.2.x"
1417+
14081418
color@^0.11.0:
14091419
version "0.11.4"
14101420
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
@@ -1425,6 +1435,12 @@ colors@~1.1.2:
14251435
version "1.1.2"
14261436
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
14271437

1438+
colour-proximity@^0.0.2:
1439+
version "0.0.2"
1440+
resolved "https://registry.yarnpkg.com/colour-proximity/-/colour-proximity-0.0.2.tgz#139ad3afecf301bc803b8da698f32c972974769c"
1441+
dependencies:
1442+
color-string "~0.1.2"
1443+
14281444
combined-stream@^1.0.5, combined-stream@~1.0.5:
14291445
version "1.0.5"
14301446
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"

0 commit comments

Comments
 (0)