Skip to content

Commit 0928077

Browse files
igorihimbazweaimedivin
authored andcommitted
ft implement superadmin dashboard
1 parent ba775e7 commit 0928077

File tree

8 files changed

+363
-77
lines changed

8 files changed

+363
-77
lines changed

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ import DocumentationResolvers from './resolvers/DocumentationResolvers'
3535
import attendanceResolver from './resolvers/attendance.resolvers'
3636
import Sessionresolvers from './resolvers/session.resolver'
3737
import invitationResolvers from './resolvers/invitation.resolvers'
38+
import organizationResolvers from './resolvers/organization.resolvers'
3839
import schemas from './schema/index'
3940
import cohortSchema from './schema/cohort.schema'
4041
import programSchema from './schema/program.schema'
4142
import coordinatorSchema from './schema/coordinator.schema'
4243
import phaseSchema from './schema/phase.schema'
4344
import ticketSchema from './schema/ticket.shema'
4445
import notificationSchema from './schema/notification.schema'
46+
import organizationSchema from './schema/organization.schema'
4547

4648
import statisticsSchema from './schema/invitationStatics.schema'
4749
import StatisticsResolvers from './resolvers/invitationStatics.resolvers'
@@ -66,6 +68,7 @@ export const typeDefs = mergeTypeDefs([
6668
notificationSchema,
6769
statisticsSchema,
6870
eventSchema,
71+
organizationSchema
6972
])
7073

7174
export const resolvers = mergeResolvers([
@@ -85,7 +88,7 @@ export const resolvers = mergeResolvers([
8588
DocumentationResolvers,
8689
attendanceResolver,
8790
Sessionresolvers,
88-
91+
organizationResolvers,
8992
StatisticsResolvers,
9093

9194
invitationResolvers,

src/models/organization.model.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ const organizationSchema = new Schema({
2525
enum: ['active', 'pending', 'rejected'],
2626
default: 'active',
2727
},
28+
logins: [
29+
{
30+
date: {
31+
type: Date,
32+
default: Date.now,
33+
},
34+
loginsCount: {
35+
type: Number,
36+
default: 0,
37+
},
38+
recentLocation: {
39+
type: String
40+
}
41+
},
42+
],
43+
}, {
44+
timestamps: true,
2845
})
2946

3047
const Organization = model('Organization', organizationSchema)

src/models/user.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface UserInterface {
2222
emailNotifications: boolean;
2323
profile?: mongoose.Types.ObjectId;
2424
ratings?: mongoose.Types.ObjectId[];
25+
createdAt: Date;
26+
updatedAt: Date;
2527
}
2628

2729
export enum RoleOfUser {
@@ -96,6 +98,7 @@ const userSchema = new Schema(
9698
},
9799

98100
{
101+
timestamps: true,
99102
toJSON: { virtuals: true },
100103
toObject: { virtuals: true },
101104
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/* eslint-disable prefer-const */
2+
import { format } from 'date-fns'
3+
import {
4+
checkUserLoggedIn,
5+
} from '../helpers/user.helpers'
6+
import { Organization } from '../models/organization.model'
7+
import { RoleOfUser, User, UserInterface } from '../models/user'
8+
import { Context } from './../context'
9+
10+
export type OrganizationType = InstanceType<typeof Organization>
11+
export type UserType = InstanceType<typeof User>
12+
13+
interface RegistrationDataStatsInterface {
14+
month:
15+
| 'jan'
16+
| 'feb'
17+
| 'mar'
18+
| 'apr'
19+
| 'may'
20+
| 'jun'
21+
| 'jul'
22+
| 'aug'
23+
| 'sep'
24+
| 'oct'
25+
| 'nov'
26+
| 'dec'
27+
| null;
28+
users: number | null;
29+
organizations: number | null;
30+
}
31+
32+
interface RegistrationDataInterface {
33+
year: number;
34+
stats: RegistrationDataStatsInterface[];
35+
}
36+
const months = [
37+
'jan',
38+
'feb',
39+
'mar',
40+
'apr',
41+
'may',
42+
'jun',
43+
'jul',
44+
'aug',
45+
'sep',
46+
'oct',
47+
'nov',
48+
'dec',
49+
];
50+
51+
const calcYearRegData = (usersOrgsData: any, yearsRegData: RegistrationDataInterface[], isUsers: boolean) => {
52+
for (const userOrg of usersOrgsData) {
53+
if (userOrg.createdAt) {
54+
const initialMonthsData: RegistrationDataStatsInterface[] = months.map((month) => ({
55+
month: month as RegistrationDataStatsInterface['month'],
56+
users: null,
57+
organizations: null,
58+
}));
59+
60+
const createdAt = new Date(userOrg.createdAt)
61+
const regYear = createdAt.getFullYear();
62+
const regMonthNum = createdAt.getMonth();
63+
const regMonth = format(createdAt, 'MMM').toLowerCase() as RegistrationDataStatsInterface['month'];
64+
65+
const yearIndex = yearsRegData.findIndex(year => year.year === regYear);
66+
67+
if (yearIndex !== -1) {
68+
const statMonthIndex = yearsRegData[yearIndex].stats.findIndex((month: { month: string | null }) => month.month === regMonth);
69+
if (statMonthIndex !== -1) {
70+
const tempValue = isUsers ? yearsRegData[yearIndex].stats[statMonthIndex].users : yearsRegData[yearIndex].stats[statMonthIndex].organizations;
71+
if (isUsers) {
72+
yearsRegData[yearIndex].stats[statMonthIndex].users = (tempValue && tempValue > 0) ? tempValue + 1 : 1;
73+
} else {
74+
yearsRegData[yearIndex].stats[statMonthIndex].organizations = (tempValue && tempValue > 0) ? tempValue + 1 : 1
75+
}
76+
} else {
77+
const statMonthIndex2 = initialMonthsData.findIndex(month => month.month === regMonth);
78+
initialMonthsData[statMonthIndex2] = { month: regMonth, users: isUsers ? 1 : null, organizations: !isUsers ? 1 : null }
79+
initialMonthsData.forEach(monthData => {
80+
const monthIndex = months.findIndex((month) => monthData.month === month);
81+
if (monthIndex !== -1 && monthIndex < regMonthNum) {
82+
monthData.users = (monthData.users != null && monthData.users >= 0) ? monthData.users : 0
83+
monthData.organizations = (monthData.organizations != null && monthData.organizations >= 0) ? monthData.organizations : 0
84+
}
85+
})
86+
yearsRegData[yearIndex].stats = initialMonthsData;
87+
}
88+
} else {
89+
const statMonthIndex = initialMonthsData.findIndex(month => month.month === regMonth);
90+
if (statMonthIndex !== -1) {
91+
initialMonthsData[statMonthIndex] = { month: regMonth, users: isUsers ? 1 : null, organizations: !isUsers ? 1 : null }
92+
initialMonthsData.forEach(monthData => {
93+
const monthIndex = months.findIndex((month) => monthData.month === month);
94+
if (monthIndex !== -1 && monthIndex < regMonthNum) {
95+
monthData.users = 0
96+
monthData.organizations = 0
97+
}
98+
})
99+
yearsRegData.push({
100+
year: regYear,
101+
stats: [...initialMonthsData],
102+
});
103+
}
104+
}
105+
}
106+
}
107+
return yearsRegData
108+
};
109+
110+
const resolvers: any = {
111+
Query: {
112+
async getAllOrgUsers(_: any, __: any, context: Context) {
113+
; (await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]);
114+
115+
const totalUsers = await User.countDocuments();
116+
const allOrgUsers = {
117+
totalUsers: totalUsers,
118+
organizations: [] as any[]
119+
}
120+
121+
const orgs = await Organization.find({ status: 'active' });
122+
123+
for (const org of orgs) {
124+
const orgUsers = await User.find({ organizations: org.name });
125+
126+
const startOfMonth = new Date();
127+
startOfMonth.setDate(1);
128+
startOfMonth.setHours(0, 0, 0, 0);
129+
130+
const endOfMonth = new Date();
131+
endOfMonth.setMonth(startOfMonth.getMonth() + 1);
132+
endOfMonth.setDate(0);
133+
endOfMonth.setHours(23, 59, 59, 999);
134+
135+
const monthlyRegistrations = await User.countDocuments({
136+
createdAt: { $gte: startOfMonth, $lte: endOfMonth },
137+
organizations: org.name
138+
});
139+
140+
const recentLogin = org.logins.find((count) => {
141+
return count.date.toISOString().split('T')[0] === new Date().toISOString().split('T')[0]
142+
})
143+
const result = {
144+
organization: org,
145+
members: orgUsers,
146+
loginsCount: recentLogin?.loginsCount || 0,
147+
recentLocation: recentLogin?.recentLocation,
148+
monthPercentage: orgUsers.length > 0 ? (monthlyRegistrations / orgUsers.length) * 100 : 0
149+
}
150+
allOrgUsers.organizations.push(result);
151+
}
152+
153+
return allOrgUsers
154+
},
155+
async getRegistrationStats(_: any, __: any, context: Context) {
156+
; (await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]);
157+
158+
const months = [
159+
'jan',
160+
'feb',
161+
'mar',
162+
'apr',
163+
'may',
164+
'jun',
165+
'jul',
166+
'aug',
167+
'sep',
168+
'oct',
169+
'nov',
170+
'dec',
171+
];
172+
173+
const monthsData: any = [];
174+
let yearsRegData: RegistrationDataInterface[] = [{
175+
year: new Date().getFullYear(),
176+
stats: monthsData
177+
}];
178+
179+
const users = await User.find();
180+
yearsRegData = calcYearRegData(users, yearsRegData, true);
181+
182+
const organizations = await Organization.find();
183+
yearsRegData = calcYearRegData(organizations, yearsRegData, false);
184+
185+
return yearsRegData
186+
}
187+
},
188+
}
189+
export default resolvers

src/resolvers/resolver.ts

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,35 +57,35 @@ const resolvers = {
5757

5858
return { token, user: newUser }
5959
},
60-
async loginUser(_: any, { loginInput: { email, password } }: any) {
61-
try {
62-
const user: any = await User.findOne({ email: email })
63-
if (!user) {
64-
throw new Error('User not found')
65-
}
60+
// async loginUser(_: any, { loginInput: { email, password } }: any) {
61+
// try {
62+
// const user: any = await User.findOne({ email: email })
63+
// if (!user) {
64+
// throw new Error('User not found')
65+
// }
6666

67-
if (await user?.checkPass(password)) {
68-
const token = jwt.sign(
69-
{ userId: user._id, role: user._doc?.role || 'user' },
70-
SECRET,
71-
{
72-
expiresIn: '2h',
73-
}
74-
)
67+
// if (await user?.checkPass(password)) {
68+
// const token = jwt.sign(
69+
// { userId: user._id, role: user._doc?.role || 'user' },
70+
// SECRET,
71+
// {
72+
// expiresIn: '2h',
73+
// }
74+
// )
7575

76-
const data = {
77-
token: token,
78-
user: user.toJSON(),
79-
}
80-
return data
81-
} else {
82-
throw new Error('Invalid credential')
83-
}
84-
} catch (error) {
85-
console.error('Login error:', error)
86-
throw new Error('Login failed. Please try again.')
87-
}
88-
},
76+
// const data = {
77+
// token: token,
78+
// user: user.toJSON(),
79+
// }
80+
// return data
81+
// } else {
82+
// throw new Error('Invalid credential')
83+
// }
84+
// } catch (error) {
85+
// console.error('Login error:', error)
86+
// throw new Error('Login failed. Please try again.')
87+
// }
88+
// },
8989
async createProfile(_: any, args: any, context: { userId: any }) {
9090
if (!context.userId) throw new Error('Unauthorized')
9191
if (!mongoose.isValidObjectId(context.userId))

src/resolvers/ticket.resolver.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -191,39 +191,39 @@ const resolvers = {
191191
id: ticket._id.toString(),
192192
user: ticket.user
193193
? {
194-
...ticket.user.toObject(),
195-
id: ticket.user._id.toString(),
196-
team: ticket.user.team
197-
? {
198-
id: ticket.user.team._id.toString(),
199-
name: ticket.user.team.name,
200-
}
201-
: null,
202-
cohort: ticket.user.cohort
203-
? {
204-
id: ticket.user.cohort._id.toString(),
205-
name: ticket.user.cohort.name,
206-
}
207-
: null,
208-
}
194+
...ticket.user.toObject(),
195+
id: ticket.user._id.toString(),
196+
team: ticket.user.team
197+
? {
198+
id: ticket.user.team._id.toString(),
199+
name: ticket.user.team.name,
200+
}
201+
: null,
202+
cohort: ticket.user.cohort
203+
? {
204+
id: ticket.user.cohort._id.toString(),
205+
name: ticket.user.cohort.name,
206+
}
207+
: null,
208+
}
209209
: null,
210210
assignee: ticket.assignee
211211
? {
212-
...ticket.assignee.toObject(),
213-
id: ticket.assignee._id.toString(),
214-
team: ticket.assignee.team
215-
? {
216-
id: ticket.assignee.team._id.toString(),
217-
name: ticket.assignee.team.name,
218-
}
219-
: null,
220-
cohort: ticket.assignee.cohort
221-
? {
222-
id: ticket.assignee.cohort._id.toString(),
223-
name: ticket.assignee.cohort.name,
224-
}
225-
: null,
226-
}
212+
...ticket.assignee.toObject(),
213+
id: ticket.assignee._id.toString(),
214+
team: ticket.assignee.team
215+
? {
216+
id: ticket.assignee.team._id.toString(),
217+
name: ticket.assignee.team.name,
218+
}
219+
: null,
220+
cohort: ticket.assignee.cohort
221+
? {
222+
id: ticket.assignee.cohort._id.toString(),
223+
name: ticket.assignee.cohort.name,
224+
}
225+
: null,
226+
}
227227
: null,
228228
}))
229229
} catch (error: any) {

0 commit comments

Comments
 (0)