diff --git a/web-server/src/content/DoraMetrics/DoraCards/SkeletalCard.tsx b/web-server/src/content/DoraMetrics/DoraCards/SkeletalCard.tsx
new file mode 100644
index 000000000..def45db67
--- /dev/null
+++ b/web-server/src/content/DoraMetrics/DoraCards/SkeletalCard.tsx
@@ -0,0 +1,182 @@
+import { Paper, Grid, Divider, useTheme } from '@mui/material';
+import Link from 'next/link';
+import { FC, useEffect } from 'react';
+
+import { FlexBox } from '@/components/FlexBox';
+import { Line } from '@/components/Text';
+import { useBoolState } from '@/hooks/useEasyState';
+import { OPEN_IN_NEW_TAB_PROPS } from '@/utils/url';
+
+import { LoaderCore } from '../DoraMetricsBody';
+
+const ANIMATON_DURATION = 1000;
+
+export const DataStillSyncing = () => {
+ const flickerAnimation = useBoolState();
+
+ useEffect(() => {
+ const flickerInterval = setInterval(
+ flickerAnimation.toggle,
+ ANIMATON_DURATION
+ );
+
+ return () => {
+ clearInterval(flickerInterval);
+ };
+ }, [flickerAnimation.toggle]);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const SkeletalCard: FC<{ animation?: boolean }> = ({ animation }) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const ScoreSkeleton: FC<{ animation?: boolean }> = ({ animation }) => {
+ const theme = useTheme();
+ const stats = {
+ avg: 2,
+ standard: 6.3,
+ lt: 4,
+ df: 5,
+ cfr: 6,
+ mttr: 7
+ };
+
+ return (
+
+
+
+
+ Your DORA
+
+
+ Performance
+
+
+
+
+
+
+
+
+
+
+ Industry
+
+
+ Standard
+
+
+
+
+
+ {stats.standard}{' '}
+
+ / 10
+
+
+
+
+
+
+ Based on data available
+
+
+ at{' '}
+
+
+ dora.dev
+
+
+
+
+
+
+ );
+};
+
+const Skeleton: FC<{ width?: string; height?: string }> = ({
+ width = '150px',
+ height = '30px'
+}) => (
+
+);
diff --git a/web-server/src/content/DoraMetrics/DoraMetricsBody.tsx b/web-server/src/content/DoraMetrics/DoraMetricsBody.tsx
index c652668c3..ae3d70eb8 100644
--- a/web-server/src/content/DoraMetrics/DoraMetricsBody.tsx
+++ b/web-server/src/content/DoraMetrics/DoraMetricsBody.tsx
@@ -1,6 +1,6 @@
import { Grid, Divider, Button } from '@mui/material';
import Link from 'next/link';
-import { useEffect, useMemo } from 'react';
+import { FC, useEffect, useMemo } from 'react';
import { DoraMetricsConfigurationSettings } from '@/components/DoraMetricsConfigurationSettings';
import { DoraScore } from '@/components/DoraScore';
@@ -30,6 +30,7 @@ import { ClassificationPills } from './ClassificationPills';
import { ChangeFailureRateCard } from './DoraCards/ChangeFailureRateCard';
import { ChangeTimeCard } from './DoraCards/ChangeTimeCard';
import { MeanTimeToRestoreCard } from './DoraCards/MeanTimeToRestoreCard';
+import { DataStillSyncing } from './DoraCards/SkeletalCard';
import { WeeklyDeliveryVolumeCard } from './DoraCards/WeeklyDeliveryVolumeCard';
export const DoraMetricsBody = () => {
@@ -103,22 +104,7 @@ export const DoraMetricsBody = () => {
);
if (!firstLoadDone) return ;
if (isTeamInsightsEmpty)
- if (isSyncing)
- return (
-
-
-
- }
- >
-
-
- );
+ if (isSyncing) return ;
else
return (
{
};
};
-const ANIMATON_DURATION = 700;
+const ANIMATON_DURATION = 1000;
-const Syncing = () => {
+export const Syncing = () => {
const flickerAnimation = useBoolState(false);
const { isSyncing } = useSyncedRepos();
@@ -240,11 +226,26 @@ const Syncing = () => {
return (
+
+
+ );
+};
+
+export const LoaderCore: FC<{
+ animation?: boolean;
+}> = ({ animation }) => {
+ return (
+
@@ -267,7 +268,7 @@ const Syncing = () => {
height={'100%'}
/>
- {isSyncing && }
+ {}
Calculating Dora
diff --git a/web-server/src/slices/dora_metrics.ts b/web-server/src/slices/dora_metrics.ts
index 4956aa69a..14dc1e0ac 100644
--- a/web-server/src/slices/dora_metrics.ts
+++ b/web-server/src/slices/dora_metrics.ts
@@ -133,6 +133,12 @@ export const doraMetricsSlice = createSlice({
'deploymentPrs',
(state, action) => (state.deploymentPrs = action.payload)
);
+ addFetchCasesToReducer(
+ builder,
+ getSyncedRepos,
+ 'bookmarkedRepos',
+ (state, action) => (state.bookmarkedRepos = action.payload)
+ );
}
});
@@ -207,3 +213,12 @@ export const fetchDeploymentPRs = createAsyncThunk(
return await handleApi(`/internal/deployments/prs`, { params });
}
);
+
+export const getSyncedRepos = createAsyncThunk(
+ 'dora_metrics/getSyncedRepos',
+ async (params: { team_id: ID }) => {
+ return await handleApi(
+ `/resources/teams/${params.team_id}/bookmarked_repos`
+ );
+ }
+);