From 154d5be3281c79a6b48527be03e84d31f5cde738 Mon Sep 17 00:00:00 2001 From: Michael Taylor Date: Tue, 2 Apr 2024 14:23:01 -0400 Subject: [PATCH] feat: add per organization sync status --- .../v1/organizations/organizations.api.ts | 10 ++++- .../blocks/widgets/OrganizationSelector.tsx | 22 +++++----- .../blocks/widgets/SyncIndicator.tsx | 44 +++++++++++++++++++ .../components/blocks/widgets/index.ts | 3 +- .../pages/ProjectsList/ProjectsList.tsx | 2 + src/renderer/pages/UnitsList/UnitsList.tsx | 2 + 6 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 src/renderer/components/blocks/widgets/SyncIndicator.tsx diff --git a/src/renderer/api/cadt/v1/organizations/organizations.api.ts b/src/renderer/api/cadt/v1/organizations/organizations.api.ts index 1109ded0..81a546c2 100644 --- a/src/renderer/api/cadt/v1/organizations/organizations.api.ts +++ b/src/renderer/api/cadt/v1/organizations/organizations.api.ts @@ -15,11 +15,19 @@ const organizationsApi = cadtApi.injectEndpoints({ transformResponse(baseQueryReturnValue: BaseQueryResult): any[]{ return Object.values(baseQueryReturnValue); } + }), + getOrganizationsMap: builder.query({ + query: () => ({ + url: `${host}/v1/organizations`, + method: 'GET', + }), + providesTags: [organizationsTag], }) }) }); export const { - useGetOrganizationsListQuery + useGetOrganizationsListQuery, + useGetOrganizationsMapQuery } = organizationsApi; \ No newline at end of file diff --git a/src/renderer/components/blocks/widgets/OrganizationSelector.tsx b/src/renderer/components/blocks/widgets/OrganizationSelector.tsx index 56c94f01..d79e45ce 100644 --- a/src/renderer/components/blocks/widgets/OrganizationSelector.tsx +++ b/src/renderer/components/blocks/widgets/OrganizationSelector.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { useGetOrganizationsListQuery } from "@/api/cadt/v1/organizations"; -import { Dropdown } from '@/components'; +import { useGetOrganizationsListQuery } from '@/api/cadt/v1/organizations'; +import { Dropdown, SyncIndicator } from '@/components'; interface OrganizationSelectorProps { onSelect: (organization: any | undefined) => void; @@ -13,9 +13,9 @@ const OrganizationSelector: React.FC = ({ onSelect, d useEffect(() => { if (defaultOrgUid === null) { - setSelectedOrganization({ name: "All Organizations" }); + setSelectedOrganization({ name: 'All Organizations' }); } else if (defaultOrgUid !== undefined && organizations) { - const defaultOrganization = organizations.find(org => org.orgUid === defaultOrgUid); + const defaultOrganization = organizations.find((org) => org.orgUid === defaultOrgUid); if (defaultOrganization) { setSelectedOrganization(defaultOrganization); } @@ -31,27 +31,25 @@ const OrganizationSelector: React.FC = ({ onSelect, d } const handleSelect = (organization: any | undefined) => { - if (!organization) { - setSelectedOrganization({ name: "All Organizations" }); - } else { - setSelectedOrganization(organization); - } + setSelectedOrganization(organization || { name: 'All Organizations' }); onSelect(organization); }; return ( - + handleSelect(undefined)}>All Organizations {organizations.map((organization) => ( handleSelect(organization)} + className="flex justify-between items-center" > - {organization.name} + {organization.name} + ))} ); -} +}; export { OrganizationSelector }; diff --git a/src/renderer/components/blocks/widgets/SyncIndicator.tsx b/src/renderer/components/blocks/widgets/SyncIndicator.tsx new file mode 100644 index 00000000..12fb4239 --- /dev/null +++ b/src/renderer/components/blocks/widgets/SyncIndicator.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { useGetOrganizationsMapQuery, useGetOrganizationsListQuery } from '@/api/cadt/v1/organizations'; + +interface SyncIndicatorProps { + detailed: boolean; + orgUid?: string; +} + +/** + * Component to display sync status indicator. + * It shows a green circle if the organization is synced, and a pulsing yellow circle if syncing. + * When 'detailed' is true, it also shows a label next to the indicator. + * The sync status is determined based on 'orgUid' from the organizations data. + */ +const SyncIndicator: React.FC = ({ detailed, orgUid }) => { + const { data: organizationsMap } = useGetOrganizationsMapQuery(null, { + skip: !orgUid, + }); + + const { data: organizationList } = useGetOrganizationsListQuery(null, { + skip: Boolean(orgUid), + }); + + let isSynced = false; + + if (!orgUid) { + isSynced = !organizationList?.some((org) => !org.synced); + } else { + isSynced = organizationsMap?.[orgUid]?.synced; + } + + return ( +
+
+ {detailed && ( + + {isSynced ? 'Synced' : 'Syncing...'} + + )} +
+ ); +}; + +export { SyncIndicator }; diff --git a/src/renderer/components/blocks/widgets/index.ts b/src/renderer/components/blocks/widgets/index.ts index 48f9c735..0ac7de71 100644 --- a/src/renderer/components/blocks/widgets/index.ts +++ b/src/renderer/components/blocks/widgets/index.ts @@ -1,2 +1,3 @@ export * from './SearchBox'; -export * from './OrganizationSelector'; \ No newline at end of file +export * from './OrganizationSelector'; +export * from './SyncIndicator'; \ No newline at end of file diff --git a/src/renderer/pages/ProjectsList/ProjectsList.tsx b/src/renderer/pages/ProjectsList/ProjectsList.tsx index 21e2bcf1..a9584f35 100644 --- a/src/renderer/pages/ProjectsList/ProjectsList.tsx +++ b/src/renderer/pages/ProjectsList/ProjectsList.tsx @@ -8,6 +8,7 @@ import { SkeletonTable, ProjectsListTable, SearchBox, + SyncIndicator } from '@/components'; import {FormattedMessage} from "react-intl"; @@ -62,6 +63,7 @@ const ProjectsList: React.FC = () => {
+
{projectsLoading ? ( diff --git a/src/renderer/pages/UnitsList/UnitsList.tsx b/src/renderer/pages/UnitsList/UnitsList.tsx index e47cfb18..5ff36686 100644 --- a/src/renderer/pages/UnitsList/UnitsList.tsx +++ b/src/renderer/pages/UnitsList/UnitsList.tsx @@ -8,6 +8,7 @@ import { SkeletonTable, SearchBox, UnitsListTable, + SyncIndicator } from '@/components'; import {FormattedMessage} from "react-intl"; @@ -62,6 +63,7 @@ const UnitsList: React.FC = () => {
+
{unitsLoading ? (