Skip to content

Commit 2b0638a

Browse files
Merge pull request #5 from StreetSupport/feature/3011-build-user-management-interface
3011 - Update logic regarding VolunteerRole
2 parents 370df10 + 44462f1 commit 2b0638a

File tree

5 files changed

+103
-93
lines changed

5 files changed

+103
-93
lines changed

src/app/users/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { getAvailableLocations } from '@/utils/locationUtils';
2222
export default function UsersPage() {
2323
// Check authorization FIRST before any other logic
2424
const { isChecking, isAuthorized } = useAuthorization({
25-
allowedRoles: [ROLES.SUPER_ADMIN, ROLES.CITY_ADMIN, ROLES.VOLUNTEER_ADMIN],
25+
allowedRoles: [ROLES.SUPER_ADMIN, ROLES.CITY_ADMIN],
2626
requiredPage: '/users',
2727
autoRedirect: true
2828
});

src/components/users/AddRoleModal.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export default function AddRoleModal({ isOpen, onClose, onAdd, currentRoles }: A
3232

3333
const userAuthClaims = (session?.user?.authClaims || { roles: [], specificClaims: [] }) as UserAuthClaims;
3434
const isSuperAdmin = userAuthClaims.roles.includes(ROLES.SUPER_ADMIN);
35-
const isVolunteerAdmin = userAuthClaims.roles.includes(ROLES.VOLUNTEER_ADMIN);
3635
const isCityAdmin = userAuthClaims.roles.includes(ROLES.CITY_ADMIN) || userAuthClaims.specificClaims.includes(ROLE_PREFIXES.CITY_ADMIN_FOR);
3736

3837
useEffect(() => {
@@ -192,7 +191,7 @@ export default function AddRoleModal({ isOpen, onClose, onAdd, currentRoles }: A
192191
)}
193192

194193
{/* Location Administrator */}
195-
{(isSuperAdmin || isVolunteerAdmin || isCityAdmin) && (
194+
{(isSuperAdmin || isCityAdmin) && (
196195
<label className="flex items-center p-3 hover:bg-brand-i rounded-md cursor-pointer transition-colors">
197196
<input
198197
type="radio"
@@ -226,7 +225,7 @@ export default function AddRoleModal({ isOpen, onClose, onAdd, currentRoles }: A
226225
</label>
227226

228227
{/* Volunteer Administrator */}
229-
{(isSuperAdmin || isVolunteerAdmin) && (
228+
{(isSuperAdmin) && (
230229
<label className="flex items-center p-3 hover:bg-brand-i rounded-md cursor-pointer transition-colors">
231230
<input
232231
type="radio"
@@ -244,7 +243,7 @@ export default function AddRoleModal({ isOpen, onClose, onAdd, currentRoles }: A
244243
)}
245244

246245
{/* SWEP Administrator */}
247-
{(isSuperAdmin || isVolunteerAdmin || isCityAdmin) && (
246+
{(isSuperAdmin || isCityAdmin) && (
248247
<label className="flex items-center p-3 hover:bg-brand-i rounded-md cursor-pointer transition-colors">
249248
<input
250249
type="radio"

src/components/users/EditUserModal.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export default function EditUserModal({
3737
const userAuthClaims = session?.user?.authClaims;
3838
const currentUserLocations = userAuthClaims ? getUserLocationSlugs(userAuthClaims) : null;
3939
const isSuperAdmin = userAuthClaims?.roles.includes(ROLES.SUPER_ADMIN) || false;
40-
const isVolunteerAdmin = userAuthClaims?.roles.includes(ROLES.VOLUNTEER_ADMIN) || false;
4140

4241
useEffect(() => {
4342
if (user && isOpen) {
@@ -85,8 +84,8 @@ export default function EditUserModal({
8584
* CityAdmin can only manage roles from their own locations
8685
*/
8786
const canManageRole = (role: RoleDisplay): { canManage: boolean; reason?: string } => {
88-
// SuperAdmin and VolunteerAdmin can manage all roles
89-
if (isSuperAdmin || isVolunteerAdmin) {
87+
// SuperAdmin can manage all roles
88+
if (isSuperAdmin) {
9089
return { canManage: true };
9190
}
9291

src/constants/roles.ts

Lines changed: 93 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -103,48 +103,52 @@ export function isOrgSpecificRole(role: string): boolean {
103103
return role.startsWith(ROLE_PREFIXES.ADMIN_FOR);
104104
}
105105

106+
// TODO: remove it if we don't need it
106107
/**
107108
* Extract the location/org slug from a specific role
108109
* @example extractRoleSlug('CityAdminFor:birmingham') => 'birmingham'
109110
*/
110-
export function extractRoleSlug(role: string): string | null {
111-
if (isLocationSpecificRole(role)) {
112-
if (role.startsWith(ROLE_PREFIXES.CITY_ADMIN_FOR)) {
113-
return role.substring(ROLE_PREFIXES.CITY_ADMIN_FOR.length);
114-
}
115-
if (role.startsWith(ROLE_PREFIXES.SWEP_ADMIN_FOR)) {
116-
return role.substring(ROLE_PREFIXES.SWEP_ADMIN_FOR.length);
117-
}
118-
}
119-
if (isOrgSpecificRole(role)) {
120-
return role.substring(ROLE_PREFIXES.ADMIN_FOR.length);
121-
}
122-
return null;
123-
}
111+
// export function extractRoleSlug(role: string): string | null {
112+
// if (isLocationSpecificRole(role)) {
113+
// if (role.startsWith(ROLE_PREFIXES.CITY_ADMIN_FOR)) {
114+
// return role.substring(ROLE_PREFIXES.CITY_ADMIN_FOR.length);
115+
// }
116+
// if (role.startsWith(ROLE_PREFIXES.SWEP_ADMIN_FOR)) {
117+
// return role.substring(ROLE_PREFIXES.SWEP_ADMIN_FOR.length);
118+
// }
119+
// }
120+
// if (isOrgSpecificRole(role)) {
121+
// return role.substring(ROLE_PREFIXES.ADMIN_FOR.length);
122+
// }
123+
// return null;
124+
// }
124125

126+
// TODO: remove it if we don't need it
125127
/**
126128
* Create a location-specific city admin role
127129
* @example createCityAdminRole('birmingham') => 'CityAdminFor:birmingham'
128130
*/
129-
export function createCityAdminRole(locationSlug: string): string {
130-
return `${ROLE_PREFIXES.CITY_ADMIN_FOR}${locationSlug}`;
131-
}
131+
// export function createCityAdminRole(locationSlug: string): string {
132+
// return `${ROLE_PREFIXES.CITY_ADMIN_FOR}${locationSlug}`;
133+
// }
132134

135+
// TODO: remove it if we don't need it
133136
/**
134137
* Create a location-specific SWEP admin role
135138
* @example createSwepAdminRole('birmingham') => 'SwepAdminFor:birmingham'
136139
*/
137-
export function createSwepAdminRole(locationSlug: string): string {
138-
return `${ROLE_PREFIXES.SWEP_ADMIN_FOR}${locationSlug}`;
139-
}
140+
// export function createSwepAdminRole(locationSlug: string): string {
141+
// return `${ROLE_PREFIXES.SWEP_ADMIN_FOR}${locationSlug}`;
142+
// }
140143

144+
// TODO: remove it if we don't need it
141145
/**
142146
* Create an org-specific admin role
143147
* @example createOrgAdminRole('org-slug') => 'AdminFor:org-slug'
144148
*/
145-
export function createOrgAdminRole(orgSlug: string): string {
146-
return `${ROLE_PREFIXES.ADMIN_FOR}${orgSlug}`;
147-
}
149+
// export function createOrgAdminRole(orgSlug: string): string {
150+
// return `${ROLE_PREFIXES.ADMIN_FOR}${orgSlug}`;
151+
// }
148152

149153
/**
150154
* Validate if a role string matches the expected format
@@ -165,26 +169,29 @@ export function isValidRole(role: string): boolean {
165169
return rolePatterns.some(pattern => pattern.test(role));
166170
}
167171

172+
// TODO: remove it if we don't need it
168173
/**
169174
* Get all roles from a user's auth claims (base roles only)
170175
*/
171-
export function getBaseRoles(authClaims: string[]): BaseRole[] {
172-
return authClaims.filter(isBaseRole);
173-
}
176+
// export function getBaseRoles(authClaims: string[]): BaseRole[] {
177+
// return authClaims.filter(isBaseRole);
178+
// }
174179

180+
// TODO: remove it if we don't need it
175181
/**
176182
* Get all location-specific roles from auth claims
177183
*/
178184
export function getLocationSpecificRoles(authClaims: string[]): string[] {
179185
return authClaims.filter(isLocationSpecificRole);
180186
}
181187

188+
// TODO: remove it if we don't need it
182189
/**
183190
* Get all org-specific roles from auth claims
184191
*/
185-
export function getOrgSpecificRoles(authClaims: string[]): string[] {
186-
return authClaims.filter(isOrgSpecificRole);
187-
}
192+
// export function getOrgSpecificRoles(authClaims: string[]): string[] {
193+
// return authClaims.filter(isOrgSpecificRole);
194+
// }
188195

189196
/**
190197
* Check if user has a specific base role
@@ -200,71 +207,74 @@ export function isSuperAdmin(authClaims: string[]): boolean {
200207
return hasRole(authClaims, ROLES.SUPER_ADMIN);
201208
}
202209

210+
// TODO: remove it if we don't need it
203211
/**
204212
* Check if user has access to a specific location
205213
*/
206-
export function hasLocationAccess(authClaims: string[], locationSlug: string): boolean {
207-
// SuperAdmin has access to all locations
208-
if (isSuperAdmin(authClaims)) {
209-
return true;
210-
}
214+
// export function hasLocationAccess(authClaims: string[], locationSlug: string): boolean {
215+
// // SuperAdmin has access to all locations
216+
// if (isSuperAdmin(authClaims)) {
217+
// return true;
218+
// }
211219

212-
// Check for general CityAdmin role
213-
if (hasRole(authClaims, ROLES.CITY_ADMIN)) {
214-
return true;
215-
}
220+
// // Check for general CityAdmin role
221+
// if (hasRole(authClaims, ROLES.CITY_ADMIN)) {
222+
// return true;
223+
// }
216224

217-
// Check for location-specific roles
218-
const cityAdminRole = createCityAdminRole(locationSlug);
219-
const swepAdminRole = createSwepAdminRole(locationSlug);
225+
// // Check for location-specific roles
226+
// const cityAdminRole = createCityAdminRole(locationSlug);
227+
// const swepAdminRole = createSwepAdminRole(locationSlug);
220228

221-
return authClaims.includes(cityAdminRole) || authClaims.includes(swepAdminRole);
222-
}
229+
// return authClaims.includes(cityAdminRole) || authClaims.includes(swepAdminRole);
230+
// }
223231

232+
// TODO: remove it if we don't need it
224233
/**
225234
* Check if user has access to a specific organisation
226235
*/
227-
export function hasOrgAccess(authClaims: string[], orgSlug: string): boolean {
228-
// SuperAdmin has access to all organisations
229-
if (isSuperAdmin(authClaims)) {
230-
return true;
231-
}
236+
// export function hasOrgAccess(authClaims: string[], orgSlug: string): boolean {
237+
// // SuperAdmin has access to all organisations
238+
// if (isSuperAdmin(authClaims)) {
239+
// return true;
240+
// }
232241

233-
// Check for general OrgAdmin role
234-
if (hasRole(authClaims, ROLES.ORG_ADMIN)) {
235-
return true;
236-
}
242+
// // Check for general OrgAdmin role
243+
// if (hasRole(authClaims, ROLES.ORG_ADMIN)) {
244+
// return true;
245+
// }
237246

238-
// Check for org-specific role
239-
const orgAdminRole = createOrgAdminRole(orgSlug);
240-
return authClaims.includes(orgAdminRole);
241-
}
247+
// // Check for org-specific role
248+
// const orgAdminRole = createOrgAdminRole(orgSlug);
249+
// return authClaims.includes(orgAdminRole);
250+
// }
242251

252+
// TODO: remove it if we don't need it
243253
/**
244254
* Format role for display in UI
245255
*/
246-
export function formatRoleForDisplay(role: string): string {
247-
if (isBaseRole(role)) {
248-
return ROLE_LABELS[role];
249-
}
256+
// export function formatRoleForDisplay(role: string): string {
257+
// if (isBaseRole(role)) {
258+
// return ROLE_LABELS[role];
259+
// }
250260

251-
const slug = extractRoleSlug(role);
252-
if (!slug) {
253-
return role;
254-
}
261+
// const slug = extractRoleSlug(role);
262+
// if (!slug) {
263+
// return role;
264+
// }
255265

256-
if (role.startsWith(ROLE_PREFIXES.CITY_ADMIN_FOR)) {
257-
return `City Admin for ${slug}`;
258-
}
259-
if (role.startsWith(ROLE_PREFIXES.SWEP_ADMIN_FOR)) {
260-
return `SWEP Admin for ${slug}`;
261-
}
262-
if (role.startsWith(ROLE_PREFIXES.ADMIN_FOR)) {
263-
return `Admin for ${slug}`;
264-
}
266+
// if (role.startsWith(ROLE_PREFIXES.CITY_ADMIN_FOR)) {
267+
// return `City Admin for ${slug}`;
268+
// }
269+
// if (role.startsWith(ROLE_PREFIXES.SWEP_ADMIN_FOR)) {
270+
// return `SWEP Admin for ${slug}`;
271+
// }
272+
// if (role.startsWith(ROLE_PREFIXES.ADMIN_FOR)) {
273+
// return `Admin for ${slug}`;
274+
// }
265275

266-
return role;
267-
}
276+
// return role;
277+
// }
268278

269279
/**
270280
* Get role options for dropdown/select components
@@ -287,13 +297,14 @@ export function getRoleOptions() {
287297
*/
288298
export const ROLE_VALIDATION_PATTERN = /^(SuperAdmin|CityAdmin|CityAdminFor:.+|VolunteerAdmin|SwepAdmin|SwepAdminFor:.+|OrgAdmin|AdminFor:.+)$/;
289299

300+
// TODO: remove it if we don't need it
290301
/**
291302
* Validate an array of roles
292303
*/
293-
export function validateRoles(roles: string[]): { valid: boolean; invalidRoles: string[] } {
294-
const invalidRoles = roles.filter(role => !isValidRole(role));
295-
return {
296-
valid: invalidRoles.length === 0,
297-
invalidRoles,
298-
};
299-
}
304+
// export function validateRoles(roles: string[]): { valid: boolean; invalidRoles: string[] } {
305+
// const invalidRoles = roles.filter(role => !isValidRole(role));
306+
// return {
307+
// valid: invalidRoles.length === 0,
308+
// invalidRoles,
309+
// };
310+
// }

src/types/auth.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,22 @@ export const ROLE_PERMISSIONS: Record<UserRole, RolePermissions> = {
3939
]
4040
},
4141
[ROLES.VOLUNTEER_ADMIN]: {
42-
pages: ['/cities', '/organisations', '/advice', '/banners', '/swep-banners', '/users', '/resources'],
42+
pages: ['/cities', '/organisations', '/advice', '/banners', '/swep-banners', '/resources'],
4343
apiEndpoints: [
44-
{ path: '/api/cities', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
44+
{ path: '/api/cities', methods: [HTTP_METHODS.GET] },
4545
{ path: '/api/service-providers', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
4646
{ path: '/api/services', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
4747
{ path: '/api/faqs', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
4848
{ path: '/api/banners', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
4949
{ path: '/api/swep-banners', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
5050
{ path: '/api/resources', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] },
51-
{ path: '/api/users', methods: [HTTP_METHODS.GET, HTTP_METHODS.POST, HTTP_METHODS.PUT, HTTP_METHODS.PATCH] }
51+
{ path: '/api/users', methods: [HTTP_METHODS.POST] }
5252
]
5353
},
5454
[ROLES.ORG_ADMIN]: {
5555
pages: ['/organisations'],
5656
apiEndpoints: [
57+
{ path: '/api/cities', methods: [HTTP_METHODS.GET] },
5758
{ path: '/api/service-providers', methods: ['*'] },
5859
{ path: '/api/services', methods: ['*'] },
5960
{ path: '/api/users', methods: [HTTP_METHODS.POST] },

0 commit comments

Comments
 (0)