mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-04 01:18:20 +02:00
feat: activity chart polish (#8665)

This commit is contained in:
parent
ba72be6169
commit
d6e722b7b3
@ -5,22 +5,25 @@ import type { ProjectActivitySchema } from '../../../../openapi';
|
|||||||
import { styled, Tooltip } from '@mui/material';
|
import { styled, Tooltip } from '@mui/material';
|
||||||
|
|
||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
gap: theme.spacing(1),
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(2),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const TitleContainer = styled('h4')({
|
||||||
|
margin: 0,
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
type Output = { date: string; count: number; level: number };
|
type Output = { date: string; count: number; level: number };
|
||||||
|
|
||||||
export function transformData(inputData: ProjectActivitySchema): Output[] {
|
export function transformData(inputData: ProjectActivitySchema): Output[] {
|
||||||
const resultMap: Record<string, number> = {};
|
const countArray = inputData.map((item) => item.count);
|
||||||
|
|
||||||
// Step 1: Count the occurrences of each date
|
|
||||||
inputData.forEach((item) => {
|
|
||||||
const formattedDate = new Date(item.date).toISOString().split('T')[0];
|
|
||||||
resultMap[formattedDate] = (resultMap[formattedDate] || 0) + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 2: Get all counts, sort them, and find the cut-off values for percentiles
|
// Step 2: Get all counts, sort them, and find the cut-off values for percentiles
|
||||||
const counts = Object.values(resultMap).sort((a, b) => a - b);
|
const counts = Object.values(countArray).sort((a, b) => a - b);
|
||||||
|
|
||||||
const percentile = (percent: number) => {
|
const percentile = (percent: number) => {
|
||||||
const index = Math.floor((percent / 100) * counts.length);
|
const index = Math.floor((percent / 100) * counts.length);
|
||||||
@ -43,13 +46,13 @@ export function transformData(inputData: ProjectActivitySchema): Output[] {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Step 4: Convert the map back to an array and assign levels
|
// Step 4: Convert the map back to an array and assign levels
|
||||||
return Object.entries(resultMap)
|
return inputData
|
||||||
.map(([date, count]) => ({
|
.map(({ date, count }) => ({
|
||||||
date,
|
date,
|
||||||
count,
|
count,
|
||||||
level: calculateLevel(count),
|
level: calculateLevel(count),
|
||||||
}))
|
}))
|
||||||
.reverse(); // Optional: reverse the order if needed
|
.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectActivity = () => {
|
export const ProjectActivity = () => {
|
||||||
@ -64,10 +67,10 @@ export const ProjectActivity = () => {
|
|||||||
const levelledData = transformData(data.activityCountByDate);
|
const levelledData = transformData(data.activityCountByDate);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<>
|
||||||
{data.activityCountByDate.length > 0 ? (
|
{data.activityCountByDate.length > 0 ? (
|
||||||
<>
|
<StyledContainer>
|
||||||
<span>Activity in project</span>
|
<TitleContainer>Activity in project</TitleContainer>
|
||||||
<ActivityCalendar
|
<ActivityCalendar
|
||||||
theme={explicitTheme}
|
theme={explicitTheme}
|
||||||
data={levelledData}
|
data={levelledData}
|
||||||
@ -81,10 +84,10 @@ export const ProjectActivity = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</StyledContainer>
|
||||||
) : (
|
) : (
|
||||||
<span>No activity</span>
|
<span>No activity</span>
|
||||||
)}
|
)}
|
||||||
</StyledContainer>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -409,7 +409,7 @@ class EventStore implements IEventStore {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProjectEventActivity(
|
async getProjectRecentEventActivity(
|
||||||
project: string,
|
project: string,
|
||||||
): Promise<ProjectActivitySchema> {
|
): Promise<ProjectActivitySchema> {
|
||||||
const result = await this.db('events')
|
const result = await this.db('events')
|
||||||
@ -418,6 +418,11 @@ class EventStore implements IEventStore {
|
|||||||
)
|
)
|
||||||
.count('* AS count')
|
.count('* AS count')
|
||||||
.where('project', project)
|
.where('project', project)
|
||||||
|
.andWhere(
|
||||||
|
'created_at',
|
||||||
|
'>=',
|
||||||
|
this.db.raw("NOW() - INTERVAL '1 year'"),
|
||||||
|
)
|
||||||
.groupBy(this.db.raw("TO_CHAR(created_at::date, 'YYYY-MM-DD')"))
|
.groupBy(this.db.raw("TO_CHAR(created_at::date, 'YYYY-MM-DD')"))
|
||||||
.orderBy('date', 'asc');
|
.orderBy('date', 'asc');
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export class ProjectStatusService {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
activityCountByDate:
|
activityCountByDate:
|
||||||
await this.eventStore.getProjectEventActivity(projectId),
|
await this.eventStore.getProjectRecentEventActivity(projectId),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,5 +47,7 @@ export interface IEventStore
|
|||||||
queryCount(operations: IQueryOperations[]): Promise<number>;
|
queryCount(operations: IQueryOperations[]): Promise<number>;
|
||||||
setCreatedByUserId(batchSize: number): Promise<number | undefined>;
|
setCreatedByUserId(batchSize: number): Promise<number | undefined>;
|
||||||
getEventCreators(): Promise<Array<{ id: number; name: string }>>;
|
getEventCreators(): Promise<Array<{ id: number; name: string }>>;
|
||||||
getProjectEventActivity(project: string): Promise<ProjectActivitySchema>;
|
getProjectRecentEventActivity(
|
||||||
|
project: string,
|
||||||
|
): Promise<ProjectActivitySchema>;
|
||||||
}
|
}
|
||||||
|
4
src/test/fixtures/fake-event-store.ts
vendored
4
src/test/fixtures/fake-event-store.ts
vendored
@ -18,7 +18,9 @@ class FakeEventStore implements IEventStore {
|
|||||||
this.events = [];
|
this.events = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjectEventActivity(project: string): Promise<ProjectActivitySchema> {
|
getProjectRecentEventActivity(
|
||||||
|
project: string,
|
||||||
|
): Promise<ProjectActivitySchema> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user