Skip to content

Commit 5a9f687

Browse files
authored
Merge pull request #181 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 826af08 + b048311 commit 5a9f687

File tree

5 files changed

+363
-1
lines changed

5 files changed

+363
-1
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useState, useEffect } from "react";
2+
import { Grid, Typography, CircularProgress } from "@mui/material";
3+
import { CippWizardStepButtons } from "./CippWizardStepButtons";
4+
5+
export const CippAddTenantConfirmation = ({ formData, onSubmit, onBack }) => {
6+
const [domainStatus, setDomainStatus] = useState(null);
7+
const [addressStatus, setAddressStatus] = useState(null);
8+
const [loading, setLoading] = useState(true);
9+
10+
useEffect(() => {
11+
const checkDomainAvailability = async () => {
12+
try {
13+
const response = await fetch("/api/AddTenant", {
14+
method: "POST",
15+
headers: { "Content-Type": "application/json" },
16+
body: JSON.stringify({ Action: "ValidateDomain", TenantName: formData.Domain }),
17+
});
18+
const result = await response.json();
19+
setDomainStatus(result.Status === "Success" ? "Available" : "Unavailable");
20+
} catch {
21+
setDomainStatus("Error");
22+
}
23+
};
24+
25+
const validateAddress = async () => {
26+
try {
27+
const response = await fetch("/api/AddTenant", {
28+
method: "POST",
29+
headers: { "Content-Type": "application/json" },
30+
body: JSON.stringify({
31+
Action: "ValidateAddress",
32+
AddressLine1: formData.AddressLine1,
33+
AddressLine2: formData.AddressLine2,
34+
City: formData.City,
35+
State: formData.State,
36+
PostalCode: formData.PostalCode,
37+
Country: formData.Country,
38+
}),
39+
});
40+
const result = await response.json();
41+
setAddressStatus(result.Status === "Success" ? "Valid" : "Invalid");
42+
} catch {
43+
setAddressStatus("Error");
44+
}
45+
};
46+
47+
const performChecks = async () => {
48+
setLoading(true);
49+
await Promise.all([checkDomainAvailability(), validateAddress()]);
50+
setLoading(false);
51+
};
52+
53+
performChecks();
54+
}, [formData]);
55+
56+
const isReadyToSubmit = domainStatus === "Available" && addressStatus === "Valid";
57+
58+
return (
59+
<Grid container spacing={3}>
60+
<Grid item xs={12}>
61+
<Typography variant="h6">Confirmation</Typography>
62+
<Typography>
63+
Please review the results of the domain availability and address validation before submitting.
64+
</Typography>
65+
</Grid>
66+
<Grid item xs={12}>
67+
<Typography>Domain Status: {loading ? <CircularProgress size={20} /> : domainStatus}</Typography>
68+
</Grid>
69+
<Grid item xs={12}>
70+
<Typography>Address Status: {loading ? <CircularProgress size={20} /> : addressStatus}</Typography>
71+
</Grid>
72+
<Grid item xs={12}>
73+
<CippWizardStepButtons onNext={onSubmit} onBack={onBack} nextDisabled={!isReadyToSubmit || loading} />
74+
</Grid>
75+
</Grid>
76+
);
77+
};
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { InputAdornment } from "@mui/material";
2+
import { Grid } from "@mui/system";
3+
import CippFormComponent from "../CippComponents/CippFormComponent";
4+
import { CippWizardStepButtons } from "./CippWizardStepButtons";
5+
6+
export const CippAddTenantForm = (props) => {
7+
const { formControl, onPreviousStep, onNextStep, currentStep } = props;
8+
9+
const fields = [
10+
{
11+
name: "TenantName",
12+
label: "Tenant Name",
13+
placeholder: "Enter the desired subdomain for the onmicrosoft.com domain",
14+
type: "textField",
15+
required: true,
16+
InputProps: {
17+
endAdornment: <InputAdornment position="end">.onmicrosoft.com</InputAdornment>,
18+
},
19+
gridSize: 12,
20+
},
21+
{
22+
name: "CompanyName",
23+
label: "Company Name",
24+
type: "textField",
25+
required: true,
26+
gridSize: 12,
27+
placeholder: "Enter the registered company/organization name",
28+
},
29+
{
30+
name: "FirstName",
31+
label: "First Name",
32+
type: "textField",
33+
required: true,
34+
placeholder: "Enter the first name of the contact person",
35+
},
36+
{
37+
name: "LastName",
38+
label: "Last Name",
39+
type: "textField",
40+
required: true,
41+
placeholder: "Enter the last name of the contact person",
42+
},
43+
{
44+
name: "Email",
45+
label: "Email",
46+
type: "email",
47+
required: true,
48+
placeholder: "Enter the customer's email address",
49+
},
50+
{
51+
name: "PhoneNumber",
52+
label: "Phone Number",
53+
type: "textField",
54+
required: true,
55+
placeholder: "Enter the contact phone number",
56+
},
57+
{
58+
name: "Country",
59+
label: "Country",
60+
type: "textField",
61+
required: true,
62+
placeholder: "Enter the country (e.g., US)",
63+
},
64+
{
65+
name: "City",
66+
label: "City",
67+
type: "textField",
68+
required: true,
69+
placeholder: "Enter the city",
70+
},
71+
{
72+
name: "State",
73+
label: "State",
74+
type: "textField",
75+
required: true,
76+
placeholder: "Enter the state or region",
77+
},
78+
{
79+
name: "AddressLine1",
80+
label: "Address Line 1",
81+
type: "textField",
82+
required: true,
83+
placeholder: "Enter the primary address line",
84+
},
85+
{
86+
name: "AddressLine2",
87+
label: "Address Line 2",
88+
type: "textField",
89+
required: false,
90+
placeholder: "Enter the secondary address line (optional)",
91+
},
92+
{
93+
name: "PostalCode",
94+
label: "Postal Code",
95+
type: "textField",
96+
required: true,
97+
placeholder: "Enter the postal code",
98+
},
99+
];
100+
101+
return (
102+
<Grid container spacing={3}>
103+
{fields.map((field, index) => (
104+
<Grid size={field?.gridSize ?? { xs: 12, md: 6 }} key={index}>
105+
<CippFormComponent
106+
{...field}
107+
formControl={formControl}
108+
/>
109+
</Grid>
110+
))}
111+
<Grid item xs={12}>
112+
<CippWizardStepButtons
113+
currentStep={currentStep}
114+
onPreviousStep={onPreviousStep}
115+
onNextStep={onNextStep}
116+
formControl={formControl}
117+
noSubmitButton={true}
118+
/>
119+
</Grid>
120+
</Grid>
121+
);
122+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Grid } from "@mui/material";
2+
import CippFormComponent from "../CippComponents/CippFormComponent";
3+
4+
export const CustomerForm = (props) => {
5+
const { formControl } = props;
6+
7+
const fields = [
8+
{ name: "Domain", label: "Domain", type: "textField", required: true },
9+
{ name: "CompanyName", label: "Company Name", type: "textField", required: true },
10+
{ name: "FirstName", label: "First Name", type: "textField", required: true },
11+
{ name: "LastName", label: "Last Name", type: "textField", required: true },
12+
{ name: "Email", label: "Email", type: "email", required: true },
13+
{ name: "PhoneNumber", label: "Phone Number", type: "textField", required: true },
14+
{ name: "Country", label: "Country", type: "textField", required: true },
15+
{ name: "City", label: "City", type: "textField", required: true },
16+
{ name: "State", label: "State", type: "textField", required: true },
17+
{ name: "AddressLine1", label: "Address Line 1", type: "textField", required: true },
18+
{ name: "AddressLine2", label: "Address Line 2", type: "textField", required: false },
19+
{ name: "PostalCode", label: "Postal Code", type: "textField", required: true },
20+
];
21+
22+
const transformPayload = (formData) => {
23+
const payload = {
24+
enableGDAPByDefault: false,
25+
Id: null,
26+
CommerceId: null,
27+
CompanyProfile: {
28+
TenantId: null,
29+
Domain: formData.Domain,
30+
CompanyName: formData.CompanyName,
31+
Attributes: { ObjectType: "CustomerCompanyProfile" },
32+
},
33+
BillingProfile: {
34+
Id: null,
35+
FirstName: formData.FirstName,
36+
LastName: formData.LastName,
37+
Email: formData.Email,
38+
Culture: "EN-US",
39+
Language: "En",
40+
CompanyName: formData.CompanyName,
41+
DefaultAddress: {
42+
Country: formData.Country,
43+
Region: null,
44+
City: formData.City,
45+
State: formData.State,
46+
AddressLine1: formData.AddressLine1,
47+
AddressLine2: formData.AddressLine2,
48+
PostalCode: formData.PostalCode,
49+
FirstName: formData.FirstName,
50+
LastName: formData.LastName,
51+
PhoneNumber: formData.PhoneNumber,
52+
},
53+
Attributes: { ObjectType: "CustomerBillingProfile" },
54+
},
55+
RelationshipToPartner: "none",
56+
AllowDelegatedAccess: null,
57+
UserCredentials: null,
58+
CustomDomains: null,
59+
Attributes: { ObjectType: "Customer" },
60+
};
61+
62+
if (formData.ResellerType === "Tier2" && associatedPartnerId) {
63+
payload.AssociatedPartnerId = associatedPartnerId;
64+
}
65+
66+
return payload;
67+
};
68+
69+
return (
70+
<Grid container spacing={3}>
71+
{fields.map((field, index) => (
72+
<Grid item xs={12} md={6} key={index}>
73+
<CippFormComponent
74+
type={field.type}
75+
label={field.label}
76+
name={field.name}
77+
required={field.required}
78+
formControl={formControl} // Use shared formControl
79+
fullWidth
80+
/>
81+
</Grid>
82+
))}
83+
</Grid>
84+
);
85+
};
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Layout as DashboardLayout } from "../../../../layouts/index.js";
2+
import CippWizardPage from "../../../../components/CippWizard/CippWizardPage.jsx";
3+
import { CippWizardOptionsList } from "../../../../components/CippWizard/CippWizardOptionsList.jsx";
4+
import { CippAddTenantForm } from "../../../../components/CippWizard/CippAddTenantForm.jsx";
5+
import { CippAddTenantConfirmation } from "../../../../components/CippWizard/CippAddTenantConfirmation.jsx";
6+
import { BuildingOfficeIcon, CloudIcon } from "@heroicons/react/24/outline";
7+
8+
const Page = () => {
9+
const steps = [
10+
{
11+
title: "Step 1",
12+
description: "Select Reseller Type",
13+
component: CippWizardOptionsList,
14+
componentProps: {
15+
title: "Select your reseller type",
16+
subtext: `Choose the type of reseller you are to proceed with adding a new tenant.`,
17+
valuesKey: "ResellerType",
18+
options: [
19+
{
20+
description: "Use this option if you are a Direct Reseller",
21+
icon: <CloudIcon />,
22+
label: "I'm a Tier 1 CSP",
23+
value: "Tier1",
24+
},
25+
{
26+
description: "Use this option if you are an Indirect Reseller",
27+
icon: <BuildingOfficeIcon />,
28+
label: "I'm a Tier 2 CSP",
29+
value: "Tier2",
30+
},
31+
],
32+
},
33+
},
34+
{
35+
title: "Step 2",
36+
description: "Enter Tenant Details",
37+
component: CippAddTenantForm,
38+
componentProps: {},
39+
},
40+
{
41+
title: "Step 3",
42+
description: "Confirm and Submit",
43+
component: CippAddTenantConfirmation,
44+
componentProps: {},
45+
},
46+
];
47+
48+
return (
49+
<>
50+
<CippWizardPage
51+
backButton={false}
52+
steps={steps}
53+
wizardTitle="Add New Tenant"
54+
postUrl={"/api/AddTenant"}
55+
/>
56+
</>
57+
);
58+
};
59+
60+
Page.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>;
61+
62+
export default Page;

src/pages/tenant/administration/tenants/index.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Layout as DashboardLayout } from "/src/layouts/index.js";
22
import { TabbedLayout } from "/src/layouts/TabbedLayout";
33
import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx";
4+
import { Button, SvgIcon } from "@mui/material";
5+
import { Add, Edit } from "@mui/icons-material";
46
import tabOptions from "./tabOptions";
5-
import { Edit } from "@mui/icons-material";
7+
import NextLink from "next/link";
68

79
const Page = () => {
810
const pageTitle = "Tenants";
@@ -40,6 +42,20 @@ const Page = () => {
4042
tenantFilter: null,
4143
}}
4244
actions={actions}
45+
cardButton={
46+
<Button
47+
variant="contained"
48+
color="primary"
49+
size="small"
50+
component={NextLink}
51+
href="/tenant/administration/tenants/add"
52+
>
53+
<SvgIcon fontSize="small" style={{ marginRight: 4 }}>
54+
<Add />
55+
</SvgIcon>
56+
Add Tenant
57+
</Button>
58+
}
4359
/>
4460
);
4561
};

0 commit comments

Comments
 (0)