Création/mise à jour basique d'un DAD #15

Manually merged
wpetit merged 7 commits from feature/dad into develop 2020-08-26 14:52:54 +02:00
8 changed files with 150 additions and 42 deletions
Showing only changes of commit 680614148c - Show all commits

View File

@ -1,31 +1,70 @@
import React, { FunctionComponent, useState } from 'react';
import { DecisionSupportFile } from '../../types/decision';
import React, { FunctionComponent, useState, ChangeEvent, useEffect } from 'react';
import { DecisionSupportFileUpdaterProps } from './DecisionSupportFileUpdaterProps';
export interface ClarificationSectionProps {
dsf: DecisionSupportFile,
export interface ClarificationSectionProps extends DecisionSupportFileUpdaterProps {};
const ClarificationSectionName = 'clarification';
export const ClarificationSection: FunctionComponent<ClarificationSectionProps> = ({ dsf, updateDSF }) => {
const [ section, setSection ] = useState({
objectives: '',
motivations: '',
scope: '',
nature: '',
deadline: undefined,
hasDeadline: false,
});
useEffect(() => {
updateDSF({ ...dsf, sections: { ...dsf.sections, [ClarificationSectionName]: { ...section }} })
}, [section]);
const onTitleChange = (evt: ChangeEvent<HTMLInputElement>) => {
const title = (evt.currentTarget).value;
updateDSF({ ...dsf, title });
};
const onSectionAttrChange = (attrName: string, evt: ChangeEvent<HTMLInputElement>) => {
const target = evt.currentTarget;
const value = target.hasOwnProperty('checked') ? target.checked : target.value;
setSection(section => ({ ...section, [attrName]: value }));
};
const onDeadlineChange = (evt: ChangeEvent<HTMLInputElement>) => {
const deadline = evt.currentTarget.valueAsDate;
setSection(section => ({ ...section, deadline }));
};
export const ClarificationSection: FunctionComponent<ClarificationSectionProps> = ({ dsf }) => {
return (
<section>
<div className="box">
<div className="field">
<label className="label">Intitulé du dossier</label>
<div className="control">
<input className="input" type="text" />
<input className="input" type="text" value={dsf.title} onChange={onTitleChange} />
</div>
</div>
<div className="field">
<label className="label">Quelle décision devons nous prendre ?</label>
<div className="control">
<textarea className="textarea" placeholder="Décrire globalement les tenants et aboutissants de la décision à prendre." rows={10}></textarea>
<textarea className="textarea"
value={section.objectives}
onChange={onSectionAttrChange.bind(null, 'objectives')}
placeholder="Décrire globalement les tenants et aboutissants de la décision à prendre."
rows={10}>
</textarea>
</div>
<p className="help is-info"><i className="fa fa-info-circle"></i> Ne pas essayer de rentrer trop dans les détails ici. Préférer l'utilisation des annexes et y faire référence.</p>
</div>
<div className="field">
<label className="label">Pourquoi devons nous prendre cette décision ?</label>
<div className="control">
<textarea className="textarea" placeholder="Décrire pourquoi il est important de prendre cette décision." rows={10}></textarea>
<textarea className="textarea"
value={section.motivations}
onChange={onSectionAttrChange.bind(null, 'motivations')}
placeholder="Décrire pourquoi il est important de prendre cette décision."
rows={10}>
</textarea>
</div>
<p className="help is-info"><i className="fa fa-info-circle"></i> Penser à indiquer si des obligations légales pèsent sur cette prise de décision.</p>
</div>
@ -33,11 +72,13 @@ export const ClarificationSection: FunctionComponent<ClarificationSectionProps>
<label className="label">Portée de la décision</label>
<div className="control">
<div className="select">
<select>
<select
onChange={onSectionAttrChange.bind(null, 'scope')}
value={section.scope}>
<option></option>
<option>Individuelle</option>
<option>Groupe identifié</option>
<option>Collective</option>
<option value="individual">Individuelle</option>
<option value="identified-group">Groupe identifié</option>
<option value="collective">Collective</option>
</select>
</div>
</div>
@ -46,19 +87,33 @@ export const ClarificationSection: FunctionComponent<ClarificationSectionProps>
<label className="label">Nature de la décision</label>
<div className="control">
<div className="select">
<select>
<select onChange={onSectionAttrChange.bind(null, 'nature')}
value={section.nature}>
<option></option>
<option>Opérationnelle</option>
<option>Tactique</option>
<option>Stratégique</option>
<option value="operational">Opérationnelle</option>
<option value="tactic">Tactique</option>
<option value="strategic">Stratégique</option>
</select>
</div>
</div>
</div>
<div className="columns">
<div className="column">
<label className="checkbox">
<input type="checkbox"
onChange={onSectionAttrChange.bind(null, 'hasDeadline')}
checked={section.hasDeadline} />
<span className="ml-1">Existe t'il une échéance particulière pour cette décision ?</span>
</label>
<div className="field">
<label className="label">Existe t'il une échéance particulière pour cette décision ?</label>
<div className="control">
<input className="input" type="date" />
<input disabled={!section.hasDeadline}
value={section.deadline ? section.deadline.toISOString().substr(0, 10) : ''}
onChange={onDeadlineChange}
type="date" className="input" />
</div>
</div>
</div>
</div>
</div>

View File

@ -4,15 +4,42 @@ import { ClarificationSection } from './ClarificationSection';
import { OptionsSection } from './OptionsSection';
import { MetadataPanel } from './MetadataPanel';
import { AppendixPanel } from './AppendixPanel';
import { DecisionSupportFile, newDecisionSupportFile, DecisionSupportFileStatus } from '../../types/decision';
import { useParams } from 'react-router';
import { useDecisions } from '../../gql/queries/decisions';
export interface DecisionSupportFilePageProps {
};
export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageProps> = () => {
const [ state, setState ] = useState({ dsf: null });
const isNew = true;
const isClosed = false;
const { id } = useParams();
const { decisions } = useDecisions({
variables:{
filter: {
ids: [id],
}
}
});
const [ state, setState ] = useState({
dsf: decisions.length > 0 ? decisions[0] : newDecisionSupportFile(),
selectedTabIndex: 0
});
const isNew = state.dsf.id === '';
const isClosed = state.dsf.status === DecisionSupportFileStatus.Closed;
const selectTab = (tabIndex: number) => {
setState(state => ({ ...state, selectedTabIndex: tabIndex }));
};
const updateDSF = (dsf: DecisionSupportFile) => {
setState(state => ({...state, dsf}));
};
console.log(state.dsf);
return (
<Page title="Dossier d'Aide à la Décision">
<div className="container is-fluid">
@ -39,21 +66,24 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
</section>
<div className="columns mt-3">
<div className="column is-9">
<div className="tabs is-medium is-toggle has-background-white">
<div className="tabs is-medium is-toggle">
<ul>
<li className="is-active">
<li className={`has-background-white ${state.selectedTabIndex === 0 ? 'is-active': ''}`}
onClick={selectTab.bind(null, 0)}>
<a>
<span className="icon is-small"><i className="fas fa-pen" aria-hidden="true"></i></span>
<span>Clarifier la décision</span>
</a>
</li>
<li>
<li className={`has-background-white ${state.selectedTabIndex === 1 ? 'is-active': ''}`}
onClick={selectTab.bind(null, 1)}>
<a>
<span className="icon is-small"><i className="fas fa-search" aria-hidden="true"></i></span>
<span>Explorer les options</span>
</a>
</li>
<li>
<li className={`has-background-white ${state.selectedTabIndex === 2 ? 'is-active': ''}`}
onClick={selectTab.bind(null, 2)}>
<a>
<span className="icon is-small"><i className="fas fa-person-booth" aria-hidden="true"></i></span>
<span>Prendre la décision</span>
@ -61,7 +91,11 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
</li>
</ul>
</div>
<ClarificationSection dsf={state.dsf} />
{
state.selectedTabIndex === 0 ?
<ClarificationSection dsf={state.dsf} updateDSF={updateDSF} /> :
null
}
</div>
<div className="column is-3">
<MetadataPanel dsf={state.dsf} />

View File

@ -0,0 +1,6 @@
import { DecisionSupportFile } from "../../types/decision";
export interface DecisionSupportFileUpdaterProps {
dsf: DecisionSupportFile
updateDSF: (dsf: DecisionSupportFile) => void
}

View File

@ -14,12 +14,12 @@ export function DecisionSupportFilePanel() {
label: 'Mes dossiers en cours',
itemFilter: (item: Item) => {
const dsf = item as DecisionSupportFile;
return dsf.status === DecisionSupportFileStatus.Opened && inWorkgroup(user, dsf.workgroup);
return (dsf.status === DecisionSupportFileStatus.Draft || dsf.status === DecisionSupportFileStatus.Ready) && inWorkgroup(user, dsf.workgroup);
}
},
{
label: 'Ouverts',
itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Opened
label: 'Brouillons',
itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Draft
},
{
label: 'Clos',

View File

@ -24,9 +24,9 @@ export function useDecisionsQuery(options = {}) {
return useQuery(QUERY_DECISIONS, options);
}
export function useDecisions() {
export function useDecisions(options = {}) {
const { data, loading, error } = useGraphQLData<DecisionSupportFile[]>(
QUERY_DECISIONS, 'decicions', []
QUERY_DECISIONS, 'decisions', [], options
);
return { decisions: data, loading, error };
}

View File

@ -1,8 +1,8 @@
import { useQuery, DocumentNode } from "@apollo/client";
import { useState, useEffect } from "react";
export function useGraphQLData<T>(q: DocumentNode, key: string, defaultValue: T) {
const query = useQuery(q);
export function useGraphQLData<T>(q: DocumentNode, key: string, defaultValue: T, options = {}) {
const query = useQuery(q, options);
const [ data, setData ] = useState<T>(defaultValue);
useEffect(() => {
setData(query.data ? query.data[key] as T : defaultValue);

View File

@ -22,9 +22,10 @@ export function useWorkgroupsQuery(options = {}) {
return useQuery(QUERY_WORKGROUP, options);
}
export function useWorkgroups() {
export function useWorkgroups(options = {}) {
const { data, loading, error } = useGraphQLData<Workgroup[]>(
QUERY_WORKGROUP, 'workgroups', []
QUERY_WORKGROUP, 'workgroups', [],
options
);
return { workgroups: data, loading, error };
}

View File

@ -1,23 +1,35 @@
import { Workgroup } from "./workgroup";
export enum DecisionSupportFileStatus {
Opened = "opened",
Draft = "draft",
Ready = "ready",
Voted = "voted",
Closed = "closed",
}
export interface DecisionSupportFileSection {
id: string
name: string
}
// aka Dossier d'aide à la décision
export interface DecisionSupportFile {
id: string
title: string
sections: DecisionSupportFileSection[]
sections: {[name: string]: any}
status: DecisionSupportFileStatus
workgroup: Workgroup,
workgroup?: Workgroup,
createdAt: Date
votedAt?: Date
closedAt?: Date
}
export function newDecisionSupportFile(): DecisionSupportFile {
return {
id: '',
title: '',
sections: {},
status: DecisionSupportFileStatus.Draft,
workgroup: null,
createdAt: new Date(),
};
}