diff --git a/client/src/components/HomePage/DecisionSupportFilePanel.tsx b/client/src/components/HomePage/DecisionSupportFilePanel.tsx
new file mode 100644
index 0000000..d241ff8
--- /dev/null
+++ b/client/src/components/HomePage/DecisionSupportFilePanel.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import { DecisionSupportFile, DecisionSupportFileStatus } from '../../types/decision';
+import { ItemPanel, TabDefinition, Item } from './ItemPanel';
+import { useUserProfile } from '../../gql/queries/profile';
+import { inWorkgroup } from '../../types/workgroup';
+import { useDecisions } from '../../gql/queries/decisions';
+
+export function DecisionSupportFilePanel() {
+ const { user } = useUserProfile();
+ const { decisions } = useDecisions();
+
+ const tabs: TabDefinition[] = [
+ {
+ label: 'Mes dossiers en cours',
+ itemFilter: (item: Item) => {
+ const dsf = item as DecisionSupportFile;
+ return dsf.status === DecisionSupportFileStatus.Opened && inWorkgroup(user, dsf.workgroup);
+ }
+ },
+ {
+ label: 'Ouverts',
+ itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Opened
+ },
+ {
+ label: 'Clos',
+ itemFilter: (item: Item) => (item as DecisionSupportFile).status === DecisionSupportFileStatus.Closed
+ },
+ ];
+
+
+ return (
+
item.id}
+ itemLabel={item => item.title}
+ itemUrl={item => `/decisions/${item.id}`}
+ />
+ );
+}
\ No newline at end of file
diff --git a/client/src/components/HomePage/ItemPanel.tsx b/client/src/components/HomePage/ItemPanel.tsx
new file mode 100644
index 0000000..054e69d
--- /dev/null
+++ b/client/src/components/HomePage/ItemPanel.tsx
@@ -0,0 +1,121 @@
+import React, { FunctionComponent, useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import { WithLoader } from "../WithLoader";
+
+export interface Item {
+ id: string
+ [propName: string]: any;
+}
+
+export interface TabDefinition {
+ label: string
+ itemFilter?: (item: Item) => boolean
+}
+
+
+export interface ItemPanelProps {
+ className?: string
+ itemIconClassName?: string
+ title?: string
+ newItemUrl: string
+ isLoading?: boolean
+ items: Item[]
+ tabs?: TabDefinition[],
+ itemKey: (item: Item, index: number) => string
+ itemLabel: (item: Item, index: number) => string
+ itemUrl: (item: Item, index: number) => string
+}
+
+export const ItemPanel: FunctionComponent = (props) => {
+ const {
+ title, className, newItemUrl,
+ itemKey, itemLabel,
+ itemIconClassName, itemUrl
+ } = props;
+
+ const [ state, setState ] = useState({ selectedTab: 0, filteredItems: [] });
+
+ const filterItemsForTab = (tab: TabDefinition, items: Item[]) => {
+ const itemFilter = tab && typeof tab.itemFilter === 'function' ? tab.itemFilter : () => true;
+ return items.filter(itemFilter);
+ };
+
+ const selectTab = (tabIndex: number) => {
+ setState(state => {
+ const { tabs, items } = props;
+ const newTab = Array.isArray(tabs) && tabs.length > 0 ? tabs[tabIndex] : null;
+ return {
+ ...state,
+ selectedTab: tabIndex,
+ filteredItems: filterItemsForTab(newTab, items)
+ };
+ });
+ };
+
+ useEffect(() => {
+ setState(state => {
+ const { tabs, items } = props;
+ const newTab = Array.isArray(tabs) && tabs.length > 0 ? tabs[state.selectedTab] : null;
+ return {
+ ...state,
+ filteredItems: filterItemsForTab(newTab, items),
+ }
+ });
+ }, [props.items, props.tabs]);
+
+ const itemElements = state.filteredItems.map((item: Item, i: number) => {
+ return (
+
+
+
+
+ {itemLabel(item, i)}
+
+ );
+ });
+
+ const tabs = props.tabs || [];
+
+ return (
+
+ )
+};
\ No newline at end of file
diff --git a/client/src/components/HomePage/WorkgroupsPanel.tsx b/client/src/components/HomePage/WorkgroupsPanel.tsx
index 439b931..c7ea0d1 100644
--- a/client/src/components/HomePage/WorkgroupsPanel.tsx
+++ b/client/src/components/HomePage/WorkgroupsPanel.tsx
@@ -1,98 +1,45 @@
import React, { useEffect, useState } from 'react';
-import { Workgroup } from '../../types/workgroup';
+import { Workgroup, inWorkgroup } from '../../types/workgroup';
import { User } from '../../types/user';
import { Link } from 'react-router-dom';
-import { useWorkgroupsQuery } from '../../gql/queries/workgroups';
-import { useUserProfileQuery } from '../../gql/queries/profile';
+import { useWorkgroupsQuery, useWorkgroups } from '../../gql/queries/workgroups';
+import { useUserProfileQuery, useUserProfile } from '../../gql/queries/profile';
import { WithLoader } from '../WithLoader';
+import { ItemPanel, Item } from './ItemPanel';
export function WorkgroupsPanel() {
- const workgroupsQuery = useWorkgroupsQuery();
- const userProfileQuery = useUserProfileQuery();
- const [ state, setState ] = useState({ selectedTab: 0 });
+ const { workgroups } = useWorkgroups();
+ const { user } = useUserProfile();
- const isLoading = userProfileQuery.loading || workgroupsQuery.loading;
- const { userProfile } = (userProfileQuery.data || {});
- const { workgroups } = (workgroupsQuery.data || {});
-
- const filterTabs = [
+ const tabs = [
{
label: "Mes groupes en cours",
- filter: workgroups => workgroups.filter((wg: Workgroup) => {
- return wg.closedAt === null && wg.members.some((u: User) => u.id === (userProfile ? userProfile.id : ''));
- })
+ itemFilter: (item: Item) => {
+ const wg = item as Workgroup;
+ return wg.closedAt === null && inWorkgroup(user, wg);
+ }
},
{
label: "Ouverts",
- filter: workgroups => workgroups.filter((wg: Workgroup) => !wg.closedAt)
+ itemFilter: (item: Item) => !(item as Workgroup).closedAt
},
{
label: "Clos",
- filter: workgroups => workgroups.filter((wg: Workgroup) => !!wg.closedAt)
+ itemFilter: (item: Item) => !!(item as Workgroup).closedAt
}
];
-
- const selectTab = (tabIndex: number) => {
- setState(state => ({ ...state, selectedTab: tabIndex }));
- };
- let workgroupsItems = [];
-
- workgroupsItems = filterTabs[state.selectedTab].filter(workgroups || []).map((wg: Workgroup) => {
- return (
-
-
-
-
- {wg.name}
-
- );
- });
-
return (
-
- )
+ item.id}
+ itemLabel={item => item.name}
+ itemUrl={item => `/workgroups/${item.id}`}
+ />
+ );
}
\ No newline at end of file
diff --git a/client/src/gql/queries/decisions.tsx b/client/src/gql/queries/decisions.tsx
new file mode 100644
index 0000000..f504155
--- /dev/null
+++ b/client/src/gql/queries/decisions.tsx
@@ -0,0 +1,32 @@
+import { gql, useQuery } from '@apollo/client';
+import { DecisionSupportFile } from '../../types/decision';
+import { useState, useEffect } from 'react';
+import { useGraphQLData } from './helper';
+
+const QUERY_DECISIONS = gql`
+ query decisions($filter: DecisionFilter) {
+ decisions(filter: $filter) {
+ id,
+ title,
+ sections
+ createdAt,
+ closedAt,
+ votedAt,
+ workgroup {
+ id,
+ name
+ }
+ }
+ }
+`;
+
+export function useDecisionsQuery(options = {}) {
+ return useQuery(QUERY_DECISIONS, options);
+}
+
+export function useDecisions() {
+ const { data, loading, error } = useGraphQLData(
+ QUERY_DECISIONS, 'decicions', []
+ );
+ return { decisions: data, loading, error };
+}
\ No newline at end of file
diff --git a/client/src/gql/queries/helper.ts b/client/src/gql/queries/helper.ts
new file mode 100644
index 0000000..f159356
--- /dev/null
+++ b/client/src/gql/queries/helper.ts
@@ -0,0 +1,11 @@
+import { useQuery, DocumentNode } from "@apollo/client";
+import { useState, useEffect } from "react";
+
+export function useGraphQLData(q: DocumentNode, key: string, defaultValue: T) {
+ const query = useQuery(q);
+ const [ data, setData ] = useState(defaultValue);
+ useEffect(() => {
+ setData(query.data ? query.data[key] as T : defaultValue);
+ }, [query.loading, query.data]);
+ return { data, loading: query.loading, error: query.error };
+}
\ No newline at end of file
diff --git a/client/src/gql/queries/profile.tsx b/client/src/gql/queries/profile.tsx
index 573fc04..b8f9bae 100644
--- a/client/src/gql/queries/profile.tsx
+++ b/client/src/gql/queries/profile.tsx
@@ -1,4 +1,7 @@
import { gql, useQuery } from '@apollo/client';
+import { User } from '../../types/user';
+import { useState, useEffect } from 'react';
+import { useGraphQLData } from './helper';
const QUERY_USER_PROFILE = gql`
query userProfile {
@@ -13,4 +16,11 @@ query userProfile {
export function useUserProfileQuery() {
return useQuery(QUERY_USER_PROFILE);
+}
+
+export function useUserProfile() {
+ const { data, loading, error } = useGraphQLData(
+ QUERY_USER_PROFILE, 'userProfile', {id: '', email: ''}
+ );
+ return { user: data, loading, error };
}
\ No newline at end of file
diff --git a/client/src/gql/queries/workgroups.tsx b/client/src/gql/queries/workgroups.tsx
index 4693edd..261ceeb 100644
--- a/client/src/gql/queries/workgroups.tsx
+++ b/client/src/gql/queries/workgroups.tsx
@@ -1,4 +1,6 @@
import { gql, useQuery } from '@apollo/client';
+import { Workgroup } from '../../types/workgroup';
+import { useGraphQLData } from './helper';
const QUERY_WORKGROUP = gql`
query workgroups($filter: WorkgroupsFilter) {
@@ -18,4 +20,11 @@ const QUERY_WORKGROUP = gql`
export function useWorkgroupsQuery(options = {}) {
return useQuery(QUERY_WORKGROUP, options);
+}
+
+export function useWorkgroups() {
+ const { data, loading, error } = useGraphQLData(
+ QUERY_WORKGROUP, 'workgroups', []
+ );
+ return { workgroups: data, loading, error };
}
\ No newline at end of file
diff --git a/client/src/types/decision.tsx b/client/src/types/decision.tsx
new file mode 100644
index 0000000..d38c619
--- /dev/null
+++ b/client/src/types/decision.tsx
@@ -0,0 +1,23 @@
+import { Workgroup } from "./workgroup";
+
+export enum DecisionSupportFileStatus {
+ Opened = "opened",
+ Voted = "voted",
+ Closed = "closed",
+}
+
+export interface DecisionSupportFileSection {
+ id: string
+}
+
+// aka Dossier d'aide à la décision
+export interface DecisionSupportFile {
+ id: string
+ title: string
+ sections: DecisionSupportFileSection[]
+ status: DecisionSupportFileStatus
+ workgroup: Workgroup,
+ createdAt: Date
+ votedAt?: Date
+ closedAt?: Date
+}
\ No newline at end of file
diff --git a/client/src/types/workgroup.ts b/client/src/types/workgroup.ts
index 459f29a..9546278 100644
--- a/client/src/types/workgroup.ts
+++ b/client/src/types/workgroup.ts
@@ -1,9 +1,18 @@
import { User } from "./user";
-
export interface Workgroup {
id: string
name: string
createdAt: Date
closedAt: Date
- members: [User]
+ members: User[]
+}
+
+export function inWorkgroup(u: User, wg: Workgroup): boolean {
+ for (let m, i = 0; (m = wg.members[i]); i++) {
+ if(m.id === u.id) {
+ return true;
+ }
+ }
+
+ return false;
}
\ No newline at end of file