From 6deaa24eeac9179d13081345a74e8589d7e8fbf3 Mon Sep 17 00:00:00 2001 From: Mahmoud Moravej Date: Mon, 5 Feb 2024 01:06:12 -0500 Subject: [PATCH] final design of visions list --- app/@types/graphql/schema.ts | 53 ++- .../FetchContentButton.tsx} | 16 +- .../graphql/analyzeActivity.gql | 0 .../components/ImportModal/ImportModal.tsx | 94 ----- .../ImportModal/graphql/importActivities.gql | 13 - .../components/index.ts | 3 +- .../graphql/query.gql | 12 +- .../route.tsx | 338 +---------------- .../FetchContentButton/FetchContentButton.tsx | 39 ++ .../graphql/analyzeActivity.gql | 8 + .../_dashboard.visions/components/index.ts | 1 + .../_dashboard.visions/graphql/query.gql | 46 +++ app/routes/_dashboard.visions/route.tsx | 346 ++++++++++++++++++ app/routesData.tsx | 21 +- public/images/google-docs-icon.svg | 17 + public/images/google-meet-icon.svg | 1 + 16 files changed, 524 insertions(+), 484 deletions(-) rename app/routes/_dashboard.individuals.($id).visions/components/{AnalyzeButton/AnalyzeButton.tsx => FetchContentButton/FetchContentButton.tsx} (80%) rename app/routes/_dashboard.individuals.($id).visions/components/{AnalyzeButton => FetchContentButton}/graphql/analyzeActivity.gql (100%) delete mode 100644 app/routes/_dashboard.individuals.($id).visions/components/ImportModal/ImportModal.tsx delete mode 100644 app/routes/_dashboard.individuals.($id).visions/components/ImportModal/graphql/importActivities.gql create mode 100644 app/routes/_dashboard.visions/components/FetchContentButton/FetchContentButton.tsx create mode 100644 app/routes/_dashboard.visions/components/FetchContentButton/graphql/analyzeActivity.gql create mode 100644 app/routes/_dashboard.visions/components/index.ts create mode 100644 app/routes/_dashboard.visions/graphql/query.gql create mode 100644 app/routes/_dashboard.visions/route.tsx create mode 100644 public/images/google-docs-icon.svg create mode 100644 public/images/google-meet-icon.svg diff --git a/app/@types/graphql/schema.ts b/app/@types/graphql/schema.ts index 215745d..4aff7e5 100644 --- a/app/@types/graphql/schema.ts +++ b/app/@types/graphql/schema.ts @@ -681,10 +681,11 @@ export type QueryVisionsArgs = { before?: InputMaybe; cycleId?: InputMaybe; first?: InputMaybe; - hasContent?: InputMaybe; individualId?: InputMaybe; last?: InputMaybe; + level?: InputMaybe; orderBy?: InputMaybe>; + organizationOnly?: InputMaybe; }; export type Report = { @@ -766,7 +767,7 @@ export type Vision = { cycle?: Maybe; cycleId?: Maybe; date?: Maybe; - description: Scalars['String']['output']; + description?: Maybe; documentId?: Maybe; documentUrl?: Maybe; hasContent: Scalars['Boolean']['output']; @@ -800,6 +801,12 @@ export type VisionEdge = { node?: Maybe; }; +export enum VisionFilterLevel { + All = 'All', + OrganizationalOnly = 'OrganizationalOnly', + PersonalOnly = 'PersonalOnly' +} + export type VisionType = { __typename?: 'VisionType'; id: Scalars['Int']['output']; @@ -932,13 +939,16 @@ export type GetManagersQueryVariables = Exact<{ [key: string]: never; }>; export type GetManagersQuery = { __typename?: 'Query', managers: { __typename?: 'IndividualConnection', nodes?: Array<{ __typename?: 'Individual', id: number, fullname?: string | null } | null> | null } }; -export type IndividualVisionsQueryVariables = Exact<{ - individualId: Scalars['ID']['input']; +export type VisionsQueryVariables = Exact<{ + individualId?: InputMaybe; cycleId?: InputMaybe; + fetchIndividualId: Scalars['ID']['input']; + fetchIndividualDetails?: InputMaybe; + level?: InputMaybe; }>; -export type IndividualVisionsQuery = { __typename?: 'Query', individual: { __typename?: 'Individual', fullname?: string | null }, cycles: { __typename?: 'CycleConnection', nodes?: Array<{ __typename?: 'Cycle', id: number, title: string } | null> | null }, visions: { __typename?: 'VisionConnection', nodes?: Array<{ __typename?: 'Vision', id: number, documentId?: string | null, documentUrl?: string | null, hasContent: boolean, date?: any | null, validFrom?: any | null, validTo?: any | null, description: string, cycleId?: number | null, individualId?: number | null, visionType: { __typename?: 'VisionType', id: number, title?: string | null }, cycle?: { __typename?: 'Cycle', title: string, from: any, to: any } | null } | null> | null } }; +export type VisionsQuery = { __typename?: 'Query', individual?: { __typename?: 'Individual', fullname?: string | null }, cycles: { __typename?: 'CycleConnection', nodes?: Array<{ __typename?: 'Cycle', id: number, title: string } | null> | null }, visions: { __typename?: 'VisionConnection', nodes?: Array<{ __typename?: 'Vision', id: number, documentId?: string | null, documentUrl?: string | null, hasContent: boolean, date?: any | null, validFrom?: any | null, validTo?: any | null, description?: string | null, cycleId?: number | null, individualId?: number | null, organizationId?: number | null, visionType: { __typename?: 'VisionType', id: number, title?: string | null }, cycle?: { __typename?: 'Cycle', title: string, from: any, to: any } | null } | null> | null } }; export type IndividualsQueryVariables = Exact<{ managerId?: InputMaybe; @@ -1654,9 +1664,9 @@ export function useGetManagersLazyQuery(baseOptions?: Apollo.LazyQueryHookOption export type GetManagersQueryHookResult = ReturnType; export type GetManagersLazyQueryHookResult = ReturnType; export type GetManagersQueryResult = Apollo.QueryResult; -export const IndividualVisionsDocument = gql` - query individualVisions($individualId: ID!, $cycleId: ID) { - individual(id: $individualId) { +export const VisionsDocument = gql` + query visions($individualId: ID, $cycleId: ID, $fetchIndividualId: ID!, $fetchIndividualDetails: Boolean = false, $level: VisionFilterLevel) { + individual(id: $fetchIndividualId) @include(if: $fetchIndividualDetails) { fullname } cycles(orderBy: [{field: "from", direction: "desc"}]) { @@ -1668,6 +1678,7 @@ export const IndividualVisionsDocument = gql` visions( individualId: $individualId cycleId: $cycleId + level: $level orderBy: [{field: "date", direction: "desc"}] ) { nodes { @@ -1685,6 +1696,7 @@ export const IndividualVisionsDocument = gql` description cycleId individualId + organizationId cycle { title from @@ -1696,33 +1708,36 @@ export const IndividualVisionsDocument = gql` `; /** - * __useIndividualVisionsQuery__ + * __useVisionsQuery__ * - * To run a query within a React component, call `useIndividualVisionsQuery` and pass it any options that fit your needs. - * When your component renders, `useIndividualVisionsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useVisionsQuery` and pass it any options that fit your needs. + * When your component renders, `useVisionsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useIndividualVisionsQuery({ + * const { data, loading, error } = useVisionsQuery({ * variables: { * individualId: // value for 'individualId' * cycleId: // value for 'cycleId' + * fetchIndividualId: // value for 'fetchIndividualId' + * fetchIndividualDetails: // value for 'fetchIndividualDetails' + * level: // value for 'level' * }, * }); */ -export function useIndividualVisionsQuery(baseOptions: Apollo.QueryHookOptions) { +export function useVisionsQuery(baseOptions: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(IndividualVisionsDocument, options); + return Apollo.useQuery(VisionsDocument, options); } -export function useIndividualVisionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useVisionsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(IndividualVisionsDocument, options); + return Apollo.useLazyQuery(VisionsDocument, options); } -export type IndividualVisionsQueryHookResult = ReturnType; -export type IndividualVisionsLazyQueryHookResult = ReturnType; -export type IndividualVisionsQueryResult = Apollo.QueryResult; +export type VisionsQueryHookResult = ReturnType; +export type VisionsLazyQueryHookResult = ReturnType; +export type VisionsQueryResult = Apollo.QueryResult; export const IndividualsDocument = gql` query individuals($managerId: ID, $fetchManagerId: ID!, $fetchManagerDetails: Boolean = false, $isManager: Boolean) { individuals(managerId: $managerId, isManager: $isManager, isActive: true) { diff --git a/app/routes/_dashboard.individuals.($id).visions/components/AnalyzeButton/AnalyzeButton.tsx b/app/routes/_dashboard.individuals.($id).visions/components/FetchContentButton/FetchContentButton.tsx similarity index 80% rename from app/routes/_dashboard.individuals.($id).visions/components/AnalyzeButton/AnalyzeButton.tsx rename to app/routes/_dashboard.individuals.($id).visions/components/FetchContentButton/FetchContentButton.tsx index e5f6fa4..4985720 100644 --- a/app/routes/_dashboard.individuals.($id).visions/components/AnalyzeButton/AnalyzeButton.tsx +++ b/app/routes/_dashboard.individuals.($id).visions/components/FetchContentButton/FetchContentButton.tsx @@ -3,13 +3,7 @@ import { BoltIcon } from "@heroicons/react/24/solid"; import { Tooltip, IconButton, Spinner } from "@material-tailwind/react"; import { useState } from "react"; -export function AnalyzeButton({ - activityId, - isAnalyzed, -}: { - activityId: string; - isAnalyzed: boolean; -}) { +export function FetchContentButton({ visionId }: { visionId: string }) { const [isSaving, setIsSaving] = useState(false); const [analyzeActivityMethod] = useAnalyzeActivityWithMinimumResultMutation(); @@ -18,7 +12,7 @@ export function AnalyzeButton({ analyzeActivityMethod({ variables: { input: { - id: activityId, + id: visionId, }, }, onError: (error) => { @@ -31,10 +25,8 @@ export function AnalyzeButton({ }); }; - return isAnalyzed ? ( - "" - ) : ( - + return ( + {isSaving ? ( diff --git a/app/routes/_dashboard.individuals.($id).visions/components/AnalyzeButton/graphql/analyzeActivity.gql b/app/routes/_dashboard.individuals.($id).visions/components/FetchContentButton/graphql/analyzeActivity.gql similarity index 100% rename from app/routes/_dashboard.individuals.($id).visions/components/AnalyzeButton/graphql/analyzeActivity.gql rename to app/routes/_dashboard.individuals.($id).visions/components/FetchContentButton/graphql/analyzeActivity.gql diff --git a/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/ImportModal.tsx b/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/ImportModal.tsx deleted file mode 100644 index 0132354..0000000 --- a/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/ImportModal.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useState } from "react"; -import { - Button, - Dialog, - DialogHeader, - DialogBody, - DialogFooter, - Spinner, -} from "@material-tailwind/react"; -import { useImportActivitiesMutation } from "@app-types/graphql"; - -export interface ImportModalProps { - individualId: number; - open: boolean; - handleClose: (imported: boolean) => void; -} - -export function ImportModal({ - open, - handleClose, - individualId, -}: ImportModalProps) { - const [importing, setImporting] = useState(false); - const [importMethod] = useImportActivitiesMutation(); - - const handleImport = async () => { - setImporting(true); - - importMethod({ - variables: { - individualId: individualId, - }, - onError: (error) => { - setImporting(false); - alert(error.message); - }, - - onCompleted: (data) => { - setImporting(false); - // - alert(`Imported ${data.importActivities?.totalCount} activities!`); - handleClose(true); - }, - }); - }; - - return ( - <> - - Import Activities - - Activities are getting imported every 5 minutes. But, if you want to - sync them manually, you can do it here. This will try to import the - following activities: -
    -
  • Github Pull Request Contributions
  • -
  • Github Reviews
  • -
  • Github Issue Contributions
  • -
  • 1:1s from Google Calendar and Fellow
  • -
  • Recorded meetings from Google Meet
  • -
  • etc...
  • -
-
- - - - -
- - ); -} diff --git a/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/graphql/importActivities.gql b/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/graphql/importActivities.gql deleted file mode 100644 index 336b074..0000000 --- a/app/routes/_dashboard.individuals.($id).visions/components/ImportModal/graphql/importActivities.gql +++ /dev/null @@ -1,13 +0,0 @@ -mutation importActivities($individualId: Int!) { - importActivities(input: { individualId: $individualId }) { - totalCount - activities { - id - title - isAnalyzed - date - channelId - channelActivityUrl - } - } -} diff --git a/app/routes/_dashboard.individuals.($id).visions/components/index.ts b/app/routes/_dashboard.individuals.($id).visions/components/index.ts index fddf084..fe35fad 100644 --- a/app/routes/_dashboard.individuals.($id).visions/components/index.ts +++ b/app/routes/_dashboard.individuals.($id).visions/components/index.ts @@ -1,2 +1 @@ -export * from "./ImportModal/ImportModal"; -export * from "./AnalyzeButton/AnalyzeButton"; +export * from "./FetchContentButton/FetchContentButton"; diff --git a/app/routes/_dashboard.individuals.($id).visions/graphql/query.gql b/app/routes/_dashboard.individuals.($id).visions/graphql/query.gql index b11924f..685757e 100644 --- a/app/routes/_dashboard.individuals.($id).visions/graphql/query.gql +++ b/app/routes/_dashboard.individuals.($id).visions/graphql/query.gql @@ -1,5 +1,11 @@ -query individualVisions($individualId: ID!, $cycleId: ID) { - individual(id: $individualId) { +query visions( + $individualId: ID + $cycleId: ID + $fetchIndividualId: ID! + $fetchIndividualDetails: Boolean = false + $level: VisionFilterLevel +) { + individual(id: $fetchIndividualId) @include(if: $fetchIndividualDetails) { fullname } cycles(orderBy: [{ field: "from", direction: "desc" }]) { @@ -11,6 +17,7 @@ query individualVisions($individualId: ID!, $cycleId: ID) { visions( individualId: $individualId cycleId: $cycleId + level: $level orderBy: [{ field: "date", direction: "desc" }] ) { nodes { @@ -28,6 +35,7 @@ query individualVisions($individualId: ID!, $cycleId: ID) { description cycleId individualId + organizationId cycle { title from diff --git a/app/routes/_dashboard.individuals.($id).visions/route.tsx b/app/routes/_dashboard.individuals.($id).visions/route.tsx index d1f236e..f5e3855 100644 --- a/app/routes/_dashboard.individuals.($id).visions/route.tsx +++ b/app/routes/_dashboard.individuals.($id).visions/route.tsx @@ -1,337 +1,7 @@ -import { useIndividualVisionsQuery } from "@app-types/graphql"; -import { - ArrowTopRightOnSquareIcon, - DocumentChartBarIcon, -} from "@heroicons/react/24/solid"; -import { - Card, - CardHeader, - Typography, - Button, - CardBody, - Chip, - CardFooter, - IconButton, - Tooltip, - Spinner, - Tab, - Tabs, - TabsHeader, - Select, - Option, -} from "@material-tailwind/react"; -import { LoaderFunction, redirect } from "@remix-run/node"; -import { Link, useParams, useLocation, useNavigate } from "@remix-run/react"; -import { useState } from "react"; -import { authenticator } from "~/services/auth.server"; -import { AnalyzeButton, ImportModal } from "./components"; -import { noNull } from "~/utils"; +import Visions, { loader as visionsloader } from "../_dashboard.visions/route"; -type FilterType = "all" | "personal" | "organizational"; -const TABS: { label: string; value: FilterType }[] = [ - { - label: "All", - value: "all", - }, - { - label: "Personal", - value: "personal", - }, - { - label: "Organizational Assigned", - value: "organizational", - }, -]; -const TABLE_HEAD = [ - "Title", - "Personal?", - "Period", - "Content Loaded?", - "Content Url", -]; +export let loader = visionsloader; -export let loader: LoaderFunction = async ({ request }) => { - //we should completely change the following appraoch - let user = await authenticator.isAuthenticated(request); - if (!user) return redirect("/login"); - return null; -}; - -export default function Visions() { - let { id: individualId } = useParams(); - if (individualId == null) throw new Error("id is null"); - - const [filter, setFilter] = useState("all"); - const [importDialogOpen, setImportDialogOpen] = useState(false); - - const { search } = useLocation(); - const navigate = useNavigate(); - const queryParams = new URLSearchParams(search); - const [selectedCycle, setSelectedCycle] = useState( - Number(queryParams.get("cycleid") ?? 0), - ); - - const { data, loading, error, refetch } = useIndividualVisionsQuery({ - variables: { - individualId: individualId, - cycleId: selectedCycle === 0 ? undefined : selectedCycle?.toString(), - }, - fetchPolicy: "network-only", - }); - - if (error) return

{JSON.stringify(error)}

; - if (loading || data?.visions?.nodes == null) - return ; - - const pageTitle = `${data?.individual.fullname ?? ""}'s visions`; - - const visions = data.visions.nodes.filter(noNull).map((node) => ({ - id: node.id, - title: node.description ?? "-", - visionType: node.visionType?.title ?? "-", - url: node.documentUrl, - date: new Date(node.date), - valid_from: new Date(node.cycle ? node.cycle.from : node.validFrom), - valid_to: new Date(node.cycle ? node.cycle.from : node.validTo), - hasContent: node.hasContent, - cycleTitle: node.cycle?.title ?? "-", - isPersonal: node.individualId != null ? true : false, - })); - - const cycles = data.cycles?.nodes?.filter(noNull).map((node) => ({ - id: node.id, - title: node.title, - })); - - cycles?.unshift({ id: 0, title: "All Cycles" }); - - const handleImportModalClose = (imported: boolean) => { - setImportDialogOpen(false); - if (imported) { - refetch(); - } - }; - - const importDialog = ( - - ); - - const handle_cycle_change = (value: string | undefined) => { - value = value ?? "0"; - setSelectedCycle(parseInt(value)); - - if (value == "0") queryParams.delete("cycleid"); - else queryParams.set("cycleid", value); - - navigate({ - search: queryParams.toString(), - }); - }; - - return ( - <> - {importDialog} - - -
-
- - {pageTitle} - -
-
- -
-
-
- {" "} - - - {TABS.map(({ label, value }) => ( - setFilter(value)} - > -   {label}   - - ))} - - -
-
- - - - - - {TABLE_HEAD.map((head) => ( - - ))} - - - - {visions.map( - ( - { - title, - visionType, - id, - url, - hasContent, - valid_from, - valid_to, - cycleTitle, - isPersonal, - }, - index, - ) => { - const isLast = index === visions.length - 1; - const classes = isLast - ? "p-4" - : "p-4 border-b border-blue-gray-50"; - - return ( - - - - - - - - ); - }, - )} - -
- - {head} - -
-
- -
- - - {title} - - - - {visionType} - -
-
-
-
- -
-
-
- - {valid_from.toLocaleString("en-CA", { - year: "numeric", - month: "numeric", - day: "numeric", - }) + " - "} - {valid_to.toLocaleString("en-CA", { - year: "numeric", - month: "numeric", - day: "numeric", - })} - {cycleTitle ? "(" + cycleTitle + ")" : ""} - -
-
-
- -
-
- - - - - - - - -
-
- - - Page 1 of 10 - -
- - -
-
-
- - ); +export default function IndividualVisions() { + return ; } diff --git a/app/routes/_dashboard.visions/components/FetchContentButton/FetchContentButton.tsx b/app/routes/_dashboard.visions/components/FetchContentButton/FetchContentButton.tsx new file mode 100644 index 0000000..4985720 --- /dev/null +++ b/app/routes/_dashboard.visions/components/FetchContentButton/FetchContentButton.tsx @@ -0,0 +1,39 @@ +import { useAnalyzeActivityWithMinimumResultMutation } from "@app-types/graphql"; +import { BoltIcon } from "@heroicons/react/24/solid"; +import { Tooltip, IconButton, Spinner } from "@material-tailwind/react"; +import { useState } from "react"; + +export function FetchContentButton({ visionId }: { visionId: string }) { + const [isSaving, setIsSaving] = useState(false); + const [analyzeActivityMethod] = useAnalyzeActivityWithMinimumResultMutation(); + + var onAnalyzeAndSave = function () { + setIsSaving(true); + analyzeActivityMethod({ + variables: { + input: { + id: visionId, + }, + }, + onError: (error) => { + setIsSaving(false); + alert(error.message); + }, + onCompleted: (data) => { + setIsSaving(false); + }, + }); + }; + + return ( + + + {isSaving ? ( + + ) : ( + + )} + + + ); +} diff --git a/app/routes/_dashboard.visions/components/FetchContentButton/graphql/analyzeActivity.gql b/app/routes/_dashboard.visions/components/FetchContentButton/graphql/analyzeActivity.gql new file mode 100644 index 0000000..fc5e68b --- /dev/null +++ b/app/routes/_dashboard.visions/components/FetchContentButton/graphql/analyzeActivity.gql @@ -0,0 +1,8 @@ +mutation AnalyzeActivityWithMinimumResult($input: AnalyzeActivityInput!) { + analyzeActivity(input: $input) { + activity { + id + isAnalyzed + } + } +} diff --git a/app/routes/_dashboard.visions/components/index.ts b/app/routes/_dashboard.visions/components/index.ts new file mode 100644 index 0000000..fe35fad --- /dev/null +++ b/app/routes/_dashboard.visions/components/index.ts @@ -0,0 +1 @@ +export * from "./FetchContentButton/FetchContentButton"; diff --git a/app/routes/_dashboard.visions/graphql/query.gql b/app/routes/_dashboard.visions/graphql/query.gql new file mode 100644 index 0000000..685757e --- /dev/null +++ b/app/routes/_dashboard.visions/graphql/query.gql @@ -0,0 +1,46 @@ +query visions( + $individualId: ID + $cycleId: ID + $fetchIndividualId: ID! + $fetchIndividualDetails: Boolean = false + $level: VisionFilterLevel +) { + individual(id: $fetchIndividualId) @include(if: $fetchIndividualDetails) { + fullname + } + cycles(orderBy: [{ field: "from", direction: "desc" }]) { + nodes { + id + title + } + } + visions( + individualId: $individualId + cycleId: $cycleId + level: $level + orderBy: [{ field: "date", direction: "desc" }] + ) { + nodes { + id + visionType { + id + title + } + documentId + documentUrl + hasContent + date + validFrom + validTo + description + cycleId + individualId + organizationId + cycle { + title + from + to + } + } + } +} diff --git a/app/routes/_dashboard.visions/route.tsx b/app/routes/_dashboard.visions/route.tsx new file mode 100644 index 0000000..c5d2779 --- /dev/null +++ b/app/routes/_dashboard.visions/route.tsx @@ -0,0 +1,346 @@ +import { VisionFilterLevel, useVisionsQuery } from "@app-types/graphql"; +import { + ArrowTopRightOnSquareIcon, + DocumentChartBarIcon, +} from "@heroicons/react/24/solid"; +import { + Card, + CardHeader, + Typography, + Button, + CardBody, + Chip, + CardFooter, + IconButton, + Tooltip, + Spinner, + Tab, + Tabs, + TabsHeader, + Select, + Option, +} from "@material-tailwind/react"; +import { LoaderFunction, redirect } from "@remix-run/node"; +import { Link, useParams, useLocation, useNavigate } from "@remix-run/react"; +import { useState } from "react"; +import { authenticator } from "~/services/auth.server"; +import { FetchContentButton } from "./components"; +import { noNull } from "~/utils"; + +type FilterType = "all" | "personal" | "organizational"; +const TABS: { label: string; value: FilterType }[] = [ + { + label: "All", + value: "all", + }, + { + label: "Personal", + value: "personal", + }, + { + label: "Organizational", + value: "organizational", + }, +]; +const TABLE_HEAD_PERSONAL = [ + "Title", + "Level", + "Period", + "Content Loaded?", + "Content Url", +]; + +const ORG_TABLE_HEAD_ORGANIZATIONAL = [ + "Title", + "Period", + "Content Loaded?", + "Content Url", +]; + +export let loader: LoaderFunction = async ({ request }) => { + //we should completely change the following appraoch + let user = await authenticator.isAuthenticated(request); + if (!user) return redirect("/login"); + return null; +}; + +export default function Visions() { + let { id: individualId } = useParams(); + + const [filter, setFilter] = useState("all"); + + const { search } = useLocation(); + const navigate = useNavigate(); + const queryParams = new URLSearchParams(search); + const [selectedCycle, setSelectedCycle] = useState( + Number(queryParams.get("cycleid") ?? 0), + ); + + const isPersonal = individualId != null; + + const { data, loading, error } = useVisionsQuery({ + variables: { + fetchIndividualDetails: isPersonal, + fetchIndividualId: individualId ?? "NoteUsed", + individualId: individualId, + cycleId: selectedCycle === 0 ? undefined : selectedCycle?.toString(), + level: + filter === "personal" + ? VisionFilterLevel.PersonalOnly + : filter === "organizational" + ? VisionFilterLevel.OrganizationalOnly + : undefined, + }, + fetchPolicy: "network-only", + }); + + if (error) return

{JSON.stringify(error)}

; + if (loading || data?.visions?.nodes == null) + return ; + + const pageTitle = data?.individual + ? `${data?.individual.fullname ?? ""}'s visions` + : "Organization's visions"; + + const visions = data.visions.nodes.filter(noNull).map((node) => ({ + id: node.id, + title: node.description ?? node.visionType.title, + visionType: node.visionType?.title ?? "-", + url: node.documentUrl, + date: new Date(node.date), + valid_from: new Date(node.cycle ? node.cycle.from : node.validFrom), + valid_to: new Date(node.cycle ? node.cycle.to : node.validTo), + hasContent: node.hasContent, + cycleTitle: node.cycle?.title, + isPersonalVision: node.organizationId ? false : true, + })); + + const cycles = data.cycles?.nodes?.filter(noNull).map((node) => ({ + id: node.id, + title: node.title, + })); + + cycles?.unshift({ id: 0, title: "All Cycles" }); + + const handle_cycle_change = (value: string | undefined) => { + value = value ?? "0"; + setSelectedCycle(parseInt(value)); + + if (value == "0") queryParams.delete("cycleid"); + else queryParams.set("cycleid", value); + + navigate({ + search: queryParams.toString(), + }); + }; + + return ( + <> + + +
+
+ + {pageTitle} + +
+
+ +
+
+ {isPersonal ? ( +
+ {" "} + + + {TABS.map(({ label, value }) => ( + setFilter(value)} + > +   {label}   + + ))} + + +
+ ) : null} +
+ + + + + + {(isPersonal + ? TABLE_HEAD_PERSONAL + : ORG_TABLE_HEAD_ORGANIZATIONAL + ).map((head) => ( + + ))} + + + + {visions.map( + ( + { + title, + visionType, + id, + url, + hasContent, + valid_from, + valid_to, + cycleTitle, + isPersonalVision, + }, + index, + ) => { + const isLast = index === visions.length - 1; + const classes = isLast + ? "p-4" + : "p-4 border-b border-blue-gray-50"; + + return ( + + + {isPersonal ? ( + + ) : null} + + + + + ); + }, + )} + +
+ + {head} + +
+
+ google doc +
+ + + {title} + + + + {visionType} + +
+
+
+
+ +
+
+
+ + {cycleTitle ? ( + cycleTitle + ) : ( + <> + {valid_from.toLocaleString("en-CA", { + year: "numeric", + month: "numeric", + day: "numeric", + }) + " "} + to + {" " + + valid_to.toLocaleString("en-CA", { + year: "numeric", + month: "numeric", + day: "numeric", + })} + + )} + +
+
+
+ +
+
+ + + + + + + + +
+
+ + + Page 1 of 10 + +
+ + +
+
+
+ + ); +} diff --git a/app/routesData.tsx b/app/routesData.tsx index 0d9f26f..b678f50 100644 --- a/app/routesData.tsx +++ b/app/routesData.tsx @@ -35,6 +35,16 @@ export const routes: RouteData[] = [ name: "Home", path: "/", }, + { + icon: , + name: "Coach me!", + path: "/individuals/#{myId}/coach", + }, + ], + }, + { + title: "Organization", + pages: [ { icon: , name: "My team", @@ -42,13 +52,13 @@ export const routes: RouteData[] = [ }, { icon: , - name: "Organization", + name: "People", path: "/individuals", }, { icon: , - name: "Rules", - path: "/rules", + name: "Visions", + path: "/visions", }, { icon: , @@ -60,11 +70,6 @@ export const routes: RouteData[] = [ { title: "My profile", pages: [ - { - icon: , - name: "Coach me!", - path: "/individuals/#{myId}/coach", - }, { icon: , name: "My activities", diff --git a/public/images/google-docs-icon.svg b/public/images/google-docs-icon.svg new file mode 100644 index 0000000..ab2a4d6 --- /dev/null +++ b/public/images/google-docs-icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/public/images/google-meet-icon.svg b/public/images/google-meet-icon.svg new file mode 100644 index 0000000..249eca9 --- /dev/null +++ b/public/images/google-meet-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file