Permettre de gérer les options proposées dans un DAD #19
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "dadd-",
|
||||
"name": "daddy",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
|
@ -2839,6 +2839,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"base-x": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
|
||||
"integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
|
@ -3127,6 +3135,14 @@
|
|||
"pkg-up": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"bs58": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||
"integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
|
||||
"requires": {
|
||||
"base-x": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"btoa": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
||||
|
@ -8631,8 +8647,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safe-regex": {
|
||||
"version": "1.1.0",
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
"dependencies": {
|
||||
"@apollo/client": "^3.0.2",
|
||||
"@types/qs": "^6.9.3",
|
||||
"bs58": "^4.0.1",
|
||||
"bulma": "^0.9.0",
|
||||
"graphql": "^15.3.0",
|
||||
"react": "^16.12.0",
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useParams, useHistory } from 'react-router';
|
|||
import { useDecisionSupportFiles } from '../../gql/queries/dsf';
|
||||
import { useCreateDecisionSupportFileMutation, useUpdateDecisionSupportFileMutation } from '../../gql/mutations/dsf';
|
||||
import { useDebounce } from '../../hooks/useDebounce';
|
||||
import { OptionsSection } from './OptionsSection';
|
||||
|
||||
export interface DecisionSupportFilePageProps {
|
||||
|
||||
|
@ -143,6 +144,11 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
|||
<ClarificationSection dsf={state.dsf} updateDSF={updateDSF} /> :
|
||||
null
|
||||
}
|
||||
{
|
||||
state.selectedTabIndex === 1 ?
|
||||
<OptionsSection dsf={state.dsf} updateDSF={updateDSF} /> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
<div className="column is-3">
|
||||
<MetadataPanel dsf={state.dsf} updateDSF={updateDSF} />
|
||||
|
|
|
@ -1,16 +1,152 @@
|
|||
import React, { FunctionComponent, useState } from 'react';
|
||||
import { DecisionSupportFile } from '../../types/decision';
|
||||
import React, { FunctionComponent, useState, useEffect, ChangeEvent, MouseEvent } from 'react';
|
||||
import { DecisionSupportFileUpdaterProps } from './DecisionSupportFileUpdaterProps';
|
||||
import { base58UUID } from "../../util/uuid";
|
||||
|
||||
export interface OptionsSectionProps {
|
||||
dsf: DecisionSupportFile,
|
||||
export interface OptionsSectionProps extends DecisionSupportFileUpdaterProps {};
|
||||
|
||||
const OptionsSectionName = 'options';
|
||||
|
||||
export const OptionsSection: FunctionComponent<OptionsSectionProps> = ({ dsf, updateDSF }) => {
|
||||
interface OptionsSectionState {
|
||||
changed: boolean
|
||||
section: OptionsSection
|
||||
}
|
||||
|
||||
interface OptionsSection {
|
||||
options: Option[]
|
||||
}
|
||||
|
||||
interface Option {
|
||||
id: string
|
||||
label: string
|
||||
pros: string
|
||||
cons: string
|
||||
}
|
||||
|
||||
const [ state, setState ] = useState<OptionsSectionState>({
|
||||
changed: false,
|
||||
section: {
|
||||
options: [],
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!state.changed) return;
|
||||
updateDSF({ ...dsf, sections: { ...dsf.sections, [OptionsSectionName]: { ...state.section }} })
|
||||
setState(state => ({ ...state, changed: false }));
|
||||
}, [state.changed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dsf.sections[OptionsSectionName]) return;
|
||||
setState(state => ({ ...state, changed: false, section: {...state.section, ...dsf.sections[OptionsSectionName] }}));
|
||||
}, [dsf.sections[OptionsSectionName]]);
|
||||
|
||||
function newOption(label: string, pros: string, cons: string): Option {
|
||||
return {
|
||||
id: base58UUID(),
|
||||
label,
|
||||
pros,
|
||||
cons
|
||||
};
|
||||
}
|
||||
|
||||
const onAddOptionClick = (evt: MouseEvent) => {
|
||||
const options = JSON.parse(JSON.stringify(state.section.options))
|
||||
|
||||
const option = newOption("Décision", "", "");
|
||||
wpetit
commented
Si il n'y aura pas d'assignation de nouvelle valeur à la variable, préférer Si il n'y aura pas d'assignation de nouvelle valeur à la variable, préférer `const` à `var` pour identifier assez vite les erreurs liées à l'écrasement de valeurs "constantes".
|
||||
options.push(option);
|
||||
setState(state => ({ ...state, changed: true, section: { ...state.section, options }}));
|
||||
};
|
||||
|
||||
const onOptionChange = (id: number, attrName: string, evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const target = evt.currentTarget;
|
||||
const value = target.hasOwnProperty('checked') ? target.checked : target.value;
|
||||
const options = JSON.parse(JSON.stringify(state.section.options))
|
||||
wpetit
commented
Idem qu'un peu plus haut. Idem qu'un peu plus haut.
|
||||
options[id][attrName] = value;
|
||||
setState(state => ({ ...state, changed: true, section: { ...state.section, options }}));
|
||||
};
|
||||
|
||||
const onRemoveOptionClick = (id: number, evt: MouseEvent) => {
|
||||
if(confirm('Voulez-vous supprimer cette option ?')){
|
||||
const options = JSON.parse(JSON.stringify(state.section.options))
|
||||
wpetit
commented
Idem, Idem, `const` semble préférable à `var` ici.
|
||||
options.splice(id, 1);
|
||||
setState(state => ({ ...state, changed: true, section: { ...state.section, options }}));
|
||||
}
|
||||
};
|
||||
|
||||
export const OptionsSection: FunctionComponent<OptionsSectionProps> = ({ dsf }) => {
|
||||
return (
|
||||
<section>
|
||||
<h4 id="options-section" className="is-size-4 title is-spaced"><a href="#options-section">Explorer les options</a></h4>
|
||||
<div className="box">
|
||||
|
||||
<div className="table-container">
|
||||
<table className={`table is-bordered is-striped is-hoverable is-fullwidth`}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Décision</th>
|
||||
<th>Pours</th>
|
||||
<th>Contres</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
state.section.options.map((o, index) => {
|
||||
return (
|
||||
<tr key={`option-${o.id}`}>
|
||||
<td>
|
||||
<button
|
||||
onClick={onRemoveOptionClick.bind(null, index)}
|
||||
className="button is-danger is-small is-outlined">
|
||||
🗑️
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<textarea className="textarea"
|
||||
value={o.label}
|
||||
onChange={onOptionChange.bind(null, index, 'label')}
|
||||
placeholder="Décrire cette décision."
|
||||
rows={10}>
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<textarea className="textarea is-success"
|
||||
value={o.pros}
|
||||
onChange={onOptionChange.bind(null, index, 'pros')}
|
||||
placeholder="Décrire les avantages de cette décision."
|
||||
rows={10}>
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<textarea className="textarea is-danger"
|
||||
value={o.cons}
|
||||
onChange={onOptionChange.bind(null, index, 'cons')}
|
||||
placeholder="Décrire les désavantages de cette décision."
|
||||
rows={10}>
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
state.section.options.length === 0 ?
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colSpan={4}>Aucune option pour l'instant.</td>
|
||||
</tr> :
|
||||
null
|
||||
}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={5}>
|
||||
<a className="button is-primary is-pulled-right" onClick={onAddOptionClick}>
|
||||
Ajouter
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -7,7 +7,7 @@ export const QUERY_DECISION_SUPPORT_FILES = gql`
|
|||
decisionSupportFiles(filter: $filter) {
|
||||
id,
|
||||
title,
|
||||
sections
|
||||
sections,
|
||||
createdAt,
|
||||
closedAt,
|
||||
votedAt,
|
||||
|
@ -18,7 +18,7 @@ export const QUERY_DECISION_SUPPORT_FILES = gql`
|
|||
members {
|
||||
id
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import bs58 from 'bs58';
|
||||
|
||||
const hex: string[] = [];
|
||||
|
||||
for (var i = 0; i < 256; i++) {
|
||||
hex[i] = (i < 16 ? '0' : '') + (i).toString(16);
|
||||
}
|
||||
|
||||
export function uuidV4(): string {
|
||||
const r = crypto.getRandomValues(new Uint8Array(16));
|
||||
|
||||
r[6] = r[6] & 0x0f | 0x40;
|
||||
r[8] = r[8] & 0x3f | 0x80;
|
||||
|
||||
return (
|
||||
hex[r[0]] +
|
||||
hex[r[1]] +
|
||||
hex[r[2]] +
|
||||
hex[r[3]] +
|
||||
"-" +
|
||||
hex[r[4]] +
|
||||
hex[r[5]] +
|
||||
"-" +
|
||||
hex[r[6]] +
|
||||
hex[r[7]] +
|
||||
"-" +
|
||||
hex[r[8]] +
|
||||
hex[r[9]] +
|
||||
"-" +
|
||||
hex[r[10]] +
|
||||
hex[r[11]] +
|
||||
hex[r[12]] +
|
||||
hex[r[13]] +
|
||||
hex[r[14]] +
|
||||
hex[r[15]]
|
||||
);
|
||||
}
|
||||
|
||||
export function toUTF8Bytes(str: string): number[] {
|
||||
var utf8 = unescape(encodeURIComponent(str));
|
||||
|
||||
var arr: number[] = [];
|
||||
for (var i = 0; i < utf8.length; i++) {
|
||||
arr.push(utf8.charCodeAt(i));
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
export function base58UUID(): string {
|
||||
const uuid = uuidV4();
|
||||
return bs58.encode(toUTF8Bytes(uuid));
|
||||
}
|
Loading…
Reference in New Issue
JSON.parse(JSON.stringify(state.section.options))
est utilisé ici pour faire une copie ?Pourquoi ne pas plutôt faire
const options = [ ...state.section.options ];
? Cette méthode est plus performante qu'une sérialisation/dé-sérialisation JSON du tableau.Malheureusement la méthode de copie que tu cites ne lève pas l'interdiction d'écrire dans options, je n'ai pas trouvé d'autre méthode que JSON.parse(JSON.stringify(state.section.options)) pour en faire une copie modifiable.