Skip to content

Commit bf227b7

Browse files
authored
Frontend for sources update (#204)
* query to fetch minutes per source; * updated endpoint to return only user's active sources; frontend update using sources with minutes endpoint; * added 2x modifier to minutes;
1 parent 013dc78 commit bf227b7

10 files changed

Lines changed: 159 additions & 25 deletions

File tree

packages/app/src/api/keys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ export const pulseKeys = {
77
['deepWork', startDate, endDate] as const,
88
categoryTimeOverview: (startDate: string, endDate: string) =>
99
['categoryTimeOverview', startDate, endDate] as const,
10+
sourcesMinutes: (startDate: string, endDate: string) =>
11+
['sourcesMinutes', startDate, endDate] as const,
1012
}

packages/app/src/api/pulse.api.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ export function useGetSources() {
2929
})
3030
}
3131

32+
export function useGetSourcesWithMinutes(startDate: string, endDate: string) {
33+
const queryFn = () =>
34+
apiRequest({
35+
url: `${BASE_API_URL}/pulses/sourcesMinutes?startDate=${startDate}&endDate=${endDate}`,
36+
method: 'GET',
37+
})
38+
return useBetterQuery<CodeClimbers.SourceWithMinutes[], Error>({
39+
queryKey: pulseKeys.sourcesMinutes(startDate, endDate),
40+
queryFn,
41+
refetchOnWindowFocus: true,
42+
})
43+
}
44+
3245
export function useExportPulses() {
3346
const exportPulses = useCallback(async () => {
3447
try {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { LinearProgress, Typography, useTheme } from '@mui/material'
2+
import Grid2 from '@mui/material/Unstable_Grid2/Grid2'
3+
4+
interface SourceTimeChartProps {
5+
color: string
6+
time: string
7+
progress: number
8+
}
9+
10+
export const SourceTimeChart = ({
11+
color,
12+
progress,
13+
time,
14+
}: SourceTimeChartProps) => {
15+
const theme = useTheme()
16+
17+
return (
18+
<Grid2
19+
container
20+
justifyContent="space-between"
21+
gap={3}
22+
alignItems="center"
23+
width="100%"
24+
>
25+
<Grid2 sx={{ flex: 1, width: '100%' }}>
26+
<LinearProgress
27+
variant="determinate"
28+
value={progress > 100 ? 100 : progress}
29+
sx={{
30+
alignSelf: 'center',
31+
width: '100%',
32+
backgroundColor: 'transparent',
33+
height: 12,
34+
'& .MuiLinearProgress-bar': {
35+
bottom: 0,
36+
top: 'auto',
37+
backgroundColor: color,
38+
height: 12,
39+
},
40+
}}
41+
/>
42+
</Grid2>
43+
<Grid2>
44+
<Typography variant="body1" color={theme.palette.grey[300]}>
45+
{time}
46+
</Typography>
47+
</Grid2>
48+
</Grid2>
49+
)
50+
}

packages/app/src/components/Home/Source/Sources.tsx

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from 'react'
12
import {
23
Button,
34
Card,
@@ -6,42 +7,81 @@ import {
67
Typography,
78
useTheme,
89
} from '@mui/material'
10+
import { LoadingButton } from '@mui/lab'
911
import SaveAltOutlinedIcon from '@mui/icons-material/SaveAltOutlined'
1012
import AddIcon from '@mui/icons-material/Add'
11-
import { useExportPulses, useGetSources } from '../../../api/pulse.api'
13+
import dayjs from 'dayjs'
14+
15+
import {
16+
useExportPulses,
17+
useGetSourcesWithMinutes,
18+
} from '../../../api/pulse.api'
1219
import {
1320
SourceDetails,
1421
supportedSources,
1522
} from '../../../utils/supportedSources'
16-
import { useState } from 'react'
1723
import SourcesEmpty from './Sources.empty'
1824
import SourcesError from './Sources.error'
1925
import SourcesLoading from './Sources.loading'
20-
import { LoadingButton } from '@mui/lab'
2126
import AddSources from './AddSources'
2227
import { getTimeSince } from '../../../utils/time'
28+
import { minutesToHours } from '../Time/utils'
29+
import { SourceTimeChart } from './SourceTimeChart'
2330

2431
interface SourceRowProps {
2532
source: SourceDetails
2633
lastActive: string
34+
minutes: number
2735
}
2836

29-
const SourceRow = ({ source, lastActive }: SourceRowProps) => {
37+
const SourceRow = ({ source, lastActive, minutes }: SourceRowProps) => {
38+
const theme = useTheme()
39+
const compareTime = 90
40+
3041
return (
31-
<Stack direction="row" alignItems="center" justifyContent="space-between">
32-
<Stack direction="row" alignItems="center" spacing={1}>
42+
<Stack
43+
direction="row"
44+
alignItems="center"
45+
justifyContent="space-between"
46+
width="100%"
47+
>
48+
<Stack direction="row" alignItems="center" spacing={1} width="100%">
3349
<img
3450
alt={source.displayName + ' Logo'}
3551
src={source.logo}
3652
style={{ height: '32px', width: '32px' }}
3753
/>
38-
<Stack direction="column">
54+
<Stack direction="column" width="100%">
3955
<Typography variant="body2" fontWeight={700}>
4056
{source.displayName}
4157
</Typography>
42-
<Typography variant="body2" fontWeight={400}>
43-
{`Last pulse ${getTimeSince(lastActive)}`}
44-
</Typography>
58+
{minutes > 0 && (
59+
<SourceTimeChart
60+
time={minutesToHours(minutes)}
61+
progress={Math.floor((minutes / compareTime) * 100)}
62+
color={theme.palette.graphColors.blue}
63+
/>
64+
)}
65+
<Stack direction="row" justifyContent="space-between">
66+
{minutes === 0 && lastActive && (
67+
<Typography
68+
variant="body2"
69+
fontWeight={400}
70+
color={theme.palette.grey[300]}
71+
>
72+
{`Last pulse ${getTimeSince(lastActive)}`}
73+
</Typography>
74+
)}
75+
{minutes === 0 && (
76+
<Typography
77+
variant="body1"
78+
fontWeight={400}
79+
color={theme.palette.grey[300]}
80+
>
81+
0m
82+
</Typography>
83+
)}
84+
</Stack>
4585
</Stack>
4686
</Stack>
4787
</Stack>
@@ -50,11 +90,15 @@ const SourceRow = ({ source, lastActive }: SourceRowProps) => {
5090

5191
const Sources = () => {
5292
const {
53-
data: connectedSources,
93+
data: sourcesWithMinutes,
5494
isPending,
5595
isEmpty,
5696
isError,
57-
} = useGetSources()
97+
} = useGetSourcesWithMinutes(
98+
dayjs().startOf('day').toISOString(),
99+
dayjs().endOf('day').toISOString(),
100+
)
101+
58102
const { exportPulses } = useExportPulses()
59103
const [exportingPulses, setExportingPulses] = useState(false)
60104
const [addSourcesOpen, setAddSourcesOpen] = useState(false)
@@ -78,7 +122,7 @@ const Sources = () => {
78122

79123
return (
80124
<>
81-
<Card sx={{ boxShadow: 'none', borderRadius: 0, minWidth: 262, flex: 1 }}>
125+
<Card sx={{ boxShadow: 'none', borderRadius: 0, minWidth: 335, flex: 1 }}>
82126
<CardContent sx={{ padding: '24px', height: '100%', display: 'flex' }}>
83127
<Stack direction="column" justifyContent={'space-between'} flex={1}>
84128
<div>
@@ -106,9 +150,10 @@ const Sources = () => {
106150
</Button>
107151
</Stack>
108152
<Stack direction="column" marginTop="24px" gap={3}>
109-
{connectedSources.map((source, index) => {
153+
{sourcesWithMinutes.map((source, index) => {
110154
const sourceDetails = supportedSources.find(
111-
(supportedSource) => supportedSource.name === source.name,
155+
(supportedSource) =>
156+
supportedSource.name.includes(source.name),
112157
)
113158

114159
if (sourceDetails) {
@@ -117,6 +162,7 @@ const Sources = () => {
117162
key={index}
118163
source={sourceDetails}
119164
lastActive={source.lastActive}
165+
minutes={source.minutes}
120166
/>
121167
)
122168
}

packages/app/src/components/Home/Time/TimeDataChart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const TimeDataChart = ({
5656
return <Box sx={{ position: 'relative' }}>{progressBars}</Box>
5757
}
5858
return (
59-
<Grid2 container justifyContent="space-between" gap={3} alignItems="center">
59+
<Grid2 container justifyContent="space-between" gap={3}>
6060
<Grid2 sx={{ minWidth: 120 }}>
6161
<Typography variant="body1">{title}</Typography>
6262
</Grid2>

packages/app/src/utils/supportedSources.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const supportedSources: SourceDetails[] = [
2424
`,
2525
},
2626
{
27-
name: 'chrome-code_climbers',
27+
name: 'chrome',
2828
displayName: 'Chromium Browser',
2929
logo: chromeLogo,
3030
link: 'https://chromewebstore.google.com/detail/code-climbers/fdmoefklpgbjapealpjfailnmalbgpbe',

packages/app/src/utils/time.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ dayjs.updateLocale('en', {
1515
mm: '%dm',
1616
h: '1h',
1717
hh: '%dh',
18-
d: '1D',
19-
dd: '%dD',
18+
d: '1d',
19+
dd: '%dd',
2020
M: '1M',
2121
MM: '%dM',
2222
y: '1Y',

packages/server/src/types/activities.api.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ declare namespace CodeClimbers {
1515
name: string
1616
lastActive: string
1717
}
18+
19+
export interface SourceWithMinutes {
20+
name: string
21+
lastActive: string
22+
minutes: number
23+
}
1824
}

packages/server/src/types/time.api.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ declare namespace CodeClimbers {
3333
}
3434

3535
export interface SourceMinutes {
36-
source: string
36+
name: string
3737
minutes: number
38+
lastActive: string
3839
}
3940

4041
export interface SourcesOverviewDao {

packages/server/src/v1/activities/activities.service.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,28 @@ export class ActivitiesService {
136136
startDate: string,
137137
endDate: string,
138138
): Promise<CodeClimbers.SourceMinutes[]> {
139-
const sources = await this.pulseRepo.getSourcesMinutes(startDate, endDate)
139+
const userSources = await this.getSources()
140+
const sourcesWithMinutes = await this.pulseRepo.getSourcesMinutes(
141+
startDate,
142+
endDate,
143+
)
144+
145+
return Object.keys(sourcesWithMinutes)
146+
.map((key) => {
147+
const lastActive =
148+
userSources.find((source) =>
149+
source.name.toLowerCase().includes(key.toLowerCase()),
150+
)?.lastActive ?? null
151+
152+
if (lastActive === null) return null
140153

141-
return Object.keys(sources).map((key) => ({
142-
source: key,
143-
minutes: sources[key],
144-
}))
154+
return {
155+
name: key,
156+
minutes: sourcesWithMinutes[key] * 2,
157+
lastActive,
158+
}
159+
})
160+
.filter((item) => item !== null)
145161
}
146162

147163
async getDeepWork(

0 commit comments

Comments
 (0)