Skip to content

Commit a0b957d

Browse files
Merge pull request #4164 from Jr7468/ProxyAddresses
Added a section to remove proxy addresses and set primary addresses
2 parents 1de6fcf + 2d45ca4 commit a0b957d

File tree

1 file changed

+216
-4
lines changed

1 file changed

+216
-4
lines changed

src/pages/identity/administration/users/user/exchange.jsx

Lines changed: 216 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useRouter } from "next/router";
44
import { ApiGetCall } from "/src/api/ApiCall";
55
import CippFormSkeleton from "/src/components/CippFormPages/CippFormSkeleton";
66
import CalendarIcon from "@heroicons/react/24/outline/CalendarIcon";
7-
import { Check, Error, Mail, Fingerprint, Launch } from "@mui/icons-material";
7+
import { Check, Error, Mail, Fingerprint, Launch, Delete, Star, Close } from "@mui/icons-material";
88
import { HeaderedTabbedLayout } from "../../../../../layouts/HeaderedTabbedLayout";
99
import tabOptions from "./tabOptions";
1010
import { CippTimeAgo } from "../../../../../components/CippComponents/CippTimeAgo";
@@ -16,18 +16,26 @@ import { CippExchangeInfoCard } from "../../../../../components/CippCards/CippEx
1616
import { useEffect, useState } from "react";
1717
import CippExchangeSettingsForm from "../../../../../components/CippFormPages/CippExchangeSettingsForm";
1818
import { useForm } from "react-hook-form";
19-
import { Alert, Button, Collapse, CircularProgress, Typography } from "@mui/material";
19+
import { Alert, Button, Collapse, CircularProgress, Typography, TextField, Dialog, DialogTitle, DialogContent, DialogActions, IconButton } from "@mui/material";
2020
import { CippApiResults } from "../../../../../components/CippComponents/CippApiResults";
21-
import { Block, PlayArrow, DeleteForever } from "@mui/icons-material";
21+
import { Block, PlayArrow } from "@mui/icons-material";
2222
import { CippPropertyListCard } from "../../../../../components/CippCards/CippPropertyListCard";
2323
import { getCippTranslation } from "../../../../../utils/get-cipp-translation";
2424
import { getCippFormatting } from "../../../../../utils/get-cipp-formatting";
2525
import CippExchangeActions from "../../../../../components/CippComponents/CippExchangeActions";
26+
import { CippApiDialog } from "../../../../../components/CippComponents/CippApiDialog";
27+
import { useDialog } from "../../../../../hooks/use-dialog";
2628

2729
const Page = () => {
2830
const userSettingsDefaults = useSettings();
2931
const [waiting, setWaiting] = useState(false);
3032
const [showDetails, setShowDetails] = useState(false);
33+
const [actionData, setActionData] = useState({ ready: false });
34+
const [showAddAliasDialog, setShowAddAliasDialog] = useState(false);
35+
const [newAliases, setNewAliases] = useState('');
36+
const [isSubmitting, setIsSubmitting] = useState(false);
37+
const [submitResult, setSubmitResult] = useState(null);
38+
const createDialog = useDialog();
3139
const router = useRouter();
3240
const { userId } = router.query;
3341

@@ -221,7 +229,7 @@ const Page = () => {
221229
{
222230
label: "Remove Mailbox Rule",
223231
type: "POST",
224-
icon: <DeleteForever />,
232+
icon: <Delete />,
225233
url: "/api/ExecRemoveMailboxRule",
226234
data: {
227235
ruleId: "Identity",
@@ -287,6 +295,142 @@ const Page = () => {
287295
},
288296
];
289297

298+
const proxyAddressActions = [
299+
{
300+
label: "Make Primary",
301+
type: "POST",
302+
icon: <Star />,
303+
url: "/api/SetUserAliases",
304+
data: {
305+
id: userId,
306+
tenantFilter: userSettingsDefaults.currentTenant,
307+
MakePrimary: "Address",
308+
},
309+
confirmText: "Are you sure you want to make this the primary proxy address?",
310+
multiPost: false,
311+
relatedQueryKeys: `ListUsers-${userId}`,
312+
},
313+
{
314+
label: "Remove Proxy Address",
315+
type: "POST",
316+
icon: <Delete />,
317+
url: "/api/SetUserAliases",
318+
data: {
319+
id: userId,
320+
tenantFilter: userSettingsDefaults.currentTenant,
321+
RemovedAliases: "Address",
322+
},
323+
confirmText: "Are you sure you want to remove this proxy address?",
324+
multiPost: false,
325+
relatedQueryKeys: `ListUsers-${userId}`,
326+
},
327+
];
328+
329+
const handleAddAliases = () => {
330+
const aliases = newAliases
331+
.split('\n')
332+
.map(alias => alias.trim())
333+
.filter(alias => alias);
334+
if (aliases.length > 0) {
335+
setIsSubmitting(true);
336+
setSubmitResult(null);
337+
fetch('/api/SetUserAliases', {
338+
method: 'POST',
339+
headers: {
340+
'Content-Type': 'application/json',
341+
},
342+
body: JSON.stringify({
343+
id: userId,
344+
tenantFilter: userSettingsDefaults.currentTenant,
345+
AddedAliases: aliases.join(','),
346+
userPrincipalName: graphUserRequest.data?.[0]?.userPrincipalName,
347+
}),
348+
})
349+
.then(response => response.json())
350+
.then(data => {
351+
setSubmitResult({ success: true, message: 'Aliases added successfully' });
352+
graphUserRequest.refetch();
353+
setTimeout(() => {
354+
setShowAddAliasDialog(false);
355+
setNewAliases('');
356+
setSubmitResult(null);
357+
}, 1500);
358+
})
359+
.catch(error => {
360+
setSubmitResult({ success: false, message: 'Failed to add aliases' });
361+
})
362+
.finally(() => {
363+
setIsSubmitting(false);
364+
});
365+
}
366+
};
367+
368+
const proxyAddressesCard = [
369+
{
370+
id: 1,
371+
cardLabelBox: {
372+
cardLabelBoxHeader: graphUserRequest.isFetching ? (
373+
<CircularProgress size="25px" color="inherit" />
374+
) : graphUserRequest.data?.[0]?.proxyAddresses?.length > 1 ? (
375+
<Check />
376+
) : (
377+
<Error />
378+
),
379+
},
380+
text: "Current Proxy Addresses",
381+
subtext: graphUserRequest.data?.[0]?.proxyAddresses?.length > 1
382+
? "Proxy addresses are configured for this user"
383+
: "No proxy addresses configured for this user",
384+
statusColor: "green.main",
385+
table: {
386+
title: "Proxy Addresses",
387+
hideTitle: true,
388+
data: graphUserRequest.data?.[0]?.proxyAddresses?.map(address => ({
389+
Address: address,
390+
Type: address.startsWith('SMTP:') ? 'Primary' : 'Alias',
391+
})) || [],
392+
refreshFunction: () => graphUserRequest.refetch(),
393+
isFetching: graphUserRequest.isFetching,
394+
simpleColumns: ["Address", "Type"],
395+
actions: proxyAddressActions,
396+
offCanvas: {
397+
children: (data) => {
398+
return (
399+
<CippPropertyListCard
400+
cardSx={{ p: 0, m: -2 }}
401+
title="Address Details"
402+
propertyItems={[
403+
{
404+
label: "Address",
405+
value: data.Address,
406+
},
407+
{
408+
label: "Type",
409+
value: data.Type,
410+
},
411+
]}
412+
actionItems={proxyAddressActions}
413+
/>
414+
);
415+
},
416+
},
417+
},
418+
children: (
419+
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', mb: 2, px: 2 }}>
420+
<Button
421+
startIcon={<Mail />}
422+
onClick={() => setShowAddAliasDialog(true)}
423+
variant="contained"
424+
color="primary"
425+
size="small"
426+
>
427+
Add Alias
428+
</Button>
429+
</Box>
430+
),
431+
},
432+
];
433+
290434
return (
291435
<HeaderedTabbedLayout
292436
tabOptions={tabOptions}
@@ -345,6 +489,11 @@ const Page = () => {
345489
</Grid>
346490
<Grid item size={8}>
347491
<Stack spacing={3}>
492+
<CippBannerListCard
493+
isFetching={graphUserRequest.isLoading}
494+
items={proxyAddressesCard}
495+
isCollapsible={graphUserRequest.data?.[0]?.proxyAddresses?.length !== 0}
496+
/>
348497
<CippBannerListCard
349498
isFetching={userRequest.isLoading}
350499
items={permissions}
@@ -374,6 +523,69 @@ const Page = () => {
374523
</Grid>
375524
</Box>
376525
)}
526+
{actionData.ready && (
527+
<CippApiDialog
528+
createDialog={createDialog}
529+
title="Confirmation"
530+
api={actionData.action}
531+
row={actionData.data}
532+
/>
533+
)}
534+
<Dialog
535+
open={showAddAliasDialog}
536+
onClose={() => setShowAddAliasDialog(false)}
537+
maxWidth="sm"
538+
fullWidth
539+
>
540+
<DialogTitle>
541+
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
542+
Add Proxy Addresses
543+
<IconButton onClick={() => setShowAddAliasDialog(false)} size="small">
544+
<Close />
545+
</IconButton>
546+
</Box>
547+
</DialogTitle>
548+
<DialogContent>
549+
<Box sx={{ mt: 2 }}>
550+
<TextField
551+
autoFocus
552+
fullWidth
553+
multiline
554+
rows={6}
555+
value={newAliases}
556+
onChange={(e) => setNewAliases(e.target.value)}
557+
placeholder="One alias per line"
558+
variant="outlined"
559+
disabled={isSubmitting}
560+
/>
561+
{submitResult && (
562+
<Alert
563+
severity={submitResult.success ? "success" : "error"}
564+
sx={{ mt: 2 }}
565+
>
566+
{submitResult.message}
567+
</Alert>
568+
)}
569+
</Box>
570+
</DialogContent>
571+
<DialogActions sx={{ px: 3, pb: 2 }}>
572+
<Button
573+
onClick={() => setShowAddAliasDialog(false)}
574+
disabled={isSubmitting}
575+
>
576+
Cancel
577+
</Button>
578+
<Button
579+
onClick={handleAddAliases}
580+
variant="contained"
581+
color="primary"
582+
disabled={!newAliases.trim() || isSubmitting}
583+
startIcon={isSubmitting ? <CircularProgress size={20} color="inherit" /> : null}
584+
>
585+
{isSubmitting ? 'Adding...' : 'Add Aliases'}
586+
</Button>
587+
</DialogActions>
588+
</Dialog>
377589
</HeaderedTabbedLayout>
378590
);
379591
};

0 commit comments

Comments
 (0)