Skip to content

Commit 6225c45

Browse files
committed
enable edit profile support
1 parent 968c410 commit 6225c45

File tree

9 files changed

+120
-16
lines changed

9 files changed

+120
-16
lines changed

src/common/types/msGraphApi.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export interface UserProfileData {
2+
id?: string;
3+
userPrincipalName: string;
4+
displayName?: string;
5+
givenName?: string;
6+
surname?: string;
7+
mail?: string;
8+
jobTitle?: string;
9+
mobilePhone?: string;
10+
officeLocation?: string;
11+
preferredLanguage?: string;
12+
businessPhones?: string[];
13+
}

src/ui/Router.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ScanTicketsPage } from './pages/tickets/ScanTickets.page';
1717
import { SelectTicketsPage } from './pages/tickets/SelectEventId.page';
1818
import { ViewTicketsPage } from './pages/tickets/ViewTickets.page';
1919
import { ManageIamPage } from './pages/iam/ManageIam.page';
20+
import { ManageProfilePage } from './pages/profile/ManageProfile.page';
2021

2122
// Component to handle redirects to login with return path
2223
const LoginRedirect: React.FC = () => {
@@ -87,6 +88,10 @@ const authenticatedRouter = createBrowserRouter([
8788
path: '/logout',
8889
element: <LogoutPage />,
8990
},
91+
{
92+
path: '/profile',
93+
element: <ManageProfilePage />,
94+
},
9095
{
9196
path: '/home',
9297
element: <HomePage />,

src/ui/components/AppShell/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { HeaderNavbar } from '../Navbar/index.js';
2525
import { AuthenticatedProfileDropdown } from '../ProfileDropdown/index.js';
2626
import { getCurrentRevision } from '@ui/util/revision.js';
2727

28-
interface AcmAppShellProps {
28+
export interface AcmAppShellProps {
2929
children: ReactNode;
3030
active?: string;
3131
showLoader?: boolean;
@@ -164,7 +164,7 @@ const AcmAppShell: React.FC<AcmAppShellProps> = ({
164164
padding="md"
165165
header={{ height: 60 }}
166166
navbar={{
167-
width: 200,
167+
width: showSidebar ? 200 : 0,
168168
breakpoint: 'sm',
169169
collapsed: { mobile: !opened },
170170
}}

src/ui/components/AuthGuard/index.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Card, Text, Title } from '@mantine/core';
22
import React, { ReactNode, useEffect, useState } from 'react';
33

4-
import { AcmAppShell } from '@ui/components/AppShell';
4+
import { AcmAppShell, AcmAppShellProps } from '@ui/components/AppShell';
55
import FullScreenLoader from '@ui/components/AuthContext/LoadingScreen';
66
import { getRunEnvironmentConfig, ValidService } from '@ui/config';
77
import { useApi } from '@ui/util/api';
@@ -60,11 +60,13 @@ export const clearAuthCache = () => {
6060
}
6161
};
6262

63-
export const AuthGuard: React.FC<{
64-
resourceDef: ResourceDefinition;
65-
children: ReactNode;
66-
isAppShell?: boolean;
67-
}> = ({ resourceDef, children, isAppShell = true }) => {
63+
export const AuthGuard: React.FC<
64+
{
65+
resourceDef: ResourceDefinition;
66+
children: ReactNode;
67+
isAppShell?: boolean;
68+
} & AcmAppShellProps
69+
> = ({ resourceDef, children, isAppShell = true, ...appShellProps }) => {
6870
const { service, validRoles } = resourceDef;
6971
const { baseEndpoint, authCheckRoute, friendlyName } =
7072
getRunEnvironmentConfig().ServiceConfiguration[service];
@@ -163,12 +165,7 @@ export const AuthGuard: React.FC<{
163165
}
164166

165167
if (isAppShell) {
166-
return (
167-
<AcmAppShell>
168-
<Title order={1}>{friendlyName}</Title>
169-
{children}
170-
</AcmAppShell>
171-
);
168+
return <AcmAppShell {...appShellProps}>{children}</AcmAppShell>;
172169
}
173170

174171
return <>{children}</>;

src/ui/components/ProfileDropdown/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useState } from 'react';
1818

1919
import { AuthContextData, useAuth } from '../AuthContext/index.js';
2020
import classes from '../Navbar/index.module.css';
21+
import { useNavigate } from 'react-router-dom';
2122

2223
interface ProfileDropdownProps {
2324
userData?: AuthContextData;
@@ -26,6 +27,7 @@ interface ProfileDropdownProps {
2627
const AuthenticatedProfileDropdown: React.FC<ProfileDropdownProps> = ({ userData }) => {
2728
const [opened, setOpened] = useState(false);
2829
const theme = useMantineTheme();
30+
const navigate = useNavigate();
2931
const { logout } = useAuth();
3032
if (!userData) {
3133
return null;
@@ -111,6 +113,16 @@ const AuthenticatedProfileDropdown: React.FC<ProfileDropdownProps> = ({ userData
111113
</Group>
112114
</UnstyledButton>
113115
<Divider my="sm" />
116+
<Button
117+
variant="primary"
118+
mb="sm"
119+
fullWidth
120+
onClick={() => {
121+
navigate('/profile');
122+
}}
123+
>
124+
Edit Profile
125+
</Button>
114126
<Button
115127
variant="outline"
116128
fullWidth

src/ui/config.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { execCouncilGroupId, execCouncilTestingGroupId } from '@common/config';
33
export const runEnvironments = ['dev', 'prod', 'local-dev'] as const;
44
// local dev should be used when you want to test against a local instance of the API
55

6-
export const services = ['core', 'tickets', 'merch'] as const;
6+
export const services = ['core', 'tickets', 'merch', 'msGraphApi'] as const;
77
export type RunEnvironment = (typeof runEnvironments)[number];
88
export type ValidServices = (typeof services)[number];
99
export type ValidService = ValidServices;
@@ -49,6 +49,12 @@ const environmentConfig: EnvironmentConfigType = {
4949
friendlyName: 'Merch Sales Service (Prod)',
5050
baseEndpoint: 'https://merchapi.acm.illinois.edu',
5151
},
52+
msGraphApi: {
53+
friendlyName: 'Microsoft Graph API',
54+
baseEndpoint: 'https://graph.microsoft.com',
55+
loginScope: 'https://graph.microsoft.com/.default',
56+
apiId: 'https://graph.microsoft.com',
57+
},
5258
},
5359
KnownGroupMappings: {
5460
Exec: execCouncilTestingGroupId,
@@ -72,6 +78,12 @@ const environmentConfig: EnvironmentConfigType = {
7278
friendlyName: 'Merch Sales Service (Prod)',
7379
baseEndpoint: 'https://merchapi.acm.illinois.edu',
7480
},
81+
msGraphApi: {
82+
friendlyName: 'Microsoft Graph API',
83+
baseEndpoint: 'https://graph.microsoft.com',
84+
loginScope: 'https://graph.microsoft.com/.default',
85+
apiId: 'https://graph.microsoft.com',
86+
},
7587
},
7688
KnownGroupMappings: {
7789
Exec: execCouncilTestingGroupId,
@@ -95,6 +107,12 @@ const environmentConfig: EnvironmentConfigType = {
95107
friendlyName: 'Merch Sales Service',
96108
baseEndpoint: 'https://merchapi.acm.illinois.edu',
97109
},
110+
msGraphApi: {
111+
friendlyName: 'Microsoft Graph API',
112+
baseEndpoint: 'https://graph.microsoft.com',
113+
loginScope: 'https://graph.microsoft.com/.default',
114+
apiId: 'https://graph.microsoft.com',
115+
},
98116
},
99117
KnownGroupMappings: {
100118
Exec: execCouncilGroupId,

src/ui/pages/events/ViewEvents.page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Text, Button, Table, Modal, Group, Transition, ButtonGroup } from '@mantine/core';
1+
import { Text, Button, Table, Modal, Group, Transition, ButtonGroup, Title } from '@mantine/core';
22
import { useDisclosure } from '@mantine/hooks';
33
import { notifications } from '@mantine/notifications';
44
import { IconPlus, IconTrash } from '@tabler/icons-react';
@@ -131,6 +131,9 @@ export const ViewEventsPage: React.FC = () => {
131131

132132
return (
133133
<AuthGuard resourceDef={{ service: 'core', validRoles: [AppRoles.EVENTS_MANAGER] }}>
134+
<Title order={1} mb={'md'}>
135+
Event Management
136+
</Title>
134137
{deleteCandidate && (
135138
<Modal
136139
opened={opened}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { Title } from '@mantine/core';
3+
import { AuthGuard } from '@ui/components/AuthGuard';
4+
import { useApi } from '@ui/util/api';
5+
import { UserProfileData } from '@common/types/msGraphApi';
6+
import { ManageProfileComponent } from './ManageProfileComponent';
7+
8+
export const ManageProfilePage: React.FC = () => {
9+
const api = useApi('msGraphApi');
10+
11+
const getProfile = async () => {
12+
return (await api.get('/v1.0/me')).data as UserProfileData;
13+
};
14+
15+
const setProfile = async (data: UserProfileData) => {
16+
return (await api.patch('/v1.0/me', data)).data;
17+
};
18+
19+
return (
20+
<AuthGuard resourceDef={{ service: 'msGraphApi', validRoles: [] }} showSidebar={true}>
21+
<Title>Edit Profile</Title>
22+
<ManageProfileComponent getProfile={getProfile} setProfile={setProfile} />
23+
</AuthGuard>
24+
);
25+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { Title, SimpleGrid, Container } from '@mantine/core';
3+
import { AuthGuard } from '@ui/components/AuthGuard';
4+
import { useApi } from '@ui/util/api';
5+
import { AppRoles } from '@common/roles';
6+
import { getRunEnvironmentConfig } from '@ui/config';
7+
import { UserProfileData } from '@common/types/msGraphApi';
8+
9+
interface ManageProfileComponentProps {
10+
getProfile: () => Promise<UserProfileData>;
11+
setProfile: (data: UserProfileData) => Promise<any>;
12+
}
13+
14+
export const ManageProfileComponent: React.FC<ManageProfileComponentProps> = ({
15+
getProfile,
16+
setProfile,
17+
}) => {
18+
const [userProfile, setUserProfile] = useState<undefined | null | UserProfileData>(undefined);
19+
useEffect(() => {
20+
const fetchProfile = async () => {
21+
try {
22+
setUserProfile(await getProfile());
23+
} catch (e) {
24+
console.error(e);
25+
setUserProfile(null);
26+
}
27+
};
28+
fetchProfile();
29+
}, [getProfile]);
30+
return null;
31+
};

0 commit comments

Comments
 (0)