Skip to content

Commit

Permalink
Working admin panel
Browse files Browse the repository at this point in the history
  • Loading branch information
fokolo committed Jan 5, 2025
1 parent 71e8eeb commit 1ee3dbd
Show file tree
Hide file tree
Showing 11 changed files with 774 additions and 8 deletions.
5 changes: 4 additions & 1 deletion backstage-plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@
"prettier --write"
]
},
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"dependencies": {
"recharts": "^2.15.0"
}
}
1 change: 1 addition & 0 deletions backstage-plugins/plugins/devex-survey-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Blueprint:
"work_planning_challenges",
"development_process_challenges",
"shipping_features_challenges",
"managing_production_challenges",
"email"
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { InfoCard } from '@backstage/core-components';
import React from 'react';
import {
Cell,
Legend,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
} from 'recharts';
import { SurveyResult } from '../../pages/admin';

type Props = {
results: SurveyResult[];
};

const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042'];

const renderCustomizedLabel = ({
cx,
cy,
midAngle,
innerRadius,
outerRadius,
value,
}: any) => {
const RADIAN = Math.PI / 180;
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);

return (
<text
x={x}
y={y}
fill="white"
textAnchor="middle"
dominantBaseline="central"
>
{value}
</text>
);
};

export const ChallengesBreakdown = ({ results }: Props) => {
const data = React.useMemo(() => {
const counts = results.reduce((acc, curr) => {
acc[curr.primary_blocker] = (acc[curr.primary_blocker] || 0) + 1;
return acc;
}, {} as Record<string, number>);

return Object.entries(counts).map(([key, value]) => ({
name: key.replace(/_/g, ' '),
value,
}));
}, [results]);

return (
<InfoCard title="All Challenges Distribution">
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={80}
fill="#8884d8"
dataKey="value"
label={renderCustomizedLabel}
>
{data.map((_, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
<Tooltip />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</InfoCard>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { InfoCard } from '@backstage/core-components';
import { Grid } from '@material-ui/core';
import React from 'react';
import {
Cell,
Legend,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
} from 'recharts';
import { SurveyResult } from '../../pages/admin';

type Props = {
results: SurveyResult[];
};

const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#FF99CC'];

const renderCustomizedLabel = ({
cx,
cy,
midAngle,
innerRadius,
outerRadius,
value,
}: any) => {
const RADIAN = Math.PI / 180;
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);

return (
<text
x={x}
y={y}
fill="white"
textAnchor="middle"
dominantBaseline="central"
>
{value}
</text>
);
};

const ChallengePieChart = ({
title,
data,
}: {
title: string;
data: { name: string; value: number }[];
}) => (
<InfoCard title={title}>
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={80}
fill="#8884d8"
dataKey="value"
label={renderCustomizedLabel}
>
{data.map((_, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
/>
))}
</Pie>
<Tooltip />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</InfoCard>
);

export const ChallengesDetails = ({ results }: Props) => {
const workPlanningData = React.useMemo(() => {
const counts = results.reduce((acc, curr) => {
acc[curr.work_planning_challenges] =
(acc[curr.work_planning_challenges] || 0) + 1;
return acc;
}, {} as Record<string, number>);

return Object.entries(counts).map(([key, value]) => ({
name: key.replace(/_/g, ' '),
value,
}));
}, [results]);

const developmentProcessData = React.useMemo(() => {
const counts = results.reduce((acc, curr) => {
acc[curr.development_process_challenges] =
(acc[curr.development_process_challenges] || 0) + 1;
return acc;
}, {} as Record<string, number>);

return Object.entries(counts).map(([key, value]) => ({
name: key.replace(/_/g, ' '),
value,
}));
}, [results]);

const shippingFeaturesData = React.useMemo(() => {
const counts = results.reduce((acc, curr) => {
acc[curr.shipping_features_challenges] =
(acc[curr.shipping_features_challenges] || 0) + 1;
return acc;
}, {} as Record<string, number>);

return Object.entries(counts).map(([key, value]) => ({
name: key.replace(/_/g, ' '),
value,
}));
}, [results]);

const productionChallengesData = React.useMemo(() => {
const counts = results.reduce((acc, curr) => {
acc[curr.managing_production_challenges] =
(acc[curr.managing_production_challenges] || 0) + 1;
return acc;
}, {} as Record<string, number>);

return Object.entries(counts).map(([key, value]) => ({
name: key.replace(/_/g, ' '),
value,
}));
}, [results]);

return (
<Grid container spacing={3}>
<Grid item md={6} xs={12}>
<ChallengePieChart
title="Work Planning Challenges"
data={workPlanningData}
/>
</Grid>
<Grid item md={6} xs={12}>
<ChallengePieChart
title="Development Process Challenges"
data={developmentProcessData}
/>
</Grid>
<Grid item md={6} xs={12}>
<ChallengePieChart
title="Shipping Features Challenges"
data={shippingFeaturesData}
/>
</Grid>
<Grid item md={6} xs={12}>
<ChallengePieChart
title="Production Management Challenges"
data={productionChallengesData}
/>
</Grid>
</Grid>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { InfoCard } from '@backstage/core-components';
import React from 'react';
import {
CartesianGrid,
Legend,
Line,
LineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { SurveyResult } from '../../pages/admin';

type Props = {
results: SurveyResult[];
};

export const ResponsesOverTime = ({ results }: Props) => {
const data = React.useMemo(() => {
// Group responses by date
const responsesByDate = results.reduce((acc, curr) => {
const date = curr.last_updated.toLocaleDateString();
acc[date] = (acc[date] || 0) + 1;
return acc;
}, {} as Record<string, number>);

// Convert to array and sort by date
const sortedData = Object.entries(responsesByDate)
.map(([date, count]) => ({
date,
responses: count,
total: 0, // Will be calculated next
}))
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());

// Calculate running total
let runningTotal = 0;
return sortedData.map(item => ({
...item,
total: (runningTotal += item.responses),
}));
}, [results]);

return (
<InfoCard title="Responses Over Time">
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Line
type="monotone"
dataKey="responses"
stroke="#8884d8"
name="Daily Responses"
/>
<Line
type="monotone"
dataKey="total"
stroke="#82ca9d"
name="Total Responses"
/>
</LineChart>
</ResponsiveContainer>
</div>
</InfoCard>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { InfoCard } from '@backstage/core-components';
import { Grid, Typography } from '@material-ui/core';
import React from 'react';
import { SurveyResult } from '../../pages/admin';

type Props = {
results: SurveyResult[];
};

export const SurveyStats = ({ results }: Props) => {
const totalResponses = results.length;
const lastResponseDate = results.length
? new Date(
Math.max(...results.map(r => new Date(r.last_updated).getTime())),
)
: null;

return (
<InfoCard title="Survey Statistics">
<Grid container spacing={3}>
<Grid item xs={5}>
<Typography variant="h4">{totalResponses}</Typography>
<Typography variant="subtitle1">Total Respondents</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="h4">
{lastResponseDate?.toLocaleDateString()}{' '}
{lastResponseDate?.toLocaleTimeString()}
</Typography>
<Typography variant="subtitle1">Last Response</Typography>
</Grid>
</Grid>
</InfoCard>
);
};
Loading

0 comments on commit 1ee3dbd

Please sign in to comment.