Skip to content

Commit 4f312eb

Browse files
committed
feat: add DashboardStats model and query; implement dashboard statistics retrieval in service and resolver
1 parent edae0c9 commit 4f312eb

File tree

8 files changed

+151
-30
lines changed

8 files changed

+151
-30
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ObjectType, Field, Int } from '@nestjs/graphql';
2+
3+
@ObjectType()
4+
export class DashboardStats {
5+
@Field(() => Int)
6+
totalUsers: number;
7+
8+
@Field(() => Int)
9+
activeUsers: number;
10+
11+
@Field(() => Int)
12+
totalChats: number;
13+
14+
@Field(() => Int)
15+
activeChats: number;
16+
17+
@Field(() => Int)
18+
totalProjects: number;
19+
20+
@Field(() => Int)
21+
activeProjects: number;
22+
23+
@Field(() => Int)
24+
totalRoles: number;
25+
26+
@Field(() => Int)
27+
totalMenus: number;
28+
}

backend/src/dashboard/dashboard.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { AuthModule } from '../auth/auth.module';
1616
import { ChatModule } from '../chat/chat.module';
1717
import { ProjectModule } from '../project/project.module';
1818
import { JwtCacheModule } from '../jwt-cache/jwt-cache.module';
19-
import { Menu } from 'src/decorator/menu.decorator';
19+
import { Menu } from 'src/auth/menu/menu.model';
2020

2121
@Module({
2222
imports: [

backend/src/dashboard/dashboard.resolver.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { UpdateRoleInput } from 'src/auth/role/dto/update-role.input';
2323
import { Role } from 'src/auth/role/role.model';
2424
import { GetUserIdFromToken } from 'src/decorator/get-auth-token.decorator';
2525
import { CreateProjectInput } from 'src/project/dto/project.input';
26+
import { DashboardStats } from './dashboard-stat.model';
2627

2728
@Resolver()
2829
@UseGuards(JWTAuthGuard)
@@ -177,4 +178,9 @@ export class DashboardResolver {
177178
): Promise<boolean> {
178179
return this.dashboardService.deleteRole(id);
179180
}
181+
182+
@Query(() => DashboardStats)
183+
async dashboardStats(): Promise<DashboardStats> {
184+
return this.dashboardService.getDashboardStats();
185+
}
180186
}

backend/src/dashboard/dashboard.service.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { UpdateRoleInput } from 'src/auth/role/dto/update-role.input';
2626
import { Menu } from 'src/auth/menu/menu.model';
2727
import { ProjectService } from 'src/project/project.service';
2828
import { CreateProjectInput } from 'src/project/dto/project.input';
29+
import { DashboardStats } from './dashboard-stat.model';
2930

3031
@Injectable()
3132
export class DashboardService {
@@ -338,4 +339,42 @@ export class DashboardService {
338339
await this.roleRepository.save(role);
339340
return true;
340341
}
342+
343+
async getDashboardStats(): Promise<DashboardStats> {
344+
const totalUsers = await this.userRepository.count({
345+
where: { isDeleted: false },
346+
});
347+
const activeUsers = await this.userRepository.count({
348+
where: { isActive: true, isDeleted: false },
349+
});
350+
const totalChats = await this.chatRepository.count({
351+
where: { isDeleted: false },
352+
});
353+
const activeChats = await this.chatRepository.count({
354+
where: { isActive: true, isDeleted: false },
355+
});
356+
const totalProjects = await this.projectRepository.count({
357+
where: { isDeleted: false },
358+
});
359+
const activeProjects = await this.projectRepository.count({
360+
where: { isActive: true, isDeleted: false },
361+
});
362+
const totalRoles = await this.roleRepository.count({
363+
where: { isDeleted: false },
364+
});
365+
const totalMenus = await this.menuRepository.count({
366+
where: { isDeleted: false },
367+
});
368+
369+
return {
370+
totalUsers,
371+
activeUsers,
372+
totalChats,
373+
activeChats,
374+
totalProjects,
375+
activeProjects,
376+
totalRoles,
377+
totalMenus,
378+
};
379+
}
341380
}

dashboard/src/content/dashboard/Overview/DashboardOverview.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import ChatIcon from '@mui/icons-material/Chat';
1212
import FolderIcon from '@mui/icons-material/Folder';
1313
import SecurityIcon from '@mui/icons-material/Security';
1414
import MenuIcon from '@mui/icons-material/Menu';
15+
import { useQuery } from '@apollo/client';
16+
import { GET_DASHBOARD_STATS } from 'src/graphql/request';
1517

1618
interface StatCardProps {
1719
title: string;
@@ -55,6 +57,12 @@ const StatCard: FC<StatCardProps> = ({ title, value, icon, description }) => (
5557
);
5658

5759
const DashboardOverview: FC = () => {
60+
const { data, loading, error } = useQuery(GET_DASHBOARD_STATS);
61+
62+
if (loading) return <div>Loading...</div>;
63+
if (error) return <div>Error: {error.message}</div>;
64+
65+
const stats = data.dashboardStats;
5866
return (
5967
<>
6068
<Box
@@ -65,48 +73,38 @@ const DashboardOverview: FC = () => {
6573
>
6674
<Container maxWidth="lg">
6775
<Grid container spacing={3}>
68-
<Grid item xs={12}>
69-
<Box pb={3}>
70-
<Typography variant="h3">Dashboard Overview</Typography>
71-
</Box>
72-
</Grid>
73-
7476
<Grid item xs={12} sm={6} md={4} lg={2.4}>
7577
<StatCard
7678
title="Total Users"
77-
value="0"
79+
value={stats.totalUsers}
7880
icon={<PersonIcon color="primary" />}
7981
/>
8082
</Grid>
81-
8283
<Grid item xs={12} sm={6} md={4} lg={2.4}>
8384
<StatCard
8485
title="Total Chats"
85-
value="0"
86+
value={stats.totalChats}
8687
icon={<ChatIcon color="primary" />}
8788
/>
8889
</Grid>
89-
9090
<Grid item xs={12} sm={6} md={4} lg={2.4}>
9191
<StatCard
9292
title="Total Projects"
93-
value="0"
93+
value={stats.totalProjects}
9494
icon={<FolderIcon color="primary" />}
9595
/>
9696
</Grid>
97-
9897
<Grid item xs={12} sm={6} md={4} lg={2.4}>
9998
<StatCard
10099
title="User Roles"
101-
value="0"
100+
value={stats.totalRoles}
102101
icon={<SecurityIcon color="primary" />}
103102
/>
104103
</Grid>
105-
106104
<Grid item xs={12} sm={6} md={4} lg={2.4}>
107105
<StatCard
108106
title="Menu Items"
109-
value="0"
107+
value={stats.totalMenus}
110108
icon={<MenuIcon color="primary" />}
111109
/>
112110
</Grid>

dashboard/src/graphql/request.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -428,20 +428,6 @@ export const DELETE_DASHBOARD_PROJECT = gql`
428428
deleteDashboardProject(id: $id)
429429
}
430430
`;
431-
export const GET_DASHBOARD_STATS = gql`
432-
query DashboardStats {
433-
dashboardStats {
434-
totalUsers
435-
totalChats
436-
totalProjects
437-
totalRoles
438-
totalMenus
439-
activeUsers
440-
activeProjects
441-
activeChats
442-
}
443-
}
444-
`;
445431

446432
// Query to get chat details
447433
export const GET_CHAT_DETAILS = gql`
@@ -466,3 +452,18 @@ export const GET_CHAT_DETAILS = gql`
466452
}
467453
}
468454
`;
455+
456+
export const GET_DASHBOARD_STATS = gql`
457+
query DashboardStats {
458+
dashboardStats {
459+
totalUsers
460+
activeUsers
461+
totalChats
462+
activeChats
463+
totalProjects
464+
activeProjects
465+
totalRoles
466+
totalMenus
467+
}
468+
}
469+
`;

frontend/src/graphql/schema.gql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ input CreateUserInput {
9393
username: String!
9494
}
9595

96+
type DashboardStats {
97+
activeChats: Int!
98+
activeProjects: Int!
99+
activeUsers: Int!
100+
totalChats: Int!
101+
totalMenus: Int!
102+
totalProjects: Int!
103+
totalRoles: Int!
104+
totalUsers: Int!
105+
}
106+
96107
"""Date custom scalar type"""
97108
scalar Date
98109

@@ -252,6 +263,7 @@ type Query {
252263
dashboardProjects(filter: ProjectFilterInput): [Project!]!
253264
dashboardRole(id: ID!): SystemRole!
254265
dashboardRoles: [SystemRole!]!
266+
dashboardStats: DashboardStats!
255267
dashboardUser(id: ID!): User!
256268
dashboardUsers(filter: UserFilterInput): [User!]!
257269
fetchPublicProjects(input: FetchPublicProjectsInputs!): [Project!]!

frontend/src/graphql/type.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ export type CreateUserInput = {
136136
username: Scalars['String']['input'];
137137
};
138138

139+
export type DashboardStats = {
140+
__typename: 'DashboardStats';
141+
activeChats: Scalars['Int']['output'];
142+
activeProjects: Scalars['Int']['output'];
143+
activeUsers: Scalars['Int']['output'];
144+
totalChats: Scalars['Int']['output'];
145+
totalMenus: Scalars['Int']['output'];
146+
totalProjects: Scalars['Int']['output'];
147+
totalRoles: Scalars['Int']['output'];
148+
totalUsers: Scalars['Int']['output'];
149+
};
150+
139151
export type EmailConfirmationResponse = {
140152
__typename: 'EmailConfirmationResponse';
141153
message: Scalars['String']['output'];
@@ -456,6 +468,7 @@ export type Query = {
456468
dashboardProjects: Array<Project>;
457469
dashboardRole: SystemRole;
458470
dashboardRoles: Array<SystemRole>;
471+
dashboardStats: DashboardStats;
459472
dashboardUser: User;
460473
dashboardUsers: Array<User>;
461474
fetchPublicProjects: Array<Project>;
@@ -787,6 +800,7 @@ export type ResolversTypes = ResolversObject<{
787800
CreateProjectInput: CreateProjectInput;
788801
CreateRoleInput: CreateRoleInput;
789802
CreateUserInput: CreateUserInput;
803+
DashboardStats: ResolverTypeWrapper<DashboardStats>;
790804
Date: ResolverTypeWrapper<Scalars['Date']['output']>;
791805
EmailConfirmationResponse: ResolverTypeWrapper<EmailConfirmationResponse>;
792806
FetchPublicProjectsInputs: FetchPublicProjectsInputs;
@@ -841,6 +855,7 @@ export type ResolversParentTypes = ResolversObject<{
841855
CreateProjectInput: CreateProjectInput;
842856
CreateRoleInput: CreateRoleInput;
843857
CreateUserInput: CreateUserInput;
858+
DashboardStats: DashboardStats;
844859
Date: Scalars['Date']['output'];
845860
EmailConfirmationResponse: EmailConfirmationResponse;
846861
FetchPublicProjectsInputs: FetchPublicProjectsInputs;
@@ -960,6 +975,22 @@ export type ChatCompletionDeltaTypeResolvers<
960975
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
961976
}>;
962977

978+
export type DashboardStatsResolvers<
979+
ContextType = any,
980+
ParentType extends
981+
ResolversParentTypes['DashboardStats'] = ResolversParentTypes['DashboardStats'],
982+
> = ResolversObject<{
983+
activeChats?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
984+
activeProjects?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
985+
activeUsers?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
986+
totalChats?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
987+
totalMenus?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
988+
totalProjects?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
989+
totalRoles?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
990+
totalUsers?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
991+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
992+
}>;
993+
963994
export interface DateScalarConfig
964995
extends GraphQLScalarTypeConfig<ResolversTypes['Date'], any> {
965996
name: 'Date';
@@ -1394,6 +1425,11 @@ export type QueryResolvers<
13941425
ParentType,
13951426
ContextType
13961427
>;
1428+
dashboardStats?: Resolver<
1429+
ResolversTypes['DashboardStats'],
1430+
ParentType,
1431+
ContextType
1432+
>;
13971433
dashboardUser?: Resolver<
13981434
ResolversTypes['User'],
13991435
ParentType,
@@ -1607,6 +1643,7 @@ export type Resolvers<ContextType = any> = ResolversObject<{
16071643
ChatCompletionChoiceType?: ChatCompletionChoiceTypeResolvers<ContextType>;
16081644
ChatCompletionChunkType?: ChatCompletionChunkTypeResolvers<ContextType>;
16091645
ChatCompletionDeltaType?: ChatCompletionDeltaTypeResolvers<ContextType>;
1646+
DashboardStats?: DashboardStatsResolvers<ContextType>;
16101647
Date?: GraphQLScalarType;
16111648
EmailConfirmationResponse?: EmailConfirmationResponseResolvers<ContextType>;
16121649
LoginResponse?: LoginResponseResolvers<ContextType>;

0 commit comments

Comments
 (0)