Création/mise à jour basique d'un DAD #15
|
@ -1,31 +1,70 @@
|
||||||
import React, { FunctionComponent, useState } from 'react';
|
import React, { FunctionComponent, useState, ChangeEvent, useEffect } from 'react';
|
||||||
import { DecisionSupportFile } from '../../types/decision';
|
import { DecisionSupportFileUpdaterProps } from './DecisionSupportFileUpdaterProps';
|
||||||
|
|
||||||
export interface ClarificationSectionProps {
|
export interface ClarificationSectionProps extends DecisionSupportFileUpdaterProps {};
|
||||||
dsf: DecisionSupportFile,
|
|
||||||
};
|
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 (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Intitulé du dossier</label>
|
<label className="label">Intitulé du dossier</label>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<input className="input" type="text" />
|
<input className="input" type="text" value={dsf.title} onChange={onTitleChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Quelle décision devons nous prendre ?</label>
|
<label className="label">Quelle décision devons nous prendre ?</label>
|
||||||
<div className="control">
|
<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>
|
</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>
|
<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>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<label className="label">Pourquoi devons nous prendre cette décision ?</label>
|
<label className="label">Pourquoi devons nous prendre cette décision ?</label>
|
||||||
<div className="control">
|
<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>
|
</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>
|
<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>
|
</div>
|
||||||
|
@ -33,11 +72,13 @@ export const ClarificationSection: FunctionComponent<ClarificationSectionProps>
|
||||||
<label className="label">Portée de la décision</label>
|
<label className="label">Portée de la décision</label>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<div className="select">
|
<div className="select">
|
||||||
<select>
|
<select
|
||||||
|
onChange={onSectionAttrChange.bind(null, 'scope')}
|
||||||
|
value={section.scope}>
|
||||||
<option></option>
|
<option></option>
|
||||||
<option>Individuelle</option>
|
<option value="individual">Individuelle</option>
|
||||||
<option>Groupe identifié</option>
|
<option value="identified-group">Groupe identifié</option>
|
||||||
<option>Collective</option>
|
<option value="collective">Collective</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,19 +87,33 @@ export const ClarificationSection: FunctionComponent<ClarificationSectionProps>
|
||||||
<label className="label">Nature de la décision</label>
|
<label className="label">Nature de la décision</label>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<div className="select">
|
<div className="select">
|
||||||
<select>
|
<select onChange={onSectionAttrChange.bind(null, 'nature')}
|
||||||
|
value={section.nature}>
|
||||||
<option></option>
|
<option></option>
|
||||||
<option>Opérationnelle</option>
|
<option value="operational">Opérationnelle</option>
|
||||||
<option>Tactique</option>
|
<option value="tactic">Tactique</option>
|
||||||
<option>Stratégique</option>
|
<option value="strategic">Stratégique</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
|
||||||
<label className="label">Existe t'il une échéance particulière pour cette décision ?</label>
|
<div className="columns">
|
||||||
<div className="control">
|
<div className="column">
|
||||||
<input className="input" type="date" />
|
<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">
|
||||||
|
<div className="control">
|
||||||
|
<input disabled={!section.hasDeadline}
|
||||||
|
value={section.deadline ? section.deadline.toISOString().substr(0, 10) : ''}
|
||||||
|
onChange={onDeadlineChange}
|
||||||
|
type="date" className="input" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,15 +4,42 @@ import { ClarificationSection } from './ClarificationSection';
|
||||||
import { OptionsSection } from './OptionsSection';
|
import { OptionsSection } from './OptionsSection';
|
||||||
import { MetadataPanel } from './MetadataPanel';
|
import { MetadataPanel } from './MetadataPanel';
|
||||||
import { AppendixPanel } from './AppendixPanel';
|
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 interface DecisionSupportFilePageProps {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageProps> = () => {
|
export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageProps> = () => {
|
||||||
const [ state, setState ] = useState({ dsf: null });
|
const { id } = useParams();
|
||||||
const isNew = true;
|
const { decisions } = useDecisions({
|
||||||
const isClosed = false;
|
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 (
|
return (
|
||||||
<Page title="Dossier d'Aide à la Décision">
|
<Page title="Dossier d'Aide à la Décision">
|
||||||
<div className="container is-fluid">
|
<div className="container is-fluid">
|
||||||
|
@ -39,21 +66,24 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
||||||
</section>
|
</section>
|
||||||
<div className="columns mt-3">
|
<div className="columns mt-3">
|
||||||
<div className="column is-9">
|
<div className="column is-9">
|
||||||
<div className="tabs is-medium is-toggle has-background-white">
|
<div className="tabs is-medium is-toggle">
|
||||||
<ul>
|
<ul>
|
||||||
<li className="is-active">
|
<li className={`has-background-white ${state.selectedTabIndex === 0 ? 'is-active': ''}`}
|
||||||
|
onClick={selectTab.bind(null, 0)}>
|
||||||
<a>
|
<a>
|
||||||
<span className="icon is-small"><i className="fas fa-pen" aria-hidden="true"></i></span>
|
<span className="icon is-small"><i className="fas fa-pen" aria-hidden="true"></i></span>
|
||||||
<span>Clarifier la décision</span>
|
<span>Clarifier la décision</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li className={`has-background-white ${state.selectedTabIndex === 1 ? 'is-active': ''}`}
|
||||||
|
onClick={selectTab.bind(null, 1)}>
|
||||||
<a>
|
<a>
|
||||||
<span className="icon is-small"><i className="fas fa-search" aria-hidden="true"></i></span>
|
<span className="icon is-small"><i className="fas fa-search" aria-hidden="true"></i></span>
|
||||||
<span>Explorer les options</span>
|
<span>Explorer les options</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li className={`has-background-white ${state.selectedTabIndex === 2 ? 'is-active': ''}`}
|
||||||
|
onClick={selectTab.bind(null, 2)}>
|
||||||
<a>
|
<a>
|
||||||
<span className="icon is-small"><i className="fas fa-person-booth" aria-hidden="true"></i></span>
|
<span className="icon is-small"><i className="fas fa-person-booth" aria-hidden="true"></i></span>
|
||||||
<span>Prendre la décision</span>
|
<span>Prendre la décision</span>
|
||||||
|
@ -61,7 +91,11 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<ClarificationSection dsf={state.dsf} />
|
{
|
||||||
|
state.selectedTabIndex === 0 ?
|
||||||
|
<ClarificationSection dsf={state.dsf} updateDSF={updateDSF} /> :
|
||||||
|
null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-3">
|
<div className="column is-3">
|
||||||
<MetadataPanel dsf={state.dsf} />
|
<MetadataPanel dsf={state.dsf} />
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { DecisionSupportFile } from "../../types/decision";
|
||||||
|
|
||||||
|
export interface DecisionSupportFileUpdaterProps {
|
||||||
|
dsf: DecisionSupportFile
|
||||||
|
updateDSF: (dsf: DecisionSupportFile) => void
|
||||||
|
}
|
|
@ -14,12 +14,12 @@ export function DecisionSupportFilePanel() {
|
||||||
label: 'Mes dossiers en cours',
|
label: 'Mes dossiers en cours',
|
||||||
itemFilter: (item: Item) => {
|
itemFilter: (item: Item) => {
|
||||||
const dsf = item as DecisionSupportFile;
|
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',
|
label: 'Brouillons',
|
||||||
itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Opened
|
itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Draft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Clos',
|
label: 'Clos',
|
||||||
|
|
|
@ -24,9 +24,9 @@ export function useDecisionsQuery(options = {}) {
|
||||||
return useQuery(QUERY_DECISIONS, options);
|
return useQuery(QUERY_DECISIONS, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDecisions() {
|
export function useDecisions(options = {}) {
|
||||||
const { data, loading, error } = useGraphQLData<DecisionSupportFile[]>(
|
const { data, loading, error } = useGraphQLData<DecisionSupportFile[]>(
|
||||||
QUERY_DECISIONS, 'decicions', []
|
QUERY_DECISIONS, 'decisions', [], options
|
||||||
);
|
);
|
||||||
return { decisions: data, loading, error };
|
return { decisions: data, loading, error };
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
import { useQuery, DocumentNode } from "@apollo/client";
|
import { useQuery, DocumentNode } from "@apollo/client";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
export function useGraphQLData<T>(q: DocumentNode, key: string, defaultValue: T) {
|
export function useGraphQLData<T>(q: DocumentNode, key: string, defaultValue: T, options = {}) {
|
||||||
const query = useQuery(q);
|
const query = useQuery(q, options);
|
||||||
const [ data, setData ] = useState<T>(defaultValue);
|
const [ data, setData ] = useState<T>(defaultValue);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setData(query.data ? query.data[key] as T : defaultValue);
|
setData(query.data ? query.data[key] as T : defaultValue);
|
||||||
|
|
|
@ -22,9 +22,10 @@ export function useWorkgroupsQuery(options = {}) {
|
||||||
return useQuery(QUERY_WORKGROUP, options);
|
return useQuery(QUERY_WORKGROUP, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWorkgroups() {
|
export function useWorkgroups(options = {}) {
|
||||||
const { data, loading, error } = useGraphQLData<Workgroup[]>(
|
const { data, loading, error } = useGraphQLData<Workgroup[]>(
|
||||||
QUERY_WORKGROUP, 'workgroups', []
|
QUERY_WORKGROUP, 'workgroups', [],
|
||||||
|
options
|
||||||
);
|
);
|
||||||
return { workgroups: data, loading, error };
|
return { workgroups: data, loading, error };
|
||||||
}
|
}
|
|
@ -1,23 +1,35 @@
|
||||||
import { Workgroup } from "./workgroup";
|
import { Workgroup } from "./workgroup";
|
||||||
|
|
||||||
export enum DecisionSupportFileStatus {
|
export enum DecisionSupportFileStatus {
|
||||||
Opened = "opened",
|
Draft = "draft",
|
||||||
|
Ready = "ready",
|
||||||
Voted = "voted",
|
Voted = "voted",
|
||||||
Closed = "closed",
|
Closed = "closed",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DecisionSupportFileSection {
|
export interface DecisionSupportFileSection {
|
||||||
id: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// aka Dossier d'aide à la décision
|
// aka Dossier d'aide à la décision
|
||||||
export interface DecisionSupportFile {
|
export interface DecisionSupportFile {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
sections: DecisionSupportFileSection[]
|
sections: {[name: string]: any}
|
||||||
status: DecisionSupportFileStatus
|
status: DecisionSupportFileStatus
|
||||||
workgroup: Workgroup,
|
workgroup?: Workgroup,
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
votedAt?: Date
|
votedAt?: Date
|
||||||
closedAt?: Date
|
closedAt?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function newDecisionSupportFile(): DecisionSupportFile {
|
||||||
|
return {
|
||||||
|
id: '',
|
||||||
|
title: '',
|
||||||
|
sections: {},
|
||||||
|
status: DecisionSupportFileStatus.Draft,
|
||||||
|
workgroup: null,
|
||||||
|
createdAt: new Date(),
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue