diff --git a/client/src/components/DecisionSupportFileLink.tsx b/client/src/components/DecisionSupportFileLink.tsx index 4e943e9..542c94a 100644 --- a/client/src/components/DecisionSupportFileLink.tsx +++ b/client/src/components/DecisionSupportFileLink.tsx @@ -7,7 +7,7 @@ export interface DecisioSupportFileLinkProps { decisionSupportFileId: number } -export const DecisioSupportFileLink: FunctionComponent = ({ decisionSupportFileId }) => { +export const DecisionSupportFileLink: FunctionComponent = ({ decisionSupportFileId }) => { const { decisionSupportFiles } = useDecisionSupportFiles({ fetchPolicy: "cache-first", variables: { diff --git a/client/src/components/DecisionSupportFilePage/DecisionReportSection.tsx b/client/src/components/DecisionSupportFilePage/DecisionReportSection.tsx new file mode 100644 index 0000000..315bb48 --- /dev/null +++ b/client/src/components/DecisionSupportFilePage/DecisionReportSection.tsx @@ -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 = ({ 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) => { + const target = evt.currentTarget; + const value = target.hasOwnProperty('checked') ? target.checked : target.value; + setState(state => ({ ...state, changed: true, section: {...state.section, [attrName]: value }})); + }; + + return ( +
+
+
+ +
+ +
+

Penser à indiquer le résultat du vote et les éléments de contexte liés à la prise de décision.

+
+
+
+ ); +}; \ No newline at end of file diff --git a/client/src/components/DecisionSupportFilePage/DecisionSupportFilePage.tsx b/client/src/components/DecisionSupportFilePage/DecisionSupportFilePage.tsx index a2dd2fa..979da13 100644 --- a/client/src/components/DecisionSupportFilePage/DecisionSupportFilePage.tsx +++ b/client/src/components/DecisionSupportFilePage/DecisionSupportFilePage.tsx @@ -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 = () => { - const { id } = useParams(); + const { id } = useParams(); const history = useHistory(); const { decisionSupportFiles } = useDecisionSupportFiles({ variables:{ @@ -164,10 +165,15 @@ export const DecisionSupportFilePage: FunctionComponent : null } + { + state.selectedTabIndex === 2 ? + : + null + }
- + {/* */}
diff --git a/client/src/components/Timeline.tsx b/client/src/components/Timeline.tsx index 4da19fc..8ad61f0 100644 --- a/client/src/components/Timeline.tsx +++ b/client/src/components/Timeline.tsx @@ -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 = { ), + "voted": (evt:Event) => ( +
+ +
+ ), } function renderEventMarker(evt: Event) { @@ -119,7 +124,7 @@ const eventContentMap = { return ( {`${evt.user.name ? evt.user.name : evt.user.email} a créé le dossier d'aide à la décision `} - "". + "". ); }, @@ -129,7 +134,7 @@ const eventContentMap = { return ( {`${evt.user.name ? evt.user.name : evt.user.email} a modifié le titre du dossier d'aide à la décision `} - "". + "". ) } @@ -139,7 +144,7 @@ const eventContentMap = { return ( {`${evt.user.name ? evt.user.name : evt.user.email} a modifié le statut du dossier d'aide à la décision `} - "". + "". ) } @@ -167,7 +172,7 @@ const eventContentMap = { return ( {`${evt.user.name ? evt.user.name : evt.user.email} a modifié le dossier d'aide à la décision `} - "". + "". ); }, @@ -183,6 +188,14 @@ const eventContentMap = { }, }, "closed": { + "dsf": (evt:Event) => { + return ( + + {`${evt.user.name ? evt.user.name : evt.user.email} a clos le dossier d'aide à la décision `} + "". + + ); + }, "workgroup": (evt:Event) => { return ( @@ -192,6 +205,17 @@ const eventContentMap = { ); }, }, + "voted": { + "dsf": (evt:Event) => { + return ( + + {`Le dossier d'aide à la décision `} + "" + a été voté. + + ); + }, + }, }; function renderEventContent(evt: Event) { diff --git a/internal/graph/dsf_handler.go b/internal/graph/dsf_handler.go index 0df7709..430449a 100644 --- a/internal/graph/dsf_handler.go +++ b/internal/graph/dsf_handler.go @@ -72,8 +72,19 @@ func handleUpdateDecisionSupportFile(ctx context.Context, id string, changes *mo eventRepo := model.NewEventRepository(db) if changes != nil && changes.Status != nil && prevDsf.Status != *changes.Status { - if _, err := eventRepo.Add(ctx, user, model.EventTypeStatusChanged, dsf); err != nil { - return nil, errs.WithStack(err) + 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) + } } } diff --git a/internal/model/dsf_repository.go b/internal/model/dsf_repository.go index ed0400d..4660713 100644 --- a/internal/model/dsf_repository.go +++ b/internal/model/dsf_repository.go @@ -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 } diff --git a/internal/model/dsf_voter.go b/internal/model/dsf_voter.go index baa4c7d..3110a8a 100644 --- a/internal/model/dsf_voter.go +++ b/internal/model/dsf_voter.go @@ -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 } diff --git a/internal/model/event.go b/internal/model/event.go index 164dc30..b4d4fdc 100644 --- a/internal/model/event.go +++ b/internal/model/event.go @@ -14,6 +14,7 @@ const ( EventTypeClosed EventType = "closed" EventTypeStatusChanged EventType = "status-changed" EventTypeTitleChanged EventType = "title-changed" + EventTypeVoted EventType = "voted" ) type EventObject interface {