Skip to content

Commit 2db8e97

Browse files
committed
FEATURE: Duplicate portfolio (closes #190)
1 parent 77f001d commit 2db8e97

12 files changed

Lines changed: 405 additions & 28 deletions

File tree

etapes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
modifier etc host
2+
ajouter reverse proxy en local

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
"dependencies": {
66
"bootstrap": "^4.1.1",
77
"hypertopic": "^3.1.4",
8+
"jquery": "^3.4.1",
89
"js-tree": "^2.0.1",
910
"json-groupby": "^1.0.2",
1011
"open-iconic": "^1.1.1",
1112
"query-string": "^4.3.4",
1213
"react": "^16.4.0",
1314
"react-autosuggest": "^9.3.4",
15+
"react-bootstrap": "^1.0.0-beta.8",
1416
"react-dom": "^16.4.0",
1517
"react-router-dom": "^4.1.1",
16-
"sort-by": "^1.2.0"
18+
"sort-by": "^1.2.0",
19+
"styled-components": "^4.2.0"
1720
},
1821
"devDependencies": {
1922
"react-scripts": "1.1.5",

reverse-proxy/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "reverse-proxy",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "server.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"start": "node server.js"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"cors": "^2.8.5",
14+
"express": "^4.17.1",
15+
"express-http-proxy": "^1.5.1",
16+
"http-proxy": "^1.17.0"
17+
}
18+
}

reverse-proxy/server.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
var express = require('express')
2+
var cors = require('cors')
3+
const proxy = require('express-http-proxy');
4+
5+
var app = express()
6+
7+
var corsOptions = {
8+
origin: true,
9+
optionsSuccessStatus: 200,
10+
credentials: true
11+
}
12+
13+
app.use(cors(corsOptions));
14+
app.use(proxy('argos2.test.hypertopic.org'));
15+
16+
17+
18+
19+
app.listen(80, function () {
20+
console.log('CORS-enabled web server listening on port 80')
21+
})

src/components/Authenticated/Authenticated.jsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React, { Component } from 'react';
22
import conf from '../../config/config.json';
3+
import Duplicator from '../Duplication/Duplicator'
4+
import { Dropdown, DropdownButton, ButtonGroup, Button, Form } from 'react-bootstrap';
35

46
const SESSION_URI = conf.services[0] + '/_session';
57

@@ -18,24 +20,39 @@ class Authenticated extends Component {
1820

1921
render() {
2022
if (this.state.user) {
23+
if(this.props.portfolio){
24+
return (
25+
<div className="Authenticated">
26+
<DropdownButton variant="secondary" alignRight as={ButtonGroup} title={this.state.user} id="bg-nested-dropdown">
27+
<Dropdown.Item eventKey="1" onClick={this.handleLogout}>Se déconnecter</Dropdown.Item>
28+
<Duplicator portfolio={this.props.portfolio} userConnected={this.state.user} viewpoints={this.props.viewpoints} corpora={this.props.corpora}/>
29+
</DropdownButton>
30+
</div>
31+
);
32+
}
33+
2134
return (
22-
<div className="Authenticated"> {this.state.user}
23-
<a href="#logout" onClick={this.handleLogout}>Se déconnecter</a>
35+
<div className="Authenticated">
36+
<DropdownButton variant="secondary" alignRight as={ButtonGroup} title={this.state.user} id="bg-nested-dropdown">
37+
<Dropdown.Item eventKey="1" onClick={this.handleLogout}>Se déconnecter</Dropdown.Item>
38+
</DropdownButton>
2439
</div>
2540
);
2641
}
2742
if (this.state.ask) {
2843
return(
29-
<form className="Authenticated" onSubmit={this.handleLogin}>
30-
<input placeholder="nom d'utilisateur" ref={(x) => this.login = x} />
31-
<input placeholder="mot de passe" ref={(x) => this.password = x} type="password" />
32-
<input type="submit" value="Se connecter" />
33-
</form>
44+
<Form className="FormConnect" onSubmit={this.handleLogin}>
45+
<Form.Control type="text" ref={(x) => this.login = x} placeholder="Nom d'utilisateur" />
46+
<Form.Control type="password" ref={(x) => this.password = x} placeholder="Mot de passe" />
47+
<Button variant="secondary" type="submit">
48+
Confirmer
49+
</Button>
50+
</Form>
3451
);
3552
}
3653
return (
3754
<div className="Authenticated">
38-
<a href="#login" onClick={this.handleAsk}>Se connecter...</a>
55+
<Button href="#login" onClick={this.handleAsk} variant="secondary">Se connecter</Button>
3956
</div>
4057
);
4158
}
@@ -82,7 +99,9 @@ class Authenticated extends Component {
8299

83100
_closeSession() {
84101
fetch(SESSION_URI, {method:'DELETE', credentials:'include'})
85-
.then(() => this.setState({user: ''}));
102+
.then(() => {
103+
this.setState({user: ''})
104+
});
86105
}
87106

88107
componentDidMount() {
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import React, { Component } from 'react';
2+
import { Modal, Button, Dropdown } from 'react-bootstrap';
3+
import $ from 'jquery'
4+
var hypertopic = require("hypertopic")
5+
6+
class Duplicator extends Component {
7+
8+
constructor(props) {
9+
super(props);
10+
11+
this.state = {
12+
showModal: false,
13+
showModalConfirmation: false,
14+
showToast: false,
15+
corpora: [],
16+
viewpoints: []
17+
};
18+
this.handleShow = this.handleShow.bind(this);
19+
this.handleClose = this.handleClose.bind(this);
20+
this.handleValidate = this.handleValidate.bind(this);
21+
this.handleConfirm = this.handleConfirm.bind(this);
22+
this.onChangeCheckBox = this.onChangeCheckBox.bind(this);
23+
this.closeToast = this.closeToast.bind(this)
24+
this.addUserToEntity = this.addUserToEntity.bind(this)
25+
}
26+
27+
handleClose() {
28+
this.setState({ showModal: false, showModalConfirmation: false });
29+
}
30+
31+
handleShow() {
32+
this.setState({ showModal: true });
33+
}
34+
35+
handleValidate() {
36+
if(this.state.viewpoints.length > 0 || this.state.corpora.length > 0) {
37+
console.log(this.state.viewpoints)
38+
console.log(this.state.corpora)
39+
this.nameDuplicatedPortfolio = $('#portfolioDuplicatedName').val()
40+
this.handleClose();
41+
this.setState({ showModalConfirmation: true})
42+
}
43+
}
44+
45+
async handleConfirm() {
46+
this.setState({ showModalConfirmation: false, showToast: true });
47+
48+
var ids = this.state.viewpoints.concat(this.state.corpora)
49+
let that = this
50+
51+
await Promise.all(ids.map(id => that.addUserToEntity(id)))
52+
//refresh page
53+
this.setState({showToast: false });
54+
window.location.replace("http://" + this.nameDuplicatedPortfolio + ".local:3000");
55+
56+
}
57+
58+
addUserToEntity(user){
59+
let db = hypertopic([
60+
"http://localhost",
61+
"http://steatite.hypertopic.org"
62+
]);
63+
64+
const _error = (x) => {
65+
console.error(x.message)
66+
return x
67+
};
68+
69+
return db.get({_id:user})
70+
.then( (x) => {
71+
if(!x.users.includes(this.nameDuplicatedPortfolio))
72+
x.users.push(this.nameDuplicatedPortfolio)
73+
return x
74+
})
75+
.then(db.post)
76+
.then((x) => {
77+
console.log(x)
78+
})
79+
.catch(_error);
80+
}
81+
82+
closeToast() {
83+
this.setState(() => {
84+
return {
85+
showToast: false
86+
};
87+
});
88+
}
89+
onChangeCheckBox(e) {
90+
$('.checkCorpora').each(function() {
91+
if ($(this).is(":checked")) {
92+
$('.Corpus').each(function() {
93+
$(this).prop('checked', true);
94+
$(this).prop('disabled', true);
95+
});
96+
}else{
97+
$('.Corpus').each(function() {
98+
$(this).prop('disabled', false);
99+
});
100+
}
101+
});
102+
103+
$('.checkViewPoints').each(function() {
104+
if ($(this).is(":checked")) {
105+
$('.ViewPoint').each(function() {
106+
$(this).prop('checked', true);
107+
$(this).prop('disabled', true);
108+
});
109+
}else{
110+
$('.ViewPoint').each(function() {
111+
$(this).prop('disabled', false);
112+
});
113+
}
114+
});
115+
116+
var selectedViewPoints = [];
117+
$('.ViewPoint').each(function() {
118+
if ($(this).is(":checked")) {
119+
selectedViewPoints.push($(this).attr('value'));
120+
}
121+
});
122+
123+
var selectedCorpora = [];
124+
$('.Corpus').each(function() {
125+
if ($(this).is(":checked")) {
126+
selectedCorpora.push($(this).attr('value'));
127+
}
128+
});
129+
130+
this.setState({
131+
corpora: selectedCorpora,
132+
viewpoints: selectedViewPoints
133+
})
134+
}
135+
136+
render() {
137+
let name = this.props.userConnected + '-' + this.props.portfolio
138+
let corpora = this.props.corpora.map((v, i) =>
139+
<div className='Modal-Group' key={v.id}>
140+
<input className='Modal-CheckBox Corpus' value={v.id} onChange={this.onChangeCheckBox} type="checkbox"/>
141+
{v.id}
142+
</div>
143+
);
144+
145+
let viewpoints = this.props.viewpoints.map((v, i) =>
146+
<div className='Modal-Group' key={v.id}>
147+
<input className='Modal-CheckBox ViewPoint' value={v.id} onChange={this.onChangeCheckBox} type="checkbox"/>
148+
{v.name}
149+
</div>
150+
);
151+
152+
153+
return (
154+
<div>
155+
<Modal show={this.state.showToast}>
156+
<Modal.Body>
157+
<h3>Redirection à la nouvelle page...</h3>
158+
</Modal.Body>
159+
</Modal>
160+
161+
<Modal show={this.state.showModalConfirmation}>
162+
<Modal.Body>
163+
<h3>Créer le nouveau portfolio</h3>
164+
</Modal.Body>
165+
<Modal.Footer>
166+
<Button variant="secondary" onClick={this.handleClose}>
167+
Annuler
168+
</Button>
169+
<Button variant="primary" onClick={this.handleConfirm}>
170+
Confirmer
171+
</Button>
172+
</Modal.Footer>
173+
</Modal>
174+
175+
<Modal show={this.state.showModal}>
176+
<Modal.Body>
177+
<h3>Nom du portfolio</h3>
178+
<input type='text' className='Modal-Input form-control' defaultValue={name} placeholder='Nom du nouveau portfolio' id="portfolioDuplicatedName"/>
179+
<div className='Modal-Group Modal-Title'>
180+
<h3>Corpus</h3>
181+
<div className='Modal-Group'><input className='Modal-CheckBox checkCorpora' type="checkbox" onChange={this.onChangeCheckBox}/>Tout</div>
182+
</div>
183+
<hr/>
184+
<div id="corporaList">
185+
{corpora}
186+
</div>
187+
<br/>
188+
<div className='Modal-Group Modal-Title'>
189+
<h3>Points de vue</h3>
190+
<div className='Modal-Group'><input className='Modal-CheckBox checkViewPoints' type="checkbox" onChange={this.onChangeCheckBox}/>Tout</div>
191+
</div>
192+
<hr/>
193+
<div id="viewPointsList">
194+
{viewpoints}
195+
</div>
196+
</Modal.Body>
197+
<Modal.Footer>
198+
<Button variant="secondary" onClick={this.handleClose}>
199+
Annuler
200+
</Button>
201+
<Button variant="primary" onClick={this.handleValidate}>
202+
Valider
203+
</Button>
204+
</Modal.Footer>
205+
</Modal>
206+
<Dropdown.Item eventKey="2" onClick={this.handleShow}>Dupliquer</Dropdown.Item>
207+
</div>
208+
209+
)
210+
}
211+
}
212+
213+
export default Duplicator;

src/components/Item/Item.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ class Item extends Component {
5555
<div className="App container-fluid">
5656
<Header />
5757
<div className="Status row h5">
58-
<Authenticated/>
59-
<Link to="/" className="badge badge-pill badge-light TopicTag">
60-
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span> Retour à l'accueil
61-
</Link>
58+
<ul>
59+
<li><Link to="/" className="badge badge-pill badge-light TopicTag">
60+
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span> Retour à l'accueil
61+
</Link></li>
62+
<li><Authenticated/></li>
63+
</ul>
6264
</div>
6365
<div className="container-fluid">
6466
<div className="App-content row">

src/components/Outliner/Outliner.jsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ class Outliner extends React.Component {
3030
<div className="App container-fluid">
3131
<Header />
3232
<div className="Status row h5">
33-
<Authenticated/>
34-
<Link to="/" className="badge badge-pill badge-light TopicTag">
33+
<ul>
34+
<li><Link to="/" className="badge badge-pill badge-light TopicTag">
3535
<span className="badge badge-pill badge-dark oi oi-chevron-left"> </span> Retour à l'accueil
36-
</Link>
36+
</Link></li>
37+
<li><Authenticated/></li>
38+
</ul>
3739
</div>
3840
<div className="container-fluid">
3941
<div className="App-content row">

0 commit comments

Comments
 (0)