Skip to content

Commit fcfeed9

Browse files
bulk user adds
1 parent a9ff314 commit fcfeed9

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

src/routes.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const Users = React.lazy(() => import('src/views/identity/administration/Users')
88
const DeletedItems = React.lazy(() => import('src/views/identity/administration/Deleted'))
99
const ViewBEC = React.lazy(() => import('src/views/identity/administration/ViewBEC'))
1010
const AddUser = React.lazy(() => import('src/views/identity/administration/AddUser'))
11+
const AddUserBulk = React.lazy(() => import('src/views/identity/administration/AddUserBulk'))
12+
1113
const InviteGuest = React.lazy(() => import('src/views/identity/administration/InviteGuest'))
1214
const EditUser = React.lazy(() => import('src/views/identity/administration/EditUser'))
1315
const ViewUser = React.lazy(() => import('src/views/identity/administration/ViewUser'))
@@ -251,6 +253,12 @@ const routes = [
251253
{ path: '/cipp/500', name: 'Error', component: Page500 },
252254
{ path: '/identity', name: 'Identity' },
253255
{ path: '/identity/administration/users/add', name: 'Add User', component: AddUser },
256+
{
257+
path: '/identity/administration/users/addbulk',
258+
name: 'Add User Bulk',
259+
component: AddUserBulk,
260+
},
261+
254262
{ path: '/identity/administration/users/edit', name: 'Edit User', component: EditUser },
255263
{ path: '/identity/administration/users/view', name: 'View User', component: ViewUser },
256264
{
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import React, { useState } from 'react'
2+
import { CButton, CCallout, CCol, CRow, CSpinner } from '@coreui/react'
3+
import { Field, FormSpy } from 'react-final-form'
4+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
5+
import { faExclamationTriangle, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'
6+
import { CippCallout, CippWizard } from 'src/components/layout'
7+
import PropTypes from 'prop-types'
8+
import { RFFCFormInput } from 'src/components/forms'
9+
import { CippTable } from 'src/components/tables'
10+
import { CippCodeBlock, TenantSelector } from 'src/components/utilities'
11+
import { CSVReader } from 'react-papaparse'
12+
import { useLazyGenericPostRequestQuery } from 'src/store/api/app'
13+
import { useSelector } from 'react-redux'
14+
15+
const Error = ({ name }) => (
16+
<Field
17+
name={name}
18+
subscription={{ touched: true, error: true }}
19+
render={({ meta: { touched, error } }) =>
20+
touched && error ? (
21+
<CCallout color="danger">
22+
<FontAwesomeIcon icon={faExclamationTriangle} color="danger" />
23+
{error}
24+
</CCallout>
25+
) : null
26+
}
27+
/>
28+
)
29+
30+
Error.propTypes = {
31+
name: PropTypes.string.isRequired,
32+
}
33+
34+
const AddUserBulk = () => {
35+
const [genericPostRequest, postResults] = useLazyGenericPostRequestQuery()
36+
const [BulkUser, setBulkUser] = useState([])
37+
const currentSettings = useSelector((state) => state.app)
38+
const addedFields = currentSettings?.userSettingsDefaults?.defaultAttributes
39+
? //if we have default attributes, add the label object to the fields array
40+
currentSettings.userSettingsDefaults.defaultAttributes.map((item) => item.label)
41+
: []
42+
const fields = [
43+
'givenName',
44+
'surName',
45+
'displayName',
46+
'mailNickName',
47+
'domain',
48+
'usageLocation',
49+
'JobTitle',
50+
'streetAddress',
51+
'PostalCode',
52+
'City',
53+
'State',
54+
'Department',
55+
'MobilePhone',
56+
'businessPhones',
57+
...addedFields,
58+
]
59+
const columns = fields.map((field) => {
60+
return {
61+
name: field,
62+
selector: (row) => row[field],
63+
sortable: true,
64+
}
65+
})
66+
67+
const tableColumns = [
68+
...columns,
69+
{
70+
name: 'Remove',
71+
button: true,
72+
cell: (row, index) => {
73+
return (
74+
<CButton onClick={() => handleRemove(row)} size="sm" variant="ghost" color="danger">
75+
<FontAwesomeIcon icon={faTrash} />
76+
</CButton>
77+
)
78+
},
79+
},
80+
]
81+
const valbutton = (value) =>
82+
BulkUser.length
83+
? undefined
84+
: 'You must add at least one user. Did you forget to click add or upload the CSV?'
85+
const handleOnDrop = (data) => {
86+
const importdata = data.map((item) => {
87+
//find any keys that have a null or blank string value, and remove them
88+
Object.keys(item.data).forEach((key) => {
89+
if (item.data[key] === null || item.data[key] === '') {
90+
delete item.data[key]
91+
}
92+
})
93+
return item.data
94+
})
95+
setBulkUser([...BulkUser, ...importdata])
96+
// console.log(importdata)
97+
}
98+
99+
const handleOnError = (err, file, inputElem, reason) => {
100+
//set upload error
101+
}
102+
const tenantDomain = useSelector((state) => state.app.currentTenant.defaultDomainName)
103+
104+
const handleSubmit = async (values) => {
105+
const shippedValues = {
106+
TenantFilter: tenantDomain,
107+
BulkUser,
108+
...values,
109+
}
110+
//alert(JSON.stringify(values, null, 2))
111+
genericPostRequest({ path: '/api/AddUserBulk', values: shippedValues })
112+
}
113+
const addRowtoData = (values) => {
114+
setBulkUser((prevState) => {
115+
if (prevState) {
116+
return [values, ...prevState]
117+
} else {
118+
return [values]
119+
}
120+
})
121+
}
122+
const handleRemove = async (itemindex) => {
123+
let RemovedItems = BulkUser.filter((item) => item !== itemindex)
124+
setBulkUser((prevState) => {
125+
return RemovedItems
126+
})
127+
}
128+
return (
129+
<CippWizard onSubmit={handleSubmit} wizardTitle="Add Bulk User Wizard">
130+
<CippWizard.Page title="Tenant Choice" description="Choose the tenant to add users to">
131+
<center>
132+
<h3 className="text-primary">Step 1</h3>
133+
<h5 className="card-title mb-4">Choose a tenant</h5>
134+
</center>
135+
<hr className="my-4" />
136+
<Field name="selectedTenants">{(props) => <TenantSelector />}</Field>
137+
<Error name="selectedTenants" />
138+
<hr className="my-4" />
139+
</CippWizard.Page>
140+
<CippWizard.Page title="Enter User Information" description="Enter the information">
141+
<center>
142+
<h3 className="text-primary">Step 2</h3>
143+
<h5>Enter user information</h5>
144+
</center>
145+
<hr className="my-4" />
146+
<div className="mb-2">
147+
<br />
148+
<p>
149+
<a
150+
href={`data:text/csv;charset=utf-8,%EF%BB%BF${encodeURIComponent(
151+
fields.join(',') + '\n',
152+
)}`}
153+
download="BulkUserAdd.csv"
154+
>
155+
Example CSV
156+
</a>
157+
</p>
158+
</div>
159+
<CCol xs={'auto'}>
160+
<CSVReader
161+
onDrop={handleOnDrop}
162+
onError={handleOnError}
163+
config={{ header: true, skipEmptyLines: true }}
164+
>
165+
<span>Drop CSV file here or click to upload.</span>
166+
</CSVReader>
167+
</CCol>
168+
<br></br>
169+
<CRow>
170+
{fields.map((field, idx) => {
171+
return (
172+
<CCol key={idx} xs={'auto'}>
173+
<RFFCFormInput name={field} label={field} type="text" />
174+
</CCol>
175+
)
176+
})}
177+
<CCol xs={'auto'} className="align-self-end">
178+
<FormSpy>
179+
{/* eslint-disable react/prop-types */}
180+
{(props) => {
181+
return (
182+
<>
183+
<CButton
184+
onClick={() => addRowtoData(props.values)}
185+
name="addButton"
186+
className="mb-3"
187+
>
188+
<FontAwesomeIcon icon={faPlus} className="me-2" />
189+
Add
190+
</CButton>
191+
</>
192+
)
193+
}}
194+
</FormSpy>
195+
</CCol>
196+
<Field
197+
key={BulkUser}
198+
name="BlockNext"
199+
component="hidden"
200+
type="hidden"
201+
validate={valbutton}
202+
></Field>
203+
<Error name="BlockNext" />
204+
</CRow>
205+
<CRow>
206+
<CCol>
207+
{BulkUser && (
208+
<CippTable
209+
reportName="none"
210+
tableProps={{ subheader: false }}
211+
data={BulkUser}
212+
columns={tableColumns}
213+
/>
214+
)}
215+
</CCol>
216+
</CRow>
217+
218+
<hr className="my-4" />
219+
</CippWizard.Page>
220+
<CippWizard.Page title="Review and Confirm" description="Confirm the settings to apply">
221+
<center>
222+
<h3 className="text-primary">Step 4</h3>
223+
<h5 className="mb-4">Confirm and apply</h5>
224+
<hr className="my-4" />
225+
{postResults.isFetching && (
226+
<CCallout color="info">
227+
<CSpinner>Loading</CSpinner>
228+
</CCallout>
229+
)}
230+
<p>
231+
{postResults.isSuccess && (
232+
<CippCodeBlock
233+
code={postResults.data?.Results.map((item) => {
234+
return <li key={item}>{item}</li>
235+
})}
236+
callout={true}
237+
calloutCopyValue={postResults.data?.Results}
238+
/>
239+
)}
240+
</p>
241+
{BulkUser && (
242+
<CippTable
243+
reportName="none"
244+
tableProps={{ subheader: false }}
245+
data={BulkUser}
246+
columns={tableColumns}
247+
/>
248+
)}
249+
</center>
250+
<hr className="my-4" />
251+
</CippWizard.Page>
252+
</CippWizard>
253+
)
254+
}
255+
256+
export default AddUserBulk

0 commit comments

Comments
 (0)