From e5d98b0180a6bd8cad6fb9790c0d2db8726d2ad2 Mon Sep 17 00:00:00 2001 From: David Nicholas Date: Thu, 6 Feb 2025 09:45:43 -0800 Subject: [PATCH] pull out to hooks --- public/canjs/routing/route-data/route-data.js | 4 + .../components/IssueSource/IssueSource.tsx | 141 +++++------------- .../StatusSelect/ExcludedStatusSelect.tsx | 6 +- .../components/StatusSelect/index.ts | 1 + .../IssueSource/hooks/useJQL/index.ts | 1 + .../IssueSource/hooks/useJQL/useJQL.ts | 47 ++++++ .../hooks/useRawIssueRequestData/index.ts | 1 + .../useRawIssueRequestData.ts | 38 +++++ public/timeline-report.js | 2 +- 9 files changed, 134 insertions(+), 107 deletions(-) create mode 100644 public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/index.ts create mode 100644 public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/useJQL.ts create mode 100644 public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/index.ts create mode 100644 public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/useRawIssueRequestData.ts diff --git a/public/canjs/routing/route-data/route-data.js b/public/canjs/routing/route-data/route-data.js index d422647c..eb158e59 100644 --- a/public/canjs/routing/route-data/route-data.js +++ b/public/canjs/routing/route-data/route-data.js @@ -35,6 +35,10 @@ import { makeAsyncFromObservableButStillSettableProperty, toSelectedParts, } from "../data-utils.js"; +import { + calculationsForLevel, + createBaseLevels, +} from "../../../react/SettingsSidebar/components/TimingCalculation/hooks/useTimingCalculations/helpers.js"; export function getTimingLevels(issueHierarchy, timingCalculations) { const baseLevels = createBaseLevels(issueHierarchy); diff --git a/public/react/SettingsSidebar/components/IssueSource/IssueSource.tsx b/public/react/SettingsSidebar/components/IssueSource/IssueSource.tsx index 3387797c..92e4e8d8 100644 --- a/public/react/SettingsSidebar/components/IssueSource/IssueSource.tsx +++ b/public/react/SettingsSidebar/components/IssueSource/IssueSource.tsx @@ -1,111 +1,39 @@ -import React, { useCallback, useMemo, useState, FC } from "react"; -import Button from "@atlaskit/button/new"; -import { useCanObservable, CanPromise } from "../../../hooks/useCanObservable/useCanObservable"; -import type { JiraIssue } from "../../../../jira/shared/types"; -import type { OidcJiraIssue } from "../../../../jira-oidc-helpers/types"; -import { value } from "../../../../can"; -import routeData from "../../../../canjs/routing/route-data"; -import type { MultiValue } from "react-select"; -import ExcludedStatusSelect, { - ExcludedStatusSelectOption, -} from "./components/StatusSelect/ExcludedStatusSelect"; -interface IssueSourceProps {} +import type { ExcludedStatusSelectOption } from "./components/StatusSelect"; -const useRawIssuesRequestData = () => { - const issuesPromise = useCanObservable>( - value.from(routeData, "rawIssuesRequestData.issuesPromise") - ); +import React, { useMemo, FC } from "react"; +import Button from "@atlaskit/button/new"; - const issuesPromisePending = useCanObservable( - value.from(routeData, "rawIssuesRequestData.issuesPromise.isPending") - ); - const issuesPromiseResolved = useCanObservable( - value.from(routeData, "rawIssuesRequestData.issuesPromise.isResolved") - ); - const issuesPromiseValueLength = useCanObservable( - value.from(routeData, "rawIssuesRequestData.issuesPromise.value.length") - ); - const issuesReceived = useCanObservable( - value.from(routeData, "rawIssuesRequestData.progressData.issuesReceived") - ); - const issuesRequested = useCanObservable( - value.from(routeData, "rawIssuesRequestData.progressData.issuesRequested") - ); +import routeData from "../../../../canjs/routing/route-data"; +import ExcludedStatusSelect from "./components/StatusSelect"; +import { useRawIssuesRequestData } from "./hooks/useRawIssueRequestData"; +import { useJQL } from "./hooks/useJQL"; - return { - issuesPromise, - issuesPromisePending, - issuesPromiseResolved, - issuesPromiseValueLength, - issuesReceived, - issuesRequested, - }; -}; +interface IssueSourceProps {} const IssueSource: FC = () => { - const { - issuesPromise, - issuesPromisePending, - issuesPromiseResolved, - issuesPromiseValueLength, - issuesReceived, - issuesRequested, - } = useRawIssuesRequestData(); + const { issuesPromise, isLoading, isSuccess, numberOfIssues, receivedChunks, totalChunks } = + useRawIssuesRequestData(); - const loadChildren = useCanObservable(value.from(routeData, "loadChildren")); - const jqlFromRouteData = useCanObservable(value.from(routeData, "jql")); - const childJQLFromRouteData = useCanObservable(value.from(routeData, "childJQL")); - const statusesToExcludeFromRouteData = useCanObservable( - value.from(routeData, "statusesToExclude") - ); - - const [jql, setJql] = useState(jqlFromRouteData); - const [childJQL, setChildJQL] = useState(childJQLFromRouteData); - const [statusesToExclude, setStatusesToExclude] = useState( - statusesToExcludeFromRouteData - ); - - const statusesToExcludeOptions = useMemo( - () => - statusesToExclude.map((status) => ({ - label: status, - value: status, - })), - [statusesToExclude] - ); - const applyJql = useCallback(() => { - routeData.assign({ - jql, - childJQL, - statusesToExclude, - }); - }, [jql, childJQL, statusesToExclude]); - - const enableApply = useMemo(() => { - return ( - !loadChildren && - (jql !== jqlFromRouteData || - childJQL !== childJQLFromRouteData || - statusesToExclude.some((filter) => !statusesToExcludeFromRouteData.includes(filter)) || - statusesToExcludeFromRouteData.some((filter) => !statusesToExclude.includes(filter))) - ); - }, [ - loadChildren, + const { jql, - jqlFromRouteData, + setJql, childJQL, - childJQLFromRouteData, + setChildJQL, + applyJql, statusesToExclude, - statusesToExcludeFromRouteData, - ]); + loadChildren, + setStatusesToExclude, + applyButtonEnabled, + } = useJQL(); - const handleExcludedStatusChange = useCallback( - (statusesToExcludeOptions: MultiValue) => { - const statusesToExclude = statusesToExcludeOptions.map((option) => option.value); - setStatusesToExclude(statusesToExclude); - }, - [] - ); + const statusesToExcludeOptions = useMemo(() => toOptions(statusesToExclude), []); + + const handleExcludedStatusChange = ( + statusesToExcludeOptions: Readonly + ) => { + const statusesToExclude = statusesToExcludeOptions.map((option) => option.value); + setStatusesToExclude(statusesToExclude); + }; return ( <> @@ -161,15 +89,15 @@ const IssueSource: FC = () => {

- {issuesPromisePending && - (issuesRequested ? ( + {isLoading && + (totalChunks ? ( <> - Loaded {issuesReceived} of {issuesRequested} issues + Loaded {receivedChunks} of {totalChunks} issues ) : ( <>Loading issues ... ))} - {issuesPromiseResolved && <>Loaded {issuesPromiseValueLength} issues} + {isSuccess && <>Loaded {numberOfIssues} issues}

= () => { />
-
@@ -189,3 +117,10 @@ const IssueSource: FC = () => { }; export default IssueSource; + +const toOptions = (statuses: string[]) => { + return statuses.map((status) => ({ + label: status, + value: status, + })); +}; diff --git a/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/ExcludedStatusSelect.tsx b/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/ExcludedStatusSelect.tsx index cbfc91e3..a1109a4d 100644 --- a/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/ExcludedStatusSelect.tsx +++ b/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/ExcludedStatusSelect.tsx @@ -2,7 +2,6 @@ import React, { FC, useId } from "react"; import Select from "@atlaskit/select"; import { Label } from "@atlaskit/form"; import useExcludedStatusSelect from "./hooks/useExcludedStatusSelect"; -import type { MultiValue } from "react-select"; export interface ExcludedStatusSelectOption { label: string; @@ -12,8 +11,8 @@ export interface ExcludedStatusSelectOption { export interface ExcludedStatusSelectProps { label: string; placeholder: string; - value: MultiValue; - onChange?: (value: MultiValue) => void; + value: ExcludedStatusSelectOption[]; + onChange?: (value: Readonly) => void; } const ExcludedStatusSelect: FC = ({ label, @@ -28,6 +27,7 @@ const ExcludedStatusSelect: FC = ({ if (allStatusesOptions.length === 0) { return null; } + return ( <> diff --git a/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/index.ts b/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/index.ts index 44462169..9434bd21 100644 --- a/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/index.ts +++ b/public/react/SettingsSidebar/components/IssueSource/components/StatusSelect/index.ts @@ -1 +1,2 @@ export { default } from "./ExcludedStatusSelect"; +export * from "./ExcludedStatusSelect"; diff --git a/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/index.ts b/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/index.ts new file mode 100644 index 00000000..33001cad --- /dev/null +++ b/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/index.ts @@ -0,0 +1 @@ +export * from "./useJQL"; diff --git a/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/useJQL.ts b/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/useJQL.ts new file mode 100644 index 00000000..f4536836 --- /dev/null +++ b/public/react/SettingsSidebar/components/IssueSource/hooks/useJQL/useJQL.ts @@ -0,0 +1,47 @@ +import { useState } from "react"; +import { value } from "../../../../../../can"; +import routeData from "../../../../../../canjs/routing/route-data"; +import { useCanObservable } from "../../../../../hooks/useCanObservable"; + +export const useJQL = () => { + const jqlFromRouteData = useCanObservable(value.from(routeData, "jql")); + const childJQLFromRouteData = useCanObservable(value.from(routeData, "childJQL")); + const loadChildren = useCanObservable(value.from(routeData, "loadChildren")); + const statusesToExcludeFromRouteData = useCanObservable( + value.from(routeData, "statusesToExclude") + ); + + const [statusesToExclude, setStatusesToExclude] = useState( + statusesToExcludeFromRouteData + ); + const [jql, setJql] = useState(jqlFromRouteData); + const [childJQL, setChildJQL] = useState(childJQLFromRouteData); + + console.log({ jql, loadChildren, childJQL, statusesToExclude }); + + const applyJql = () => { + routeData.assign({ + jql, + childJQL, + statusesToExclude, + }); + }; + + const statusesDiffer = + statusesToExclude.some((filter) => !statusesToExcludeFromRouteData.includes(filter)) || + statusesToExcludeFromRouteData.some((filter) => !statusesToExclude.includes(filter)); + + return { + loadChildren, + jql, + setJql, + childJQL, + setChildJQL, + applyJql, + statusesToExclude, + setStatusesToExclude, + applyButtonEnabled: + !loadChildren && + (jql !== jqlFromRouteData || childJQL !== childJQLFromRouteData || statusesDiffer), + }; +}; diff --git a/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/index.ts b/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/index.ts new file mode 100644 index 00000000..19b099e8 --- /dev/null +++ b/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/index.ts @@ -0,0 +1 @@ +export * from "./useRawIssueRequestData"; diff --git a/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/useRawIssueRequestData.ts b/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/useRawIssueRequestData.ts new file mode 100644 index 00000000..75533f4a --- /dev/null +++ b/public/react/SettingsSidebar/components/IssueSource/hooks/useRawIssueRequestData/useRawIssueRequestData.ts @@ -0,0 +1,38 @@ +import type { CanPromise } from "../../../../../hooks/useCanObservable"; +import type { JiraIssue } from "../../../../../../jira/shared/types"; +import type { OidcJiraIssue } from "../../../../../../jira-oidc-helpers/types"; + +import { value } from "../../../../../../can"; +import routeData from "../../../../../../canjs/routing/route-data"; +import { useCanObservable } from "../../../../../hooks/useCanObservable"; + +export const useRawIssuesRequestData = () => { + const issuesPromise = useCanObservable>( + value.from(routeData, "rawIssuesRequestData.issuesPromise") + ); + + const issuesPromisePending = useCanObservable( + value.from(routeData, "rawIssuesRequestData.issuesPromise.isPending") + ); + const issuesPromiseResolved = useCanObservable( + value.from(routeData, "rawIssuesRequestData.issuesPromise.isResolved") + ); + const issuesPromiseValueLength = useCanObservable( + value.from(routeData, "rawIssuesRequestData.issuesPromise.value.length") + ); + const issuesReceived = useCanObservable( + value.from(routeData, "rawIssuesRequestData.progressData.issuesReceived") + ); + const issuesRequested = useCanObservable( + value.from(routeData, "rawIssuesRequestData.progressData.issuesRequested") + ); + + return { + issuesPromise, + isLoading: issuesPromisePending, + isSuccess: issuesPromiseResolved, + numberOfIssues: issuesPromiseValueLength, + receivedChunks: issuesReceived, + totalChunks: issuesRequested, + }; +}; diff --git a/public/timeline-report.js b/public/timeline-report.js index 8b32ea53..ca7d447b 100644 --- a/public/timeline-report.js +++ b/public/timeline-report.js @@ -26,7 +26,7 @@ import SavedReports from "./react/SaveReports"; import SettingsSidebar from "./react/SettingsSidebar"; import SampleDataNotice from "./react/SampleDataNotice"; -import { getTheme } from "./jira/theme"; +import { getTheme, applyThemeToCssVars } from "./jira/theme"; export class TimelineReport extends StacheElement { static view = `