diff --git a/package-lock.json b/package-lock.json
index d4e757c0..a9c51776 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,6 +21,7 @@
"chia-web2-gateway": "^1.0.10",
"components": "^0.1.0",
"dayjs": "^1.11.10",
+ "diff": "^5.2.0",
"express": "^4.19.2",
"flowbite": "^2.3.0",
"flowbite-react": "^0.7.8",
@@ -30,8 +31,10 @@
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-content-loader": "^7.0.0",
+ "react-diff-view": "^3.2.1",
"react-dom": "^18.2.0",
"react-intl": "^6.6.5",
+ "react-json-view-compare": "^2.0.2",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",
"react-webview": "^0.1.0",
@@ -4448,6 +4451,19 @@
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
+ "node_modules/diff": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/diff-match-patch": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+ "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
+ },
"node_modules/dir-compare": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
@@ -6055,6 +6071,11 @@
"ini": "^1.3.2"
}
},
+ "node_modules/gitdiff-parser": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz",
+ "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ=="
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -8628,6 +8649,22 @@
"react": ">=16.0.0"
}
},
+ "node_modules/react-diff-view": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/react-diff-view/-/react-diff-view-3.2.1.tgz",
+ "integrity": "sha512-JoDahgiyeReeH9W9lrI3Z4c4esbd/HNAOdThj6Pce/ZAukFBmXSbZ4Qv8ayo7yow+fTpRNfqtQ9gX5nArEi08w==",
+ "dependencies": {
+ "classnames": "^2.3.2",
+ "diff-match-patch": "^1.0.5",
+ "gitdiff-parser": "^0.3.1",
+ "lodash": "^4.17.21",
+ "shallow-equal": "^3.1.0",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.14.0"
+ }
+ },
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -8684,6 +8721,27 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/react-json-view-compare": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/react-json-view-compare/-/react-json-view-compare-2.0.2.tgz",
+ "integrity": "sha512-we+OMLFR2FGqHeoqfubQnMhY5V4jLK15/cOnh7cj+v6v7XpizdJu4oSfbEwRWxvTzywSTqpBw/xL0kPZ2YkLIg==",
+ "dependencies": {
+ "react-json-viewer-cool": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-json-view-compare/node_modules/react-json-viewer-cool": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/react-json-viewer-cool/-/react-json-viewer-cool-2.0.0.tgz",
+ "integrity": "sha512-2bCC9szMbh9PEK5WmAv2253MwjWdK/58RCleqIqvtVjoFFCtWRPXKbe+uK6cxxGRrtCPFLJE+4H1+T+pUQltLQ==",
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.0",
+ "react-dom": "^16.14.0 || ^17.0.0"
+ }
+ },
"node_modules/react-redux": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.1.tgz",
@@ -9415,6 +9473,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shallow-equal": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz",
+ "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg=="
+ },
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
@@ -10749,6 +10812,14 @@
"node": ">=12.0.0"
}
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 8c493cc3..dbf43b8a 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"chia-web2-gateway": "^1.0.10",
"components": "^0.1.0",
"dayjs": "^1.11.10",
+ "diff": "^5.2.0",
"express": "^4.19.2",
"flowbite": "^2.3.0",
"flowbite-react": "^0.7.8",
@@ -45,8 +46,10 @@
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
"react-content-loader": "^7.0.0",
+ "react-diff-view": "^3.2.1",
"react-dom": "^18.2.0",
"react-intl": "^6.6.5",
+ "react-json-view-compare": "^2.0.2",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",
"react-webview": "^0.1.0",
diff --git a/src/renderer/api/cadt/v1/index.ts b/src/renderer/api/cadt/v1/index.ts
index b3e4c993..09418620 100644
--- a/src/renderer/api/cadt/v1/index.ts
+++ b/src/renderer/api/cadt/v1/index.ts
@@ -1,46 +1,49 @@
-import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
-import initialState from '@/store/slices/app/app.initialstate'; // Ensure this path is correct
-
-const projectsTag = 'projects';
-const organizationsTag = 'organizations';
-const unitsTag = 'projects';
-const auditTag = 'audit';
-const issuancesTag = 'issuances';
-
-const baseQuery = fetchBaseQuery({
- baseUrl: '/',
-});
-
-const baseQueryWithDynamicHost = async (args, api, extraOptions) => {
- let modifiedArgs = args;
- const state = api.getState();
- const currentHost = state.app.apiHost;
-
- // Check if currentHost is equal to the initialState's apiHost
- const effectiveHost = (currentHost === initialState.apiHost && import.meta.env.VITE_API_HOST) ? import.meta.env.VITE_API_HOST : currentHost;
-
- if (!args.url.startsWith('/')) {
- return await baseQuery(args, api, extraOptions);
- }
-
- // Modify the URL based on the effectiveHost
- if (typeof args === 'string') {
- modifiedArgs = `${effectiveHost}${args}`;
- } else if (args && typeof args === 'object') {
- modifiedArgs = {
- ...args,
- url: `${effectiveHost}${args.url}`,
- };
- }
-
- return await baseQuery(modifiedArgs, api, extraOptions);
-};
-
-export const cadtApi = createApi({
- baseQuery: baseQueryWithDynamicHost,
- reducerPath: 'cadtApi',
- tagTypes: [projectsTag, organizationsTag, unitsTag, auditTag, issuancesTag],
- endpoints: () => ({}),
-});
-
-export { projectsTag, organizationsTag, unitsTag, auditTag, issuancesTag };
+import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
+import initialState from '@/store/slices/app/app.initialstate';
+
+const projectsTag = 'projects';
+const organizationsTag = 'organizations';
+const unitsTag = 'projects';
+const auditTag = 'audit';
+const issuancesTag = 'issuances';
+const stagedProjectsTag = 'stagedProjects';
+const stagedUnitsTag = 'stagedUnits';
+
+const baseQuery = fetchBaseQuery({
+ baseUrl: '/',
+});
+
+const baseQueryWithDynamicHost = async (args, api, extraOptions) => {
+ let modifiedArgs = args;
+ const state = api.getState();
+ const currentHost = state.app.apiHost;
+
+ // Check if currentHost is equal to the initialState's apiHost
+ const effectiveHost =
+ currentHost === initialState.apiHost && import.meta.env.VITE_API_HOST ? import.meta.env.VITE_API_HOST : currentHost;
+
+ if (!args.url.startsWith('/')) {
+ return await baseQuery(args, api, extraOptions);
+ }
+
+ // Modify the URL based on the effectiveHost
+ if (typeof args === 'string') {
+ modifiedArgs = `${effectiveHost}${args}`;
+ } else if (args && typeof args === 'object') {
+ modifiedArgs = {
+ ...args,
+ url: `${effectiveHost}${args.url}`,
+ };
+ }
+
+ return await baseQuery(modifiedArgs, api, extraOptions);
+};
+
+export const cadtApi = createApi({
+ baseQuery: baseQueryWithDynamicHost,
+ reducerPath: 'cadtApi',
+ tagTypes: [projectsTag, organizationsTag, unitsTag, auditTag, stagedProjectsTag, stagedUnitsTag, issuancesTag],
+ endpoints: () => ({}),
+});
+
+export { projectsTag, organizationsTag, unitsTag, auditTag, stagedProjectsTag, stagedUnitsTag, issuancesTag };
diff --git a/src/renderer/api/cadt/v1/staging/index.ts b/src/renderer/api/cadt/v1/staging/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/src/renderer/api/cadt/v1/staging/staging.api.ts b/src/renderer/api/cadt/v1/staging/staging.api.ts
new file mode 100644
index 00000000..73dae8dc
--- /dev/null
+++ b/src/renderer/api/cadt/v1/staging/staging.api.ts
@@ -0,0 +1,33 @@
+import { cadtApi, stagedProjectsTag, stagedUnitsTag } from '../';
+
+const stagingApi = cadtApi.injectEndpoints({
+ endpoints: (builder) => ({
+ getStagedProjects: builder.query({
+ query: () => {
+ const params: { type: string } = { type: 'projects' };
+
+ return {
+ url: `/v1/staging`,
+ params,
+ method: 'GET',
+ };
+ },
+ providesTags: [stagedProjectsTag],
+ }),
+
+ getStagedUnits: builder.query({
+ query: () => {
+ const params: { type: string } = { type: 'units' };
+
+ return {
+ url: `/v1/staging`,
+ params,
+ method: 'GET',
+ };
+ },
+ providesTags: [stagedUnitsTag],
+ }),
+ }),
+});
+
+export const { useGetStagedProjectsQuery, useGetStagedUnitsQuery } = stagingApi;
diff --git a/src/renderer/components/blocks/index.ts b/src/renderer/components/blocks/index.ts
index 6395aebc..1bdb4296 100644
--- a/src/renderer/components/blocks/index.ts
+++ b/src/renderer/components/blocks/index.ts
@@ -4,3 +4,4 @@ export * from './modals';
export * from './tables';
export * from './widgets';
export * from './forms';
+export * from './tabs';
diff --git a/src/renderer/components/blocks/modals/StagingDiffModal.tsx b/src/renderer/components/blocks/modals/StagingDiffModal.tsx
new file mode 100644
index 00000000..5adf29ad
--- /dev/null
+++ b/src/renderer/components/blocks/modals/StagingDiffModal.tsx
@@ -0,0 +1,35 @@
+import React, { useMemo } from 'react';
+import { DiffViewer, Modal } from '@/components';
+import { FormattedMessage } from 'react-intl';
+import { useGetStagedProjectsQuery } from '@/api/cadt/v1/staging/staging.api';
+
+interface ProjectModalProps {
+ stagingUuid: string;
+ onClose: () => void;
+}
+
+const StagingDiffModal: React.FC = ({ stagingUuid, onClose }: ProjectModalProps) => {
+ const { data: stagingData, isLoading } = useGetStagedProjectsQuery();
+
+ const changeRecord: any = useMemo(() => {
+ if (isLoading || !stagingData) {
+ return undefined;
+ }
+ return stagingData.find((record) => record.uuid === stagingUuid);
+ }, [stagingData, isLoading, stagingUuid]);
+
+ if (isLoading || !changeRecord) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {isLoading ? loading...
: }
+
+ );
+};
+
+export { StagingDiffModal };
diff --git a/src/renderer/components/blocks/modals/index.ts b/src/renderer/components/blocks/modals/index.ts
index b3c6f392..6f979de3 100644
--- a/src/renderer/components/blocks/modals/index.ts
+++ b/src/renderer/components/blocks/modals/index.ts
@@ -1,4 +1,5 @@
-export * from './ProjectModal';
-export * from './CreateOrganizationModal';
-export * from './ConnectModal';
-export * from './UnitModal';
\ No newline at end of file
+export * from './ProjectModal';
+export * from './CreateOrganizationModal';
+export * from './ConnectModal';
+export * from './UnitModal';
+export * from './StagingDiffModal';
diff --git a/src/renderer/components/blocks/tables/StagingTable.tsx b/src/renderer/components/blocks/tables/StagingTable.tsx
new file mode 100644
index 00000000..a8374900
--- /dev/null
+++ b/src/renderer/components/blocks/tables/StagingTable.tsx
@@ -0,0 +1,78 @@
+import React, { useMemo } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Badge, DataTable } from '@/components';
+import dayjs from 'dayjs';
+
+interface TableProps {
+ data: any[];
+ isLoading: boolean;
+ setOrder: (sort: string) => void;
+ onRowClick: (row: any) => void;
+ order: string;
+}
+
+const StagingTable: React.FC = ({ data, isLoading, onRowClick, setOrder, order }) => {
+ const columns = useMemo(
+ () => [
+ {
+ title: ,
+ key: 'table',
+ render: (row: any) => {row?.table || '-'},
+ },
+ {
+ title: ,
+ key: 'action',
+ render: (row: any) => {
+ if (row.action === 'INSERT') {
+ return {row.action};
+ }
+ if (row.action === 'UPDATE') {
+ return {row.action};
+ }
+ if (row.action === 'DELETE') {
+ return {row.action};
+ }
+ return {'--'};
+ },
+ },
+ {
+ title: ,
+ key: 'uuid',
+ render: (row: any) => {row.uuid}
,
+ },
+ {
+ title: ,
+ key: 'createdAt',
+ render: (row: any) => {
+ return <>{dayjs(new Date(row.createdAt)).format('MMMM D, YYYY h:mm:ss A')}>;
+ },
+ },
+ {
+ title: ,
+ key: 'updatedAt',
+ render: (row: any) => {
+ return <>{dayjs(new Date(row.updatedAt)).format('MMMM D, YYYY h:mm:ss A')}>;
+ },
+ },
+ ],
+ [],
+ );
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export { StagingTable };
diff --git a/src/renderer/components/blocks/tables/index.ts b/src/renderer/components/blocks/tables/index.ts
index 16a7fe5f..d0d5504b 100644
--- a/src/renderer/components/blocks/tables/index.ts
+++ b/src/renderer/components/blocks/tables/index.ts
@@ -1,6 +1,8 @@
-export * from './ProjectsListTable';
-export * from './SkeletonTable';
-export * from './UnitsListTable';
-export * from './AuditsTable';
-export * from './GlossaryTable';
-export * from './CompactUnitsTable';
\ No newline at end of file
+export * from './ProjectsListTable';
+export * from './SkeletonTable';
+export * from './UnitsListTable';
+export * from './AuditsTable';
+export * from './GlossaryTable';
+export * from './StagingTable';
+export * from './OrganizationSubscriptionsTable';
+export * from './CompactUnitsTable';
diff --git a/src/renderer/components/blocks/tabs/MyCommittedProjectsTab.tsx b/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx
similarity index 93%
rename from src/renderer/components/blocks/tabs/MyCommittedProjectsTab.tsx
rename to src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx
index 84da9716..5149ccb1 100644
--- a/src/renderer/components/blocks/tabs/MyCommittedProjectsTab.tsx
+++ b/src/renderer/components/blocks/tabs/CommittedProjectsTab.tsx
@@ -11,7 +11,7 @@ interface PageTabProps {
setIsLoading: (isLoading: boolean) => void;
}
-const MyCommittedProjectsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => {
+const CommittedProjectsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => {
const [currentPage, setCurrentPage] = useQueryParamState('page', '1');
const [order, setOrder] = useQueryParamState('order', undefined);
const handleSetOrder = useColumnOrderHandler(order, setOrder);
@@ -69,4 +69,4 @@ const MyCommittedProjectsTab: React.FC = ({ orgUid, search, setIsL
);
};
-export { MyCommittedProjectsTab };
+export { CommittedProjectsTab };
diff --git a/src/renderer/components/blocks/tabs/MyCommittedUnitsTab.tsx b/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx
similarity index 93%
rename from src/renderer/components/blocks/tabs/MyCommittedUnitsTab.tsx
rename to src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx
index 3bc91163..e48b8102 100644
--- a/src/renderer/components/blocks/tabs/MyCommittedUnitsTab.tsx
+++ b/src/renderer/components/blocks/tabs/CommittedUnitsTab.tsx
@@ -11,7 +11,7 @@ interface PageTabProps {
setIsLoading: (isLoading: boolean) => void;
}
-const MyCommittedUnitsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => {
+const CommittedUnitsTab: React.FC = ({ orgUid, search, setIsLoading }: PageTabProps) => {
const [currentPage, setCurrentPage] = useQueryParamState('page', '1');
const [order, setOrder] = useQueryParamState('order', undefined);
const handleSetOrder = useColumnOrderHandler(order, setOrder);
@@ -65,4 +65,4 @@ const MyCommittedUnitsTab: React.FC = ({ orgUid, search, setIsLoad
);
};
-export { MyCommittedUnitsTab };
+export { CommittedUnitsTab };
diff --git a/src/renderer/components/blocks/tabs/StagingTableTab.tsx b/src/renderer/components/blocks/tabs/StagingTableTab.tsx
new file mode 100644
index 00000000..1c803eb4
--- /dev/null
+++ b/src/renderer/components/blocks/tabs/StagingTableTab.tsx
@@ -0,0 +1,47 @@
+import { FormattedMessage } from 'react-intl';
+import { SkeletonTable, StagingDiffModal, StagingTable } from '@/components';
+import React from 'react';
+import { useColumnOrderHandler, useQueryParamState, useWildCardUrlHash } from '@/hooks';
+
+interface PageTabProps {
+ stagingData: any[];
+ showLoading: boolean;
+}
+
+const StagingTableTab: React.FC = ({ stagingData, showLoading }: PageTabProps) => {
+ const [order, setOrder] = useQueryParamState('order', undefined);
+ const handleSetOrder = useColumnOrderHandler(order, setOrder);
+ const [stagingDiffFragment, stagingDiffModalActive, setStagingDiffModalActive] = useWildCardUrlHash('staging');
+
+ if (showLoading) {
+ return ;
+ }
+
+ if (!stagingData) {
+ return ;
+ }
+
+ return (
+ <>
+ {showLoading ? (
+
+ ) : (
+ setStagingDiffModalActive(true, row.uuid)}
+ />
+ )}
+ {stagingDiffModalActive && (
+ setStagingDiffModalActive(false)}
+ stagingUuid={stagingDiffFragment.replace('staging-', '')}
+ />
+ )}
+ >
+ );
+};
+
+export { StagingTableTab };
diff --git a/src/renderer/components/blocks/tabs/index.ts b/src/renderer/components/blocks/tabs/index.ts
index 61983471..5c0d6779 100644
--- a/src/renderer/components/blocks/tabs/index.ts
+++ b/src/renderer/components/blocks/tabs/index.ts
@@ -1 +1,3 @@
-export * from './MyCommittedProjectsTab';
+export * from './CommittedProjectsTab';
+export * from './CommittedUnitsTab';
+export * from './StagingTableTab';
diff --git a/src/renderer/components/blocks/widgets/DiffViewer.tsx b/src/renderer/components/blocks/widgets/DiffViewer.tsx
new file mode 100644
index 00000000..f3eedb8d
--- /dev/null
+++ b/src/renderer/components/blocks/widgets/DiffViewer.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import JSONDiffReact from 'react-json-view-compare';
+
+interface ChangeRecord {
+ id: number;
+ uuid: string;
+ table: string;
+ action: string;
+ commited: boolean;
+ failedCommit: boolean;
+ isTransfer: boolean;
+ createdAt: string;
+ updatedAt: string;
+ diff: {
+ original: any;
+ change: any[];
+ };
+}
+
+interface DiffViewerProps {
+ data: ChangeRecord;
+}
+
+const DiffViewer: React.FC = ({ data }) => {
+ return (
+
+
+ Change for {data.table} - Action: {data.action}
+
+ 1 ? data.diff.change : data.diff.change[0]}
+ collapsed={false}
+ compareArraysInOrder={true}
+ leftTitle="Original"
+ rightTitle="Modified"
+ />
+
+ );
+};
+
+export { DiffViewer };
diff --git a/src/renderer/components/blocks/widgets/OrganizationSubscriptionsManager.tsx b/src/renderer/components/blocks/widgets/OrganizationSubscriptionsManager.tsx
index c9d683ed..b26afafc 100644
--- a/src/renderer/components/blocks/widgets/OrganizationSubscriptionsManager.tsx
+++ b/src/renderer/components/blocks/widgets/OrganizationSubscriptionsManager.tsx
@@ -1,8 +1,12 @@
import React from 'react';
-import { ComponentCenteredSpinner, IndeterminateProgressOverlay, SkeletonTable } from '@/components';
+import {
+ ComponentCenteredSpinner,
+ IndeterminateProgressOverlay,
+ OrganizationSubscriptionsTable,
+ SkeletonTable,
+} from '@/components';
import { FormattedMessage } from 'react-intl';
import { useGetDefaultOrgListQuery } from '@/api/cadt/v1/governance';
-import { OrganizationSubscriptionsTable } from '@/components/blocks/tables/OrganizationSubscriptionsTable';
const OrganizationSubscriptionsManager: React.FC = () => {
const {
diff --git a/src/renderer/components/blocks/widgets/index.ts b/src/renderer/components/blocks/widgets/index.ts
index 39745394..e73dac95 100644
--- a/src/renderer/components/blocks/widgets/index.ts
+++ b/src/renderer/components/blocks/widgets/index.ts
@@ -3,4 +3,5 @@ export * from './OrganizationSelector';
export * from './SyncIndicator';
export * from './OrgUidBadge';
export * from './ThemeSelector';
-export * from './UnitSummary';
\ No newline at end of file
+export * from './UnitSummary';
+export * from './DiffViewer';
diff --git a/src/renderer/components/proxy/Badge.tsx b/src/renderer/components/proxy/Badge.tsx
new file mode 100644
index 00000000..fd9edaeb
--- /dev/null
+++ b/src/renderer/components/proxy/Badge.tsx
@@ -0,0 +1,7 @@
+import { Badge as FlowbiteBadge, BadgeProps } from 'flowbite-react';
+
+function Badge({ children, ...props }: BadgeProps) {
+ return {children};
+}
+
+export { Badge };
diff --git a/src/renderer/components/proxy/index.ts b/src/renderer/components/proxy/index.ts
index bf1b79f7..764d5575 100644
--- a/src/renderer/components/proxy/index.ts
+++ b/src/renderer/components/proxy/index.ts
@@ -1,16 +1,17 @@
-export * from './Button';
-export * from './Pagination';
-export * from './Dropdown';
-export * from './TextInput';
-export * from './Spinner';
-export * from './Sidebar';
-export * from './Tooltip';
-export * from './Modal';
-export * from './Tabs';
-export * from './Card';
-export * from './FloatingLabel';
-export * from './Toast';
-export * from './HelperText';
-export * from './Label';
-export * from './FloatingLabel';
-export * from './Select';
\ No newline at end of file
+export * from './Button';
+export * from './Pagination';
+export * from './Dropdown';
+export * from './TextInput';
+export * from './Spinner';
+export * from './Sidebar';
+export * from './Tooltip';
+export * from './Modal';
+export * from './Tabs';
+export * from './Card';
+export * from './FloatingLabel';
+export * from './Toast';
+export * from './HelperText';
+export * from './Label';
+export * from './FloatingLabel';
+export * from './Select';
+export * from './Badge';
diff --git a/src/renderer/pages/MyProjectsPage.tsx b/src/renderer/pages/MyProjectsPage.tsx
index 3d0c74c0..37b2af3a 100644
--- a/src/renderer/pages/MyProjectsPage.tsx
+++ b/src/renderer/pages/MyProjectsPage.tsx
@@ -4,10 +4,12 @@ import { useQueryParamState } from '@/hooks';
import { debounce } from 'lodash';
import {
Button,
+ CommittedProjectsTab,
ComponentCenteredSpinner,
IndeterminateProgressOverlay,
OrgUidBadge,
SearchBox,
+ StagingTableTab,
SyncIndicator,
Tabs,
} from '@/components';
@@ -15,7 +17,7 @@ import { FormattedMessage } from 'react-intl';
import { useGetOrganizationsMapQuery } from '@/api/cadt/v1/organizations';
import { Organization } from '@/schemas/Organization.schema';
import { useNavigate } from 'react-router-dom';
-import { MyCommittedProjectsTab } from '@/components/blocks/tabs/MyCommittedProjectsTab';
+import { useGetStagedProjectsQuery } from '@/api/cadt/v1/staging/staging.api';
enum TabTypes {
COMMITTED,
@@ -25,6 +27,12 @@ enum TabTypes {
TRANSFERS,
}
+interface ProcessedStagingData {
+ staged: any[];
+ pending: any[];
+ failed: any[];
+}
+
const MyProjectsPage: React.FC = () => {
const navigate = useNavigate();
const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined);
@@ -34,7 +42,7 @@ const MyProjectsPage: React.FC = () => {
const { data: organizationsMap } = useGetOrganizationsMapQuery(null, {
skip: !orgUid,
});
-
+ const { data: unprocessedStagedProjects, isLoading: stagingDataLoading } = useGetStagedProjectsQuery();
const { data: organizationsListData, isLoading: organizationsListLoading } = useGetOrganizationsListQuery();
const myOrganization = useMemo(
@@ -42,9 +50,27 @@ const MyProjectsPage: React.FC = () => {
[organizationsListData],
);
+ const processedStagingData: ProcessedStagingData = useMemo(() => {
+ const data: ProcessedStagingData = { staged: [], pending: [], failed: [] };
+ if (unprocessedStagedProjects?.forEach) {
+ unprocessedStagedProjects.forEach((stagedProject: any) => {
+ if (unprocessedStagedProjects?.forEach) {
+ if (!stagedProject.commited && !stagedProject.failedCommit && !stagedProject.isTransfer) {
+ data.staged.push(stagedProject);
+ } else if (stagedProject.commited && !stagedProject.failedCommit && !stagedProject.isTransfer) {
+ data.pending.push(stagedProject);
+ } else if (!stagedProject.commited && stagedProject.failedCommit && !stagedProject.isTransfer) {
+ data.failed.push(stagedProject);
+ }
+ }
+ });
+ }
+ return data;
+ }, [unprocessedStagedProjects]);
+
const contentsLoading = useMemo(() => {
- return committedDataLoading;
- }, [committedDataLoading]);
+ return committedDataLoading || stagingDataLoading;
+ }, [committedDataLoading, stagingDataLoading]);
useEffect(() => {
if (myOrganization) {
@@ -83,11 +109,38 @@ const MyProjectsPage: React.FC = () => {
setActiveTab(tab)}>
}>
-
+
+
+
+
+ {' (' + String(processedStagingData.staged.length + ') ')}
+
+ }
+ >
+
+
+
+
+ {' (' + String(processedStagingData.pending.length + ') ')}
+
+ }
+ >
+
+
+
+
+ {' (' + String(processedStagingData.failed.length + ') ')}
+
+ }
+ >
+
- }>todo staging
- }>todo pending
- }>todo failed
}>todo transfers
diff --git a/src/renderer/pages/MyUnitsPage.tsx b/src/renderer/pages/MyUnitsPage.tsx
index 78d14518..18687da6 100644
--- a/src/renderer/pages/MyUnitsPage.tsx
+++ b/src/renderer/pages/MyUnitsPage.tsx
@@ -1,20 +1,23 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { useGetOrganizationsListQuery, useGetOrganizationsMapQuery } from '@/api';
+import { useGetOrganizationsListQuery } from '@/api';
import { useQueryParamState } from '@/hooks';
-import { debounce, DebouncedFunc } from 'lodash';
+import { debounce } from 'lodash';
import {
Button,
+ CommittedUnitsTab,
ComponentCenteredSpinner,
IndeterminateProgressOverlay,
OrgUidBadge,
SearchBox,
+ StagingTableTab,
SyncIndicator,
Tabs,
} from '@/components';
import { FormattedMessage } from 'react-intl';
-import { useNavigate } from 'react-router-dom';
+import { useGetOrganizationsMapQuery } from '@/api/cadt/v1/organizations';
import { Organization } from '@/schemas/Organization.schema';
-import { MyCommittedUnitsTab } from '@/components/blocks/tabs/MyCommittedUnitsTab';
+import { useNavigate } from 'react-router-dom';
+import { useGetStagedUnitsQuery } from '@/api/cadt/v1/staging/staging.api';
enum TabTypes {
COMMITTED,
@@ -24,6 +27,12 @@ enum TabTypes {
TRANSFERS,
}
+interface ProcessedStagingData {
+ staged: any[];
+ pending: any[];
+ failed: any[];
+}
+
const MyUnitsPage: React.FC = () => {
const navigate = useNavigate();
const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined);
@@ -33,7 +42,7 @@ const MyUnitsPage: React.FC = () => {
const { data: organizationsMap } = useGetOrganizationsMapQuery(null, {
skip: !orgUid,
});
-
+ const { data: unprocessedStagedUnits, isLoading: stagingDataLoading } = useGetStagedUnitsQuery();
const { data: organizationsListData, isLoading: organizationsListLoading } = useGetOrganizationsListQuery();
const myOrganization = useMemo(
@@ -41,9 +50,27 @@ const MyUnitsPage: React.FC = () => {
[organizationsListData],
);
+ const processedStagingData: ProcessedStagingData = useMemo(() => {
+ const data: ProcessedStagingData = { staged: [], pending: [], failed: [] };
+ if (unprocessedStagedUnits?.forEach) {
+ unprocessedStagedUnits.forEach((stagedUnit: any) => {
+ if (stagedUnit?.table === 'Units') {
+ if (!stagedUnit.commited && !stagedUnit.failedCommit && !stagedUnit.isTransfer) {
+ data.staged.push(stagedUnit);
+ } else if (stagedUnit.commited && !stagedUnit.failedCommit && !stagedUnit.isTransfer) {
+ data.pending.push(stagedUnit);
+ } else if (!stagedUnit.commited && stagedUnit.failedCommit && !stagedUnit.isTransfer) {
+ data.failed.push(stagedUnit);
+ }
+ }
+ });
+ }
+ return data;
+ }, [unprocessedStagedUnits]);
+
const contentsLoading = useMemo(() => {
- return committedDataLoading;
- }, [committedDataLoading]);
+ return committedDataLoading || stagingDataLoading;
+ }, [committedDataLoading, stagingDataLoading]);
useEffect(() => {
if (myOrganization) {
@@ -57,7 +84,7 @@ const MyUnitsPage: React.FC = () => {
}
}, [myOrganization, navigate, organizationsListLoading]);
- const handleSearchChange: DebouncedFunc = useCallback(
+ const handleSearchChange = useCallback(
debounce((event: any) => {
setSearch(event.target.value);
}, 800),
@@ -72,7 +99,7 @@ const MyUnitsPage: React.FC = () => {
{contentsLoading &&
}
-
diff --git a/src/renderer/translations/tokens/en-US.json b/src/renderer/translations/tokens/en-US.json
index 86e2c837..3a443889 100644
--- a/src/renderer/translations/tokens/en-US.json
+++ b/src/renderer/translations/tokens/en-US.json
@@ -92,6 +92,11 @@
"transfers": "Transfers",
"my-units": "My Units",
"create-unit": "Create Unit",
+ "action": "Action",
+ "date-created": "Date Created",
+ "date-last-updated": "Date Last Updated",
+ "uuid": "UUID",
+ "staging-diff": "Staging Diff",
"retry": "Retry",
"unit": "Unit",
"detailed-unit-view": "Detailed Unit View"