Skip to content

Commit 9a95a43

Browse files
committed
ui page for admin or officers
1 parent 6acd302 commit 9a95a43

File tree

4 files changed

+261
-12
lines changed

4 files changed

+261
-12
lines changed

api/main_endpoints/models/PermissionRequest.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ const PermissionRequestSchema = new Schema(
1414
enum: Object.values(PermissionRequestTypes),
1515
required: true,
1616
},
17+
approved: {
18+
type: Boolean,
19+
default: false,
20+
},
1721
deletedAt: {
1822
type: Date,
1923
default: null,
@@ -23,7 +27,7 @@ const PermissionRequestSchema = new Schema(
2327
);
2428

2529
// Compound unique index prevents duplicate active requests per user+type
26-
PermissionRequestSchema.index({ userId: 1, type: 1 }, { unique: true, partialFilterExpression: { deletedAt: null }});
30+
PermissionRequestSchema.index({ userId: 1, type: 1 }, { unique: true, partialFilterExpression: { deletedAt: null, approved: false }});
2731

2832
module.exports = mongoose.model('PermissionRequest', PermissionRequestSchema);
2933

api/main_endpoints/routes/PermissionRequest.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,20 @@ router.get('/get', async (req, res) => {
3939
try {
4040
const query = { deletedAt: null };
4141

42-
// If theres no userId, return all for officers and admins
42+
// If theres no userId, return all for officers and admins (only pending requests)
4343
if (!queryUserId) {
4444
if (!isOfficer) {
4545
return res.sendStatus(UNAUTHORIZED);
4646
}
47+
// For admin view, only show pending (non-approved) requests
48+
query.approved = false;
4749
} else {
4850
// If there is a userId, check their perms
4951
if (!isOfficer && queryUserId !== decoded.token._id.toString()) {
5052
return res.sendStatus(FORBIDDEN);
5153
}
5254
query.userId = queryUserId;
55+
// For member's own request, return it regardless of approval status
5356
}
5457

5558
// If there is a type, filter by it
@@ -68,6 +71,36 @@ router.get('/get', async (req, res) => {
6871
}
6972
});
7073

74+
router.post('/approve', async (req, res) => {
75+
const decoded = await decodeToken(req, membershipState.OFFICER);
76+
if (decoded.status !== OK) return res.sendStatus(decoded.status);
77+
78+
const { type, _id } = req.body;
79+
if (!type || !Object.keys(PermissionRequestTypes).includes(type)) {
80+
return res.status(BAD_REQUEST).send({ error: 'Invalid type' });
81+
}
82+
83+
if (!_id) {
84+
return res.status(BAD_REQUEST).send({ error: '_id is required' });
85+
}
86+
87+
try {
88+
const request = await PermissionRequest.findOne({
89+
_id,
90+
type,
91+
deletedAt: null,
92+
});
93+
94+
if (!request) return res.sendStatus(NOT_FOUND);
95+
request.approved = true;
96+
await request.save();
97+
res.sendStatus(OK);
98+
} catch (error) {
99+
logger.error('Failed to approve permission request:', error);
100+
res.sendStatus(SERVER_ERROR);
101+
}
102+
});
103+
71104
router.post('/delete', async (req, res) => {
72105
const decoded = await decodeToken(req, membershipState.MEMBER);
73106
if (decoded.status !== OK) return res.sendStatus(decoded.status);

src/APIFunctions/PermissionRequest.js

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { ApiResponse } from './ApiResponses';
22
import { BASE_API_URL } from '../Enums';
33

4-
export async function getPermissionRequest(type, token) {
4+
export async function getPermissionRequest(type, userId, token) {
55
const status = new ApiResponse();
66
const url = new URL('/api/PermissionRequest/get', BASE_API_URL);
77
url.searchParams.append('type', type);
8+
if (userId) {
9+
url.searchParams.append('userId', userId);
10+
}
811

912
try {
1013
const res = await fetch(url.toString(), {
@@ -15,11 +18,11 @@ export async function getPermissionRequest(type, token) {
1518

1619
if (res.ok) {
1720
const data = await res.json();
18-
status.responseData = data;
19-
} else if (res.status === 404) {
20-
status.responseData = null;
21+
// API returns an array, return first item or null
22+
status.responseData = Array.isArray(data) && data.length > 0 ? data[0] : null;
2123
} else {
2224
status.error = true;
25+
status.responseData = null;
2326
}
2427
} catch (err) {
2528
status.responseData = err;
@@ -43,6 +46,38 @@ export async function createPermissionRequest(type, token) {
4346
body: JSON.stringify({ type }),
4447
});
4548

49+
if (res.ok) {
50+
// API returns 200 with no body, so we just mark success
51+
status.responseData = true;
52+
} else if (res.status === 409) {
53+
// CONFLICT - duplicate request
54+
status.error = true;
55+
status.responseData = 'Request already exists';
56+
} else {
57+
status.error = true;
58+
}
59+
} catch (err) {
60+
status.responseData = err;
61+
status.error = true;
62+
}
63+
64+
return status;
65+
}
66+
67+
export async function getAllPermissionRequests(type, token) {
68+
const status = new ApiResponse();
69+
const url = new URL('/api/PermissionRequest/get', BASE_API_URL);
70+
if (type) {
71+
url.searchParams.append('type', type);
72+
}
73+
74+
try {
75+
const res = await fetch(url.toString(), {
76+
headers: {
77+
Authorization: `Bearer ${token}`,
78+
},
79+
});
80+
4681
if (res.ok) {
4782
const data = await res.json();
4883
status.responseData = data;
@@ -57,3 +92,57 @@ export async function createPermissionRequest(type, token) {
5792
return status;
5893
}
5994

95+
export async function approvePermissionRequest(type, id, token) {
96+
const status = new ApiResponse();
97+
const url = new URL('/api/PermissionRequest/approve', BASE_API_URL);
98+
99+
try {
100+
const res = await fetch(url.toString(), {
101+
method: 'POST',
102+
headers: {
103+
'Content-Type': 'application/json',
104+
Authorization: `Bearer ${token}`,
105+
},
106+
body: JSON.stringify({ type, _id: id }),
107+
});
108+
109+
if (res.ok) {
110+
status.responseData = true;
111+
} else {
112+
status.error = true;
113+
}
114+
} catch (err) {
115+
status.responseData = err;
116+
status.error = true;
117+
}
118+
119+
return status;
120+
}
121+
122+
export async function deletePermissionRequest(type, id, token) {
123+
const status = new ApiResponse();
124+
const url = new URL('/api/PermissionRequest/delete', BASE_API_URL);
125+
126+
try {
127+
const res = await fetch(url.toString(), {
128+
method: 'POST',
129+
headers: {
130+
'Content-Type': 'application/json',
131+
Authorization: `Bearer ${token}`,
132+
},
133+
body: JSON.stringify({ type, _id: id }),
134+
});
135+
136+
if (res.ok) {
137+
status.responseData = true;
138+
} else {
139+
status.error = true;
140+
}
141+
} catch (err) {
142+
status.responseData = err;
143+
status.error = true;
144+
}
145+
146+
return status;
147+
}
148+

0 commit comments

Comments
 (0)