Merge branch 'develop' into dist/ubuntu/bionic/develop
This commit is contained in:
commit
42f7ba3473
|
@ -45,7 +45,7 @@ export const App: FunctionComponent<AppProps> = () => {
|
||||||
<Route path="/unauthorized" exact component={UnauthorizedPage} />
|
<Route path="/unauthorized" exact component={UnauthorizedPage} />
|
||||||
<PrivateRoute path="/profile" exact component={ProfilePage} />
|
<PrivateRoute path="/profile" exact component={ProfilePage} />
|
||||||
<PrivateRoute path="/workgroups/:id" exact component={WorkgroupPage} />
|
<PrivateRoute path="/workgroups/:id" exact component={WorkgroupPage} />
|
||||||
<PrivateRoute path="/decisions/:id" exact component={DecisionSupportFilePage} />
|
<PrivateRoute path="/decisions/:id" component={DecisionSupportFilePage} />
|
||||||
<PrivateRoute path="/dashboard" exact component={DashboardPage} />
|
<PrivateRoute path="/dashboard" exact component={DashboardPage} />
|
||||||
<PrivateRoute path="/logout" exact component={LogoutPage} />
|
<PrivateRoute path="/logout" exact component={LogoutPage} />
|
||||||
<Route component={() => <Redirect to="/" />} />
|
<Route component={() => <Redirect to="/" />} />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DecisionSupportFile, DecisionSupportFileStatus } from '../../types/decision';
|
import { DecisionSupportFile, DecisionSupportFileStatus } from '../../types/decision';
|
||||||
import { ItemPanel, TabDefinition, Item } from './ItemPanel';
|
import { ItemPanel, TabDefinition, Item } from '../ItemPanel';
|
||||||
import { useUserProfile } from '../../gql/queries/profile';
|
import { useUserProfile } from '../../gql/queries/profile';
|
||||||
import { inWorkgroup } from '../../types/workgroup';
|
import { inWorkgroup } from '../../types/workgroup';
|
||||||
import { useDecisionSupportFiles } from '../../gql/queries/dsf';
|
import { useDecisionSupportFiles } from '../../gql/queries/dsf';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { } from 'react';
|
||||||
import { Workgroup, inWorkgroup } from '../../types/workgroup';
|
import { Workgroup, inWorkgroup } from '../../types/workgroup';
|
||||||
import { useWorkgroups } from '../../gql/queries/workgroups';
|
import { useWorkgroups } from '../../gql/queries/workgroups';
|
||||||
import { useUserProfile } from '../../gql/queries/profile';
|
import { useUserProfile } from '../../gql/queries/profile';
|
||||||
import { ItemPanel, Item } from './ItemPanel';
|
import { ItemPanel, Item } from '../ItemPanel';
|
||||||
|
|
||||||
export function WorkgroupsPanel() {
|
export function WorkgroupsPanel() {
|
||||||
const { workgroups } = useWorkgroups();
|
const { workgroups } = useWorkgroups();
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useWorkgroups } from "../gql/queries/workgroups";
|
||||||
import { useDecisionSupportFiles } from "../gql/queries/dsf";
|
import { useDecisionSupportFiles } from "../gql/queries/dsf";
|
||||||
|
|
||||||
export interface DecisioSupportFileLinkProps {
|
export interface DecisioSupportFileLinkProps {
|
||||||
decisionSupportFileId: number
|
decisionSupportFileId: number|string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DecisionSupportFileLink: FunctionComponent<DecisioSupportFileLinkProps> = ({ decisionSupportFileId }) => {
|
export const DecisionSupportFileLink: FunctionComponent<DecisioSupportFileLinkProps> = ({ decisionSupportFileId }) => {
|
||||||
|
|
|
@ -49,90 +49,88 @@ export const ClarificationSection: FunctionComponent<ClarificationSectionProps>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="box">
|
<div className="field">
|
||||||
<div className="field">
|
<label className="label is-medium">Intitulé du dossier</label>
|
||||||
<label className="label is-medium">Intitulé du dossier</label>
|
<div className="control">
|
||||||
<div className="control">
|
<input className="input is-medium" type="text" readOnly={readOnly} value={dsf.title} onChange={onTitleChange} />
|
||||||
<input className="input is-medium" type="text" readOnly={readOnly} value={dsf.title} onChange={onTitleChange} />
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="field">
|
||||||
|
<label className="label is-medium">Quelle décision devons nous prendre ?</label>
|
||||||
|
<div className="control">
|
||||||
|
<textarea className="textarea is-medium"
|
||||||
|
readOnly={readOnly}
|
||||||
|
value={state.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 is-medium">Pourquoi devons nous prendre cette décision ?</label>
|
||||||
|
<div className="control">
|
||||||
|
<textarea className="textarea is-medium"
|
||||||
|
readOnly={readOnly}
|
||||||
|
value={state.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>
|
||||||
|
<div className="field">
|
||||||
|
<label className="label is-medium">Portée de la décision</label>
|
||||||
|
<div className="control">
|
||||||
|
<div className="select is-medium">
|
||||||
|
<select
|
||||||
|
disabled={readOnly}
|
||||||
|
onChange={onSectionAttrChange.bind(null, 'scope')}
|
||||||
|
value={state.section.scope}>
|
||||||
|
<option></option>
|
||||||
|
<option value="individual">Individuelle</option>
|
||||||
|
<option value="identified-group">Groupe identifié</option>
|
||||||
|
<option value="collective">Collective</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
</div>
|
||||||
<label className="label is-medium">Quelle décision devons nous prendre ?</label>
|
<div className="field">
|
||||||
<div className="control">
|
<label className="label is-medium">Nature de la décision</label>
|
||||||
<textarea className="textarea is-medium"
|
<div className="control">
|
||||||
readOnly={readOnly}
|
<div className="select is-medium">
|
||||||
value={state.section.objectives}
|
<select
|
||||||
onChange={onSectionAttrChange.bind(null, 'objectives')}
|
disabled={readOnly}
|
||||||
placeholder="Décrire globalement les tenants et aboutissants de la décision à prendre."
|
onChange={onSectionAttrChange.bind(null, 'nature')}
|
||||||
rows={10}>
|
value={state.section.nature}>
|
||||||
</textarea>
|
<option></option>
|
||||||
</div>
|
<option value="operational">Opérationnelle</option>
|
||||||
<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>
|
<option value="tactic">Tactique</option>
|
||||||
</div>
|
<option value="strategic">Stratégique</option>
|
||||||
<div className="field">
|
</select>
|
||||||
<label className="label is-medium">Pourquoi devons nous prendre cette décision ?</label>
|
|
||||||
<div className="control">
|
|
||||||
<textarea className="textarea is-medium"
|
|
||||||
readOnly={readOnly}
|
|
||||||
value={state.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>
|
|
||||||
<div className="field">
|
|
||||||
<label className="label is-medium">Portée de la décision</label>
|
|
||||||
<div className="control">
|
|
||||||
<div className="select is-medium">
|
|
||||||
<select
|
|
||||||
disabled={readOnly}
|
|
||||||
onChange={onSectionAttrChange.bind(null, 'scope')}
|
|
||||||
value={state.section.scope}>
|
|
||||||
<option></option>
|
|
||||||
<option value="individual">Individuelle</option>
|
|
||||||
<option value="identified-group">Groupe identifié</option>
|
|
||||||
<option value="collective">Collective</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
</div>
|
||||||
<label className="label is-medium">Nature de la décision</label>
|
|
||||||
<div className="control">
|
<div className="columns">
|
||||||
<div className="select is-medium">
|
<div className="column">
|
||||||
<select
|
<label className="checkbox">
|
||||||
disabled={readOnly}
|
<input type="checkbox"
|
||||||
onChange={onSectionAttrChange.bind(null, 'nature')}
|
className="is-medium"
|
||||||
value={state.section.nature}>
|
disabled={readOnly}
|
||||||
<option></option>
|
onChange={onSectionAttrChange.bind(null, 'hasDeadline')}
|
||||||
<option value="operational">Opérationnelle</option>
|
checked={state.section.hasDeadline} />
|
||||||
<option value="tactic">Tactique</option>
|
<span className="ml-1 has-text-weight-bold is-size-5">Existe t'il une échéance particulière pour cette décision ?</span>
|
||||||
<option value="strategic">Stratégique</option>
|
</label>
|
||||||
</select>
|
<div className="field">
|
||||||
</div>
|
<div className="control">
|
||||||
</div>
|
<input disabled={!state.section.hasDeadline}
|
||||||
</div>
|
readOnly={readOnly}
|
||||||
|
value={state.section.deadline ? asDate(state.section.deadline).toISOString().substr(0, 10) : ''}
|
||||||
<div className="columns">
|
onChange={onDeadlineChange}
|
||||||
<div className="column">
|
type="date" className="input is-medium" />
|
||||||
<label className="checkbox">
|
|
||||||
<input type="checkbox"
|
|
||||||
className="is-medium"
|
|
||||||
disabled={readOnly}
|
|
||||||
onChange={onSectionAttrChange.bind(null, 'hasDeadline')}
|
|
||||||
checked={state.section.hasDeadline} />
|
|
||||||
<span className="ml-1 has-text-weight-bold is-size-5">Existe t'il une échéance particulière pour cette décision ?</span>
|
|
||||||
</label>
|
|
||||||
<div className="field">
|
|
||||||
<div className="control">
|
|
||||||
<input disabled={!state.section.hasDeadline}
|
|
||||||
readOnly={readOnly}
|
|
||||||
value={state.section.deadline ? asDate(state.section.deadline).toISOString().substr(0, 10) : ''}
|
|
||||||
onChange={onDeadlineChange}
|
|
||||||
type="date" className="input is-medium" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,19 +32,17 @@ export const DecisionReportSection: FunctionComponent<DecisionReportSectionProps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="box">
|
<div className="field">
|
||||||
<div className="field">
|
<label className="label is-medium">Compte rendu du vote</label>
|
||||||
<label className="label is-medium">Compte rendu du vote</label>
|
<div className="control">
|
||||||
<div className="control">
|
<textarea className="textarea is-medium"
|
||||||
<textarea className="textarea is-medium"
|
readOnly={readOnly}
|
||||||
readOnly={readOnly}
|
value={state.section.report}
|
||||||
value={state.section.report}
|
onChange={onSectionAttrChange.bind(null, 'report')}
|
||||||
onChange={onSectionAttrChange.bind(null, 'report')}
|
rows={20}>
|
||||||
rows={20}>
|
</textarea>
|
||||||
</textarea>
|
|
||||||
</div>
|
|
||||||
<p className="help is-info"><i className="fa fa-info-circle"></i> Penser à indiquer le résultat du vote et les éléments de contexte liés à la prise de décision.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="help is-info"><i className="fa fa-info-circle"></i> Penser à indiquer le résultat du vote et les éléments de contexte liés à la prise de décision.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { OptionsSection } from './OptionsSection';
|
||||||
import { useIsAuthorized } from '../../gql/queries/authorization';
|
import { useIsAuthorized } from '../../gql/queries/authorization';
|
||||||
import { TimelinePanel } from './TimelinePanel';
|
import { TimelinePanel } from './TimelinePanel';
|
||||||
import { DecisionReportSection } from './DecisionReportSection';
|
import { DecisionReportSection } from './DecisionReportSection';
|
||||||
|
import { RoutedTabs } from '../RoutedTabs';
|
||||||
|
|
||||||
export interface DecisionSupportFilePageProps {
|
export interface DecisionSupportFilePageProps {
|
||||||
|
|
||||||
|
@ -30,10 +31,8 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
||||||
const [ state, setState ] = useState({
|
const [ state, setState ] = useState({
|
||||||
dsf: newDecisionSupportFile(),
|
dsf: newDecisionSupportFile(),
|
||||||
saved: true,
|
saved: true,
|
||||||
selectedTabIndex: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const { isAuthorized } = useIsAuthorized({
|
const { isAuthorized } = useIsAuthorized({
|
||||||
variables: {
|
variables: {
|
||||||
action: 'update',
|
action: 'update',
|
||||||
|
@ -48,9 +47,26 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
||||||
setState(state => ({ ...state, dsf: { ...state.dsf, ...dsf }}))
|
setState(state => ({ ...state, dsf: { ...state.dsf, ...dsf }}))
|
||||||
}, [ decisionSupportFiles ]);
|
}, [ decisionSupportFiles ]);
|
||||||
|
|
||||||
const selectTab = (tabIndex: number) => {
|
const tabs = [
|
||||||
setState(state => ({ ...state, selectedTabIndex: tabIndex }));
|
{
|
||||||
};
|
name: "Clarifier la proposition",
|
||||||
|
icon: "fas fa-pen",
|
||||||
|
route: '/info',
|
||||||
|
render: () => (<ClarificationSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Explorer les options",
|
||||||
|
icon: "fas fa-search",
|
||||||
|
route: '/options',
|
||||||
|
render: () => (<OptionsSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Prendre la décision",
|
||||||
|
icon: "fas fa-person-booth",
|
||||||
|
route: '/vote',
|
||||||
|
render: () => (<DecisionReportSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const updateDSF = (dsf: DecisionSupportFile) => {
|
const updateDSF = (dsf: DecisionSupportFile) => {
|
||||||
setState(state => {
|
setState(state => {
|
||||||
|
@ -130,46 +146,9 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
||||||
</section>
|
</section>
|
||||||
<div className="columns mt-3">
|
<div className="columns mt-3">
|
||||||
<div className="column is-8">
|
<div className="column is-8">
|
||||||
<div className="tabs is-medium is-toggle">
|
<div className="box">
|
||||||
<ul>
|
<RoutedTabs baseRoute={`/decisions/${id}`} tabs={tabs} />
|
||||||
<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 proposition</span>
|
|
||||||
</a>
|
|
||||||
</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 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>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
{
|
|
||||||
state.selectedTabIndex === 0 ?
|
|
||||||
<ClarificationSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} /> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
state.selectedTabIndex === 1 ?
|
|
||||||
<OptionsSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} /> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
state.selectedTabIndex === 2 ?
|
|
||||||
<DecisionReportSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} /> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-4">
|
<div className="column is-4">
|
||||||
<MetadataPanel readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />
|
<MetadataPanel readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />
|
||||||
|
|
|
@ -75,8 +75,7 @@ export const OptionsSection: FunctionComponent<OptionsSectionProps> = ({ dsf, up
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<h4 id="options-section" className="is-size-4 title is-spaced"><a href="#options-section">Explorer les options</a></h4>
|
<h4 id="options-section" className="is-size-4 title is-spaced">Explorer les options</h4>
|
||||||
<div className="box">
|
|
||||||
<div className="table-container">
|
<div className="table-container">
|
||||||
<table className={`table is-bordered is-striped is-hoverable is-fullwidth`}>
|
<table className={`table is-bordered is-striped is-hoverable is-fullwidth`}>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -154,7 +153,6 @@ export const OptionsSection: FunctionComponent<OptionsSectionProps> = ({ dsf, up
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { FunctionComponent, useState, useEffect } from "react";
|
import React, { FunctionComponent, useState, useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { WithLoader } from "../WithLoader";
|
import { WithLoader } from "./WithLoader";
|
||||||
|
|
||||||
export interface Item {
|
export interface Item {
|
||||||
id: string
|
id: string
|
|
@ -0,0 +1,79 @@
|
||||||
|
import React, { FunctionComponent, ReactNode, useEffect, useState } from 'react';
|
||||||
|
import { useHistory, useLocation, useRouteMatch } from 'react-router';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export interface Tab {
|
||||||
|
route: string
|
||||||
|
name: string
|
||||||
|
icon ?: string
|
||||||
|
render: (tab: Tab) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoutedTabsProps {
|
||||||
|
tabs: Tab[]
|
||||||
|
baseRoute?: string
|
||||||
|
defaultTabIndex?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const RoutedTabs: FunctionComponent<RoutedTabsProps> = ({ tabs, baseRoute, defaultTabIndex }) => {
|
||||||
|
const history = useHistory();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const tabRoute = (route: string): string => {
|
||||||
|
return `${baseRoute}${route}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [ selectedTabIndex, setSelectedTabIndex ] = useState(defaultTabIndex || 0);
|
||||||
|
const expectedTab = tabs[selectedTabIndex];
|
||||||
|
const expectedTabRoute = tabRoute(expectedTab.route);
|
||||||
|
|
||||||
|
let matchExpectedTabRoute = useRouteMatch(expectedTabRoute);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (matchExpectedTabRoute) return;
|
||||||
|
|
||||||
|
const newTabIndex = tabs.findIndex(t => location.pathname === tabRoute(t.route));
|
||||||
|
|
||||||
|
if (newTabIndex !== -1) {
|
||||||
|
selectTab(newTabIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push(expectedTabRoute);
|
||||||
|
}, [matchExpectedTabRoute]);
|
||||||
|
|
||||||
|
const selectTab = (tabIndex: number) => {
|
||||||
|
setSelectedTabIndex(tabIndex);
|
||||||
|
const newTab = tabs[tabIndex];
|
||||||
|
history.push(tabRoute(newTab.route));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="tabs is-medium is-boxed">
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
tabs.map((t: Tab, i: number) => {
|
||||||
|
return (
|
||||||
|
<li key={`tab-${i}`} className={`has-background-white ${selectedTabIndex === i ? 'is-active': ''}`}
|
||||||
|
onClick={selectTab.bind(null, i)}>
|
||||||
|
<a>
|
||||||
|
{
|
||||||
|
t.icon ?
|
||||||
|
<span className="icon is-small"><i className={t.icon} aria-hidden="true"></i></span> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
<span>{t.name}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{ expectedTab.render(expectedTab) }
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, { FunctionComponent } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { useDecisionSupportFiles } from '../../gql/queries/dsf';
|
||||||
|
import { DecisionSupportFile } from '../../types/decision';
|
||||||
|
import { User } from '../../types/user';
|
||||||
|
import { DecisionSupportFileLink } from '../DecisionSupportFileLink';
|
||||||
|
import { WorkgroupLink } from '../WorkgroupLink';
|
||||||
|
|
||||||
|
export interface DecisionSupportFilePanelProps {
|
||||||
|
workgroupId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DecisionSupportFilePanel: FunctionComponent<DecisionSupportFilePanelProps> = ({ workgroupId }) => {
|
||||||
|
const { decisionSupportFiles } = useDecisionSupportFiles({
|
||||||
|
variables: {
|
||||||
|
filter: {
|
||||||
|
workgroups: [workgroupId],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="panel">
|
||||||
|
<p className="panel-heading">
|
||||||
|
Dossiers d'aide à la décision
|
||||||
|
</p>
|
||||||
|
{
|
||||||
|
decisionSupportFiles.map((dsf: DecisionSupportFile) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/decisions/${dsf.id}`} key={`dsf-${dsf.id}`} className="panel-block">
|
||||||
|
<span className="panel-icon">
|
||||||
|
<i className="fas fa-file" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span>{dsf.title}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
decisionSupportFiles.length === 0 ?
|
||||||
|
<a className="panel-block has-text-centered is-block">
|
||||||
|
<p className="is-italic">Aucun dossier pour l'instant.</p>
|
||||||
|
</a> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
import React, { useEffect, useState, Fragment } from 'react';
|
import React, { useEffect, useState, Fragment } from 'react';
|
||||||
import { Page } from '../Page';
|
import { Page } from '../Page';
|
||||||
import { WithLoader } from '../WithLoader';
|
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
import { useWorkgroupsQuery, useWorkgroups } from '../../gql/queries/workgroups';
|
import { useWorkgroups } from '../../gql/queries/workgroups';
|
||||||
import { useUserProfileQuery, useUserProfile } from '../../gql/queries/profile';
|
import { useUserProfile } from '../../gql/queries/profile';
|
||||||
import { MembersPanel } from './MembersPanel';
|
import { MembersPanel } from './MembersPanel';
|
||||||
import { User } from '../../types/user';
|
import { User } from '../../types/user';
|
||||||
import { InfoPanel } from './InfoPanel';
|
import { InfoPanel } from './InfoPanel';
|
||||||
import { Workgroup } from '../../types/workgroup';
|
import { Workgroup } from '../../types/workgroup';
|
||||||
import { useJoinWorkgroupMutation, useLeaveWorkgroupMutation, useCloseWorkgroupMutation } from '../../gql/mutations/workgroups';
|
import { useJoinWorkgroupMutation, useLeaveWorkgroupMutation, useCloseWorkgroupMutation } from '../../gql/mutations/workgroups';
|
||||||
import { TimelinePanel } from './TimelinePanel';
|
import { TimelinePanel } from './TimelinePanel';
|
||||||
|
import { DecisionSupportFilePanel } from './DecisionSupportFilePanel';
|
||||||
|
|
||||||
export function WorkgroupPage() {
|
export function WorkgroupPage() {
|
||||||
const { id } = useParams();
|
const { id } = useParams<any>();
|
||||||
const { workgroups } = useWorkgroups({
|
const { workgroups } = useWorkgroups({
|
||||||
variables:{
|
variables:{
|
||||||
filter: {
|
filter: {
|
||||||
|
@ -140,6 +140,7 @@ export function WorkgroupPage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-4">
|
<div className="column is-4">
|
||||||
<MembersPanel users={state.workgroup.members as User[]} />
|
<MembersPanel users={state.workgroup.members as User[]} />
|
||||||
|
<DecisionSupportFilePanel workgroupId={state.workgroup.id} />
|
||||||
</div>
|
</div>
|
||||||
<div className="column is-4">
|
<div className="column is-4">
|
||||||
<TimelinePanel workgroup={state.workgroup} />
|
<TimelinePanel workgroup={state.workgroup} />
|
||||||
|
|
|
@ -46,6 +46,7 @@ type DecisionSupportFile {
|
||||||
|
|
||||||
input DecisionSupportFileFilter {
|
input DecisionSupportFileFilter {
|
||||||
ids: [ID]
|
ids: [ID]
|
||||||
|
workgroups: [ID]
|
||||||
}
|
}
|
||||||
|
|
||||||
input AuthorizationObject {
|
input AuthorizationObject {
|
||||||
|
|
|
@ -108,6 +108,10 @@ func (r *DSFRepository) Search(ctx context.Context, filter *DecisionSupportFileF
|
||||||
if filter.Ids != nil {
|
if filter.Ids != nil {
|
||||||
query = query.Where("id in (?)", filter.Ids)
|
query = query.Where("id in (?)", filter.Ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if filter.Workgroups != nil {
|
||||||
|
query = query.Where("workgroup_id in (?)", filter.Workgroups)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dsfs := make([]*DecisionSupportFile, 0)
|
dsfs := make([]*DecisionSupportFile, 0)
|
||||||
|
|
Loading…
Reference in New Issue