Création/mise à jour basique d'un DAD #15
|
@ -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="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" />
|
||||
|
||||
<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">
|
||||
<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>
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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',
|
||||
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',
|
||||
|
|
|
@ -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 };
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
}
|
|
@ -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(),
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue