Ajout d'un champ permettant de rédiger le rapport de vote #28
|
@ -7,7 +7,7 @@ export interface DecisioSupportFileLinkProps {
|
|||
decisionSupportFileId: number
|
||||
}
|
||||
|
||||
export const DecisioSupportFileLink: FunctionComponent<DecisioSupportFileLinkProps> = ({ decisionSupportFileId }) => {
|
||||
export const DecisionSupportFileLink: FunctionComponent<DecisioSupportFileLinkProps> = ({ decisionSupportFileId }) => {
|
||||
const { decisionSupportFiles } = useDecisionSupportFiles({
|
||||
fetchPolicy: "cache-first",
|
||||
variables: {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import React, { FunctionComponent, useState, ChangeEvent, useEffect } from 'react';
|
||||
import { DecisionSupportFileUpdaterProps } from './DecisionSupportFileUpdaterProps';
|
||||
|
||||
export interface DecisionReportSectionProps extends DecisionSupportFileUpdaterProps {};
|
||||
|
||||
const DecisionReportSectionName = 'decision-report';
|
||||
|
||||
export const DecisionReportSection: FunctionComponent<DecisionReportSectionProps> = ({ dsf, updateDSF, readOnly }) => {
|
||||
const [ state, setState ] = useState({
|
||||
changed: false,
|
||||
section: {
|
||||
report: "",
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!state.changed) return;
|
||||
updateDSF({ ...dsf, sections: { ...dsf.sections, [DecisionReportSectionName]: { ...state.section }} })
|
||||
setState(state => ({ ...state, changed: false }));
|
||||
}, [state.changed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dsf.sections[DecisionReportSectionName]) return;
|
||||
setState(state => ({ ...state, changed: false, section: {...state.section, ...dsf.sections[DecisionReportSectionName] }}));
|
||||
}, [dsf.sections[DecisionReportSectionName]]);
|
||||
|
||||
const onSectionAttrChange = (attrName: string, evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const target = evt.currentTarget;
|
||||
const value = target.hasOwnProperty('checked') ? target.checked : target.value;
|
||||
setState(state => ({ ...state, changed: true, section: {...state.section, [attrName]: value }}));
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="box">
|
||||
<div className="field">
|
||||
<label className="label is-medium">Compte rendu du vote</label>
|
||||
<div className="control">
|
||||
<textarea className="textarea is-medium"
|
||||
readOnly={readOnly}
|
||||
value={state.section.report}
|
||||
onChange={onSectionAttrChange.bind(null, 'report')}
|
||||
rows={20}>
|
||||
</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>
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -10,13 +10,14 @@ import { useCreateDecisionSupportFileMutation, useUpdateDecisionSupportFileMutat
|
|||
import { OptionsSection } from './OptionsSection';
|
||||
import { useIsAuthorized } from '../../gql/queries/authorization';
|
||||
import { TimelinePanel } from './TimelinePanel';
|
||||
import { DecisionReportSection } from './DecisionReportSection';
|
||||
|
||||
export interface DecisionSupportFilePageProps {
|
||||
|
||||
};
|
||||
|
||||
export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageProps> = () => {
|
||||
const { id } = useParams();
|
||||
const { id } = useParams<any>();
|
||||
const history = useHistory();
|
||||
const { decisionSupportFiles } = useDecisionSupportFiles({
|
||||
variables:{
|
||||
|
@ -164,10 +165,15 @@ export const DecisionSupportFilePage: FunctionComponent<DecisionSupportFilePageP
|
|||
<OptionsSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} /> :
|
||||
null
|
||||
}
|
||||
{
|
||||
state.selectedTabIndex === 2 ?
|
||||
<DecisionReportSection readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} /> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
<div className="column is-4">
|
||||
<MetadataPanel readOnly={!isAuthorized} dsf={state.dsf} updateDSF={updateDSF} />
|
||||
<AppendixPanel dsf={state.dsf} />
|
||||
{/* <AppendixPanel dsf={state.dsf} /> */}
|
||||
<TimelinePanel dsf={state.dsf} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { formatDate } from "../util/date";
|
|||
import { Event } from "../types/event";
|
||||
import { Link } from "react-router-dom";
|
||||
import { WorkgroupLink } from "./WorkgroupLink";
|
||||
import { DecisioSupportFileLink } from "./DecisionSupportFileLink";
|
||||
import { DecisionSupportFileLink } from "./DecisionSupportFileLink";
|
||||
|
||||
export interface TimelineProps {
|
||||
events?: Event[]
|
||||
|
@ -97,6 +97,11 @@ const eventMarkerMap = {
|
|||
<i className="fas fa-users-slash"></i>
|
||||
</div>
|
||||
),
|
||||
"voted": (evt:Event) => (
|
||||
<div className="timeline-marker is-icon is-success">
|
||||
<i className="fas fa-thumbs-up"></i>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
function renderEventMarker(evt: Event) {
|
||||
|
@ -119,7 +124,7 @@ const eventContentMap = {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<span>{`${evt.user.name ? evt.user.name : evt.user.email} a créé le dossier d'aide à la décision `}</span>
|
||||
"<DecisioSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
</React.Fragment>
|
||||
);
|
||||
},
|
||||
|
@ -129,7 +134,7 @@ const eventContentMap = {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<span>{`${evt.user.name ? evt.user.name : evt.user.email} a modifié le titre du dossier d'aide à la décision `}</span>
|
||||
"<DecisioSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ const eventContentMap = {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<span>{`${evt.user.name ? evt.user.name : evt.user.email} a modifié le statut du dossier d'aide à la décision `}</span>
|
||||
"<DecisioSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
@ -167,7 +172,7 @@ const eventContentMap = {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<span>{`${evt.user.name ? evt.user.name : evt.user.email} a modifié le dossier d'aide à la décision `}</span>
|
||||
"<DecisioSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
</React.Fragment>
|
||||
);
|
||||
},
|
||||
|
@ -183,6 +188,14 @@ const eventContentMap = {
|
|||
},
|
||||
},
|
||||
"closed": {
|
||||
"dsf": (evt:Event) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<span>{`${evt.user.name ? evt.user.name : evt.user.email} a clos le dossier d'aide à la décision `}</span>
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />".
|
||||
</React.Fragment>
|
||||
);
|
||||
},
|
||||
"workgroup": (evt:Event) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
@ -192,6 +205,17 @@ const eventContentMap = {
|
|||
);
|
||||
},
|
||||
},
|
||||
"voted": {
|
||||
"dsf": (evt:Event) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<span>{`Le dossier d'aide à la décision `}</span>
|
||||
"<DecisionSupportFileLink decisionSupportFileId={evt.objectId} />"
|
||||
<span> a été voté.</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function renderEventContent(evt: Event) {
|
||||
|
|
|
@ -172,6 +172,16 @@ func NewDefault() *Config {
|
|||
{{ end }}
|
||||
{{- end}}
|
||||
|
||||
{{- with .Voted }}
|
||||
|
||||
Récemment votés
|
||||
---------------
|
||||
|
||||
{{range . -}}
|
||||
- "{{ .Title }}" - {{ $root.BaseURL }}/decisions/{{ .ID }} - voté le {{ .VotedAt.Format "02/01/2006" }}
|
||||
{{ end }}
|
||||
{{- end}}
|
||||
|
||||
{{- with .NewDecisionSupportFiles }}
|
||||
|
||||
Nouveaux dossiers d'aide à la décision
|
||||
|
|
|
@ -72,10 +72,21 @@ func handleUpdateDecisionSupportFile(ctx context.Context, id string, changes *mo
|
|||
eventRepo := model.NewEventRepository(db)
|
||||
|
||||
if changes != nil && changes.Status != nil && prevDsf.Status != *changes.Status {
|
||||
switch *changes.Status {
|
||||
case model.StatusVoted:
|
||||
if _, err := eventRepo.Add(ctx, user, model.EventTypeVoted, dsf); err != nil {
|
||||
return nil, errs.WithStack(err)
|
||||
}
|
||||
case model.StatusClosed:
|
||||
if _, err := eventRepo.Add(ctx, user, model.EventTypeClosed, dsf); err != nil {
|
||||
return nil, errs.WithStack(err)
|
||||
}
|
||||
default:
|
||||
if _, err := eventRepo.Add(ctx, user, model.EventTypeStatusChanged, dsf); err != nil {
|
||||
return nil, errs.WithStack(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if changes != nil && changes.Title != nil && prevDsf.Title != *changes.Title {
|
||||
if _, err := eventRepo.Add(ctx, user, model.EventTypeTitleChanged, dsf); err != nil {
|
||||
|
|
|
@ -3,6 +3,7 @@ package model
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
errs "github.com/pkg/errors"
|
||||
|
@ -75,6 +76,14 @@ func (r *DSFRepository) updateFromChanges(dsf *DecisionSupportFile, changes *Dec
|
|||
}
|
||||
|
||||
if changes.Status != nil {
|
||||
if dsf.Status != StatusVoted && *changes.Status == StatusVoted {
|
||||
dsf.VotedAt = time.Now().UTC()
|
||||
}
|
||||
|
||||
if *changes.Status == StatusClosed {
|
||||
dsf.VotedAt = time.Time{}
|
||||
}
|
||||
|
||||
dsf.Status = *changes.Status
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ func (v *DecisionSupportFileVoter) Vote(ctx context.Context, subject interface{}
|
|||
case ActionRead:
|
||||
return voter.Allow, nil
|
||||
case ActionUpdate:
|
||||
if dsf.Status == StatusClosed || dsf.Status == StatusVoted {
|
||||
return voter.Deny, nil
|
||||
}
|
||||
|
||||
if inWorkgroup(user, dsf.Workgroup) {
|
||||
return voter.Allow, nil
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ const (
|
|||
EventTypeClosed EventType = "closed"
|
||||
EventTypeStatusChanged EventType = "status-changed"
|
||||
EventTypeTitleChanged EventType = "title-changed"
|
||||
EventTypeVoted EventType = "voted"
|
||||
)
|
||||
|
||||
type EventObject interface {
|
||||
|
|
|
@ -73,6 +73,7 @@ func (t *Newsletter) Run() {
|
|||
newWorkgroups := make([]*model.Workgroup, 0)
|
||||
newDecisionSupportFiles := make([]*model.DecisionSupportFile, 0)
|
||||
readyToVote := make([]*model.DecisionSupportFile, 0)
|
||||
voted := make([]*model.DecisionSupportFile, 0)
|
||||
|
||||
workgroupRepo := model.NewWorkgroupRepository(db)
|
||||
dsfRepo := model.NewDSFRepository(db)
|
||||
|
@ -122,6 +123,10 @@ func (t *Newsletter) Run() {
|
|||
if dsf.Status == model.StatusReady {
|
||||
readyToVote = append(readyToVote, dsf)
|
||||
}
|
||||
|
||||
if dsf.Status == model.StatusVoted {
|
||||
voted = append(voted, dsf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +152,7 @@ func (t *Newsletter) Run() {
|
|||
NewWorkgroups []*model.Workgroup
|
||||
NewDecisionSupportFiles []*model.DecisionSupportFile
|
||||
ReadyToVote []*model.DecisionSupportFile
|
||||
Voted []*model.DecisionSupportFile
|
||||
BaseURL string
|
||||
From time.Time
|
||||
To time.Time
|
||||
|
@ -157,6 +163,7 @@ func (t *Newsletter) Run() {
|
|||
NewWorkgroups: newWorkgroups,
|
||||
NewDecisionSupportFiles: newDecisionSupportFiles,
|
||||
ReadyToVote: readyToVote,
|
||||
Voted: voted,
|
||||
From: from.Local(),
|
||||
To: to.Local(),
|
||||
HasEvents: hasEvents,
|
||||
|
|
Loading…
Reference in New Issue