Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions thingconnect.pulse.client/obj/Debug/package.g.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@
<PackageJsonScriptsPreview Condition="$(PackageJsonScriptsPreview) == ''">vite preview</PackageJsonScriptsPreview>
<PackageJsonScriptsFormat Condition="$(PackageJsonScriptsFormat) == ''">prettier --write .</PackageJsonScriptsFormat>
<PackageJsonScriptsPrepare Condition="$(PackageJsonScriptsPrepare) == ''">cd .. &amp;&amp; husky ./thingconnect.pulse.client/.husky</PackageJsonScriptsPrepare>
<PackageJsonDependenciesChakraUiCharts Condition="$(PackageJsonDependenciesChakraUiCharts) == ''">^3.27.0</PackageJsonDependenciesChakraUiCharts>
<PackageJsonDependenciesChakraUiReact Condition="$(PackageJsonDependenciesChakraUiReact) == ''">^3.24.2</PackageJsonDependenciesChakraUiReact>
<PackageJsonDependenciesEmotionReact Condition="$(PackageJsonDependenciesEmotionReact) == ''">^11.14.0</PackageJsonDependenciesEmotionReact>
<PackageJsonDependenciesHookformResolvers Condition="$(PackageJsonDependenciesHookformResolvers) == ''">^5.2.1</PackageJsonDependenciesHookformResolvers>
<PackageJsonDependenciesMonacoEditorReact Condition="$(PackageJsonDependenciesMonacoEditorReact) == ''">^4.7.0</PackageJsonDependenciesMonacoEditorReact>
<PackageJsonDependenciesSentryReact Condition="$(PackageJsonDependenciesSentryReact) == ''">^10.11.0</PackageJsonDependenciesSentryReact>
<PackageJsonDependenciesTanstackReactQuery Condition="$(PackageJsonDependenciesTanstackReactQuery) == ''">^5.84.2</PackageJsonDependenciesTanstackReactQuery>
<PackageJsonDependenciesVisxAxis Condition="$(PackageJsonDependenciesVisxAxis) == ''">^3.12.0</PackageJsonDependenciesVisxAxis>
<PackageJsonDependenciesVisxResponsive Condition="$(PackageJsonDependenciesVisxResponsive) == ''">^3.12.0</PackageJsonDependenciesVisxResponsive>
<PackageJsonDependenciesVisxScale Condition="$(PackageJsonDependenciesVisxScale) == ''">^3.12.0</PackageJsonDependenciesVisxScale>
<PackageJsonDependenciesVisxShape Condition="$(PackageJsonDependenciesVisxShape) == ''">^3.12.0</PackageJsonDependenciesVisxShape>
<PackageJsonDependenciesVisxTooltip Condition="$(PackageJsonDependenciesVisxTooltip) == ''">^3.12.0</PackageJsonDependenciesVisxTooltip>
<PackageJsonDependenciesAxios Condition="$(PackageJsonDependenciesAxios) == ''">^1.11.0</PackageJsonDependenciesAxios>
<PackageJsonDependenciesDateFns Condition="$(PackageJsonDependenciesDateFns) == ''">^4.1.0</PackageJsonDependenciesDateFns>
<PackageJsonDependenciesFramerMotion Condition="$(PackageJsonDependenciesFramerMotion) == ''">^12.23.14</PackageJsonDependenciesFramerMotion>
Expand All @@ -37,6 +33,7 @@
<PackageJsonDependenciesReactHookForm Condition="$(PackageJsonDependenciesReactHookForm) == ''">^7.62.0</PackageJsonDependenciesReactHookForm>
<PackageJsonDependenciesReactIcons Condition="$(PackageJsonDependenciesReactIcons) == ''">^5.5.0</PackageJsonDependenciesReactIcons>
<PackageJsonDependenciesReactRouterDom Condition="$(PackageJsonDependenciesReactRouterDom) == ''">^7.8.1</PackageJsonDependenciesReactRouterDom>
<PackageJsonDependenciesRecharts Condition="$(PackageJsonDependenciesRecharts) == ''">^3.2.1</PackageJsonDependenciesRecharts>
<PackageJsonDependenciesZod Condition="$(PackageJsonDependenciesZod) == ''">^4.1.9</PackageJsonDependenciesZod>
<PackageJsonDevdependenciesChakraUiCli Condition="$(PackageJsonDevdependenciesChakraUiCli) == ''">^3.24.0</PackageJsonDevdependenciesChakraUiCli>
<PackageJsonDevdependenciesEslintJs Condition="$(PackageJsonDevdependenciesEslintJs) == ''">^9.33.0</PackageJsonDevdependenciesEslintJs>
Expand Down
1,064 changes: 434 additions & 630 deletions thingconnect.pulse.client/package-lock.json

Large diffs are not rendered by default.

7 changes: 2 additions & 5 deletions thingconnect.pulse.client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@
"prepare": "cd .. && husky ./thingconnect.pulse.client/.husky"
},
"dependencies": {
"@chakra-ui/charts": "^3.27.0",
"@chakra-ui/react": "^3.24.2",
"@emotion/react": "^11.14.0",
"@hookform/resolvers": "^5.2.1",
"@monaco-editor/react": "^4.7.0",
"@sentry/react": "^10.11.0",
"@tanstack/react-query": "^5.84.2",
"@visx/axis": "^3.12.0",
"@visx/responsive": "^3.12.0",
"@visx/scale": "^3.12.0",
"@visx/shape": "^3.12.0",
"@visx/tooltip": "^3.12.0",
"axios": "^1.11.0",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.14",
Expand All @@ -38,6 +34,7 @@
"react-hook-form": "^7.62.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.8.1",
"recharts": "^3.2.1",
"zod": "^4.1.9"
},
"devDependencies": {
Expand Down
143 changes: 46 additions & 97 deletions thingconnect.pulse.client/src/components/AvailabilityChart.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { useMemo } from 'react';
import { Box, Text, VStack, Skeleton } from '@chakra-ui/react';
import { ParentSize } from '@visx/responsive';
import { Group } from '@visx/group';
import { AxisLeft, AxisBottom } from '@visx/axis';
import { scaleBand, scaleLinear } from '@visx/scale';
import { Chart, useChart } from '@chakra-ui/charts';
import { Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from 'recharts';
import type { HistoryResponse } from '@/api/types';
import type { BucketType } from '@/types/bucket';
import { CloudOff } from 'lucide-react';
Expand All @@ -18,37 +16,42 @@

export function AvailabilityChart({ data, bucket, isLoading }: AvailabilityChartProps) {
const chartData = useMemo(() => {
if (!data) return null;
if (!data) return [];
switch (bucket) {
case 'raw':
return data.raw.map(check => ({
xaxis: new Date(check.ts).toLocaleTimeString('en-US', {
label: new Date(check.ts).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
}),
yaxis: check.status === 'up' ? 100 : 0,
uptime: check.status === 'up' ? 100 : 0,
}));
case '15m':
return data.rollup15m.map(bucket => ({
xaxis: new Date(bucket.bucketTs).toLocaleTimeString('en-US', {
label: new Date(bucket.bucketTs).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
}),
yaxis: bucket.upPct,
uptime: bucket.upPct,
}));
case 'daily':
return data.rollupDaily.map(bucket => ({
xaxis: new Date(bucket.bucketDate).toLocaleDateString('en-US', {
label: new Date(bucket.bucketDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
}),
yaxis: bucket.upPct,
uptime: bucket.upPct,
}));
default:
return [];
}
}, [data, bucket]);

const chart = useChart({
data: chartData,
series: [{ name: 'uptime', color: 'blue.500' }],
});

if (chartData?.length === 0) {
return (
<Box
Expand Down Expand Up @@ -92,92 +95,38 @@
);
}

const margin = { top: 20, right: 30, bottom: 40, left: 70 };

return (
<Box flex={1} minH={0} w='full' h='full' position='relative'>
<ParentSize>
{({ width, height }) => {
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;

const xScale = scaleBand({
range: [0, xMax],
domain: chartData?.map(d => d.xaxis) ?? [],
padding: 0.2,
});

const yScale = scaleLinear<number>({
range: [yMax, 0],
domain: [0, 100],
});

return (
<svg width={width} height={height}>
<Group left={margin.left} top={margin.top}>
{chartData?.map((d, i) => {
const barWidth = xScale.bandwidth();
const barHeight = yMax - (yScale(d.yaxis) ?? 0);
const x = xScale(d.xaxis) ?? 0;
const y = yMax - barHeight;
return (
<rect
key={`bar-${i}`}
x={x}
y={y}
width={barWidth}
height={barHeight}
fill='#3182ce'
/>
);
})}

<AxisLeft
scale={yScale}
tickFormat={d => `${d}%`}
stroke='#718096'
tickStroke='transparent'
tickLabelProps={{
fill: '#718096',
textAnchor: 'end',
dx: -4,
style: {
fontSize: '12px',
},
}}
/>

{/* Y-axis label */}
<text
x={-margin.left + 15}
y={yMax / 2}
style={{ fontSize: '14px' }}
fill='#718096'
textAnchor='middle'
transform={`rotate(-90, ${-margin.left + 15}, ${yMax / 2})`}
>
Uptime %
</text>

<AxisBottom
top={yMax}
scale={xScale}
stroke='#718096'
tickStroke='#718096'
tickFormat={() => ''}
tickLabelProps={{
fill: '#718096',
textAnchor: 'middle',
style: {
fontSize: '12px',
},
}}
/>
</Group>
</svg>
);
}}
</ParentSize>
</Box>
<Chart.Root chart={chart} w='100%' h='100%'>
<BarChart data={chart.data}>
<CartesianGrid stroke={chart.color('border.muted')} vertical={false} />

Check failure on line 101 in thingconnect.pulse.client/src/components/AvailabilityChart.tsx

View workflow job for this annotation

GitHub Actions / Frontend Code Quality

Unsafe assignment of an `any` value
<XAxis axisLine={true} tickLine={false} dataKey={chart.key('label')} tick={false} />
<YAxis
label={{ value: 'Uptime %', angle: -90, position: 'insideLeft' }}
axisLine={false}
tickLine={false}
domain={[0, 100]}
ticks={[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]}
tickFormatter={v => `${v}%`}
/>
<Tooltip
content={({ active, payload, label }) => {
if (active && payload && payload.length) {
const uptime = payload[0].payload.uptime;

Check failure on line 114 in thingconnect.pulse.client/src/components/AvailabilityChart.tsx

View workflow job for this annotation

GitHub Actions / Frontend Code Quality

Unsafe assignment of an `any` value
return (
<Box bg='gray.700' color='white' p={2} borderRadius='md'>
<Text fontSize='sm'>{`Time: ${label}`}</Text>
<Text fontSize='sm'>{`Uptime: ${uptime.toFixed(3)}%`}</Text>
</Box>
);
}
return null;
}}
/>
{chart.series.map(s => (
<Bar key={s.name} dataKey={chart.key(s.name)} fill={chart.color(s.color)} />
))}
<YAxis />
</BarChart>
</Chart.Root>
);
}
3 changes: 3 additions & 0 deletions thingconnect.pulse.client/src/components/OutageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
gap={1}
py={5}
h='100%'
bg='gray.50'
_dark={{ bg: 'gray.800' }}
borderRadius='md'
>
<CloudOff size={'40px'} />
<Text textAlign='center' color='gray.500'>
Expand Down Expand Up @@ -74,7 +77,7 @@
return (
<VStack gap={4} align='stretch'>
{sortedOutages.map((outage, index) => (
<Card.Root key={`${outage.startedTs}-${index}`} size={'sm'}>

Check warning on line 80 in thingconnect.pulse.client/src/components/OutageList.tsx

View workflow job for this annotation

GitHub Actions / Frontend Code Quality

Do not use item index in the array as its key
<Card.Body p={3}>
<VStack gap={2} align='stretch'>
<HStack justify='space-between'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
gap={1}
py={5}
h='100%'
bg='gray.50'
_dark={{ bg: 'gray.800' }}
borderRadius='md'
>
<CloudOff size={'40px'} />
<Text textAlign='center' color='gray.500'>
Expand All @@ -59,7 +62,7 @@
</Table.Header>
<Table.Body>
{pagedChecks.map((check, index) => (
<Table.Row key={`${check.ts}-${index}`}>

Check warning on line 65 in thingconnect.pulse.client/src/components/RecentChecksTable.tsx

View workflow job for this annotation

GitHub Actions / Frontend Code Quality

Do not use item index in the array as its key
<Table.Cell w='30%'>
<Text flex='1' fontSize='sm'>
{formatDistanceToNow(new Date(check.ts), { addSuffix: true })}
Expand Down
16 changes: 16 additions & 0 deletions thingconnect.pulse.client/src/icons/Discord.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

export function Discord(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
role='img'
viewBox='0 0 24 24'
xmlns='http://www.w3.org/2000/svg'
{...props}
fill='currentColor'
>
<title>Discord</title>
<path d='M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z' />
</svg>
);
}
38 changes: 30 additions & 8 deletions thingconnect.pulse.client/src/pages/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { PageHeader } from '@/components/layout/PageHeader';
import { useForceRefreshNotifications, useNotificationStats } from '@/hooks/useNotifications';
import thingConnectLogo from '@/assets/thingconnect-pulse-logo.svg';
import { Discord } from '@/icons/Discord';

export default function About() {
const { data: stats } = useNotificationStats();
Expand Down Expand Up @@ -122,7 +123,7 @@ export default function About() {
<Grid templateColumns={{ base: '1fr', md: 'repeat(2, 1fr)' }} gap={6}>
{[
{
icon: MessageCircle,
icon: Discord,
title: 'Discord',
desc: 'Community support and real-time help',
tags: ['Community Support', 'Q&A', 'General Chat', 'Networking'],
Expand All @@ -133,7 +134,7 @@ export default function About() {
title: 'Reddit',
desc: 'Share questions and experiences',
tags: ['Discussions', 'Tips', 'Troubleshooting'],
link: 'https://reddit.com',
link: 'https://www.reddit.com/r/thingconnectio/',
},
{
icon: Linkedin,
Expand Down Expand Up @@ -340,7 +341,10 @@ export default function About() {
<Text fontSize='sm' color='gray.600' _dark={{ color: 'gray.400' }}>
Unread Count:
</Text>
<Badge variant='solid' colorPalette={stats?.unreadNotifications ? 'red' : 'gray'}>
<Badge
variant='solid'
colorPalette={stats?.unreadNotifications ? 'red' : 'gray'}
>
{stats?.unreadNotifications || 0}
</Badge>
</HStack>
Expand All @@ -350,7 +354,9 @@ export default function About() {
Last Sync:
</Text>
<Text fontSize='sm' color='gray.700' _dark={{ color: 'gray.300' }}>
{stats?.lastFetch ? new Date(stats.lastFetch).toLocaleDateString() : 'Never'}
{stats?.lastFetch
? new Date(stats.lastFetch).toLocaleDateString()
: 'Never'}
</Text>
</HStack>

Expand Down Expand Up @@ -389,7 +395,8 @@ export default function About() {
</HStack>

<Text fontSize='sm' color='gray.600' _dark={{ color: 'gray.400' }} mb={4}>
Notifications are automatically synced every 6 hours. Use the button below to trigger an immediate refresh.
Notifications are automatically synced every 6 hours. Use the button below to
trigger an immediate refresh.
</Text>

<VStack align='stretch' gap={3}>
Expand All @@ -405,18 +412,33 @@ export default function About() {
</Button>

{refreshMutation.isSuccess && (
<Text fontSize='sm' color='green.600' _dark={{ color: 'green.400' }} textAlign='center'>
<Text
fontSize='sm'
color='green.600'
_dark={{ color: 'green.400' }}
textAlign='center'
>
Notifications refreshed successfully!
</Text>
)}

{refreshMutation.isError && (
<Text fontSize='sm' color='red.600' _dark={{ color: 'red.400' }} textAlign='center'>
<Text
fontSize='sm'
color='red.600'
_dark={{ color: 'red.400' }}
textAlign='center'
>
Failed to refresh notifications. Please try again.
</Text>
)}

<Text fontSize='xs' color='gray.500' _dark={{ color: 'gray.500' }} textAlign='center'>
<Text
fontSize='xs'
color='gray.500'
_dark={{ color: 'gray.500' }}
textAlign='center'
>
Syncs from: thingconnect-pulse.s3.ap-south-1.amazonaws.com
</Text>
</VStack>
Expand Down
Loading
Loading