Skip to content

Commit 0074e7d

Browse files
Merge pull request #2930 from devtron-labs/feat/export-csv
feat: export csv
2 parents 2b78e48 + 6fe1b5f commit 0074e7d

27 files changed

+462
-744
lines changed

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.20.2-alpha-2",
7+
"@devtron-labs/devtron-fe-common-lib": "1.20.3-pre-4",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",
@@ -23,7 +23,6 @@
2323
"moment": "^2.29.4",
2424
"query-string": "^7.1.1",
2525
"react": "^17.0.2",
26-
"react-csv": "^2.2.2",
2726
"react-dates": "^21.8.0",
2827
"react-dom": "^17.0.2",
2928
"react-ga4": "^1.4.1",
@@ -76,7 +75,6 @@
7675
"@types/jest": "^27.4.1",
7776
"@types/node": "20.11.0",
7877
"@types/react": "17.0.39",
79-
"@types/react-csv": "^1.1.3",
8078
"@types/react-dates": "^21.8.6",
8179
"@types/react-dom": "17.0.13",
8280
"@types/react-router-dom": "^5.3.3",

src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/ExportPermissionGroupsToCsv.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { FILE_NAMES, GROUP_EXPORT_HEADER_ROW } from '../../../../../components/common/ExportToCsv/constants'
18-
import ExportToCsv from '../../../../../components/common/ExportToCsv/ExportToCsv'
17+
import { ExportToCsv } from '@devtron-labs/devtron-fe-common-lib'
18+
1919
import { useAuthorizationContext } from '../../AuthorizationProvider'
2020
import { getRoleFiltersToExport } from '../../utils'
21-
import { PermissionGroupListHeaderProps } from './types'
21+
import { GROUP_EXPORT_HEADER_ROW, GROUP_EXPORT_HEADERS } from './constants'
22+
import { ExportPermissionGroupDataType, PermissionGroupListHeaderProps } from './types'
2223

2324
const ExportPermissionGroupsToCsv = ({
2425
disabled,
@@ -29,7 +30,7 @@ const ExportPermissionGroupsToCsv = ({
2930
/**
3031
* Provides the list of permission groups which have access to devtron applications
3132
*/
32-
const getPermissionGroupDataToExport = async () => {
33+
const getPermissionGroupDataToExport = async (): Promise<ExportPermissionGroupDataType[]> => {
3334
const { permissionGroups } = await exportCsvPromise()
3435

3536
const groupsList = permissionGroups.reduce((_groupsList, _group) => {
@@ -44,7 +45,7 @@ const ExportPermissionGroupsToCsv = ({
4445
isRowAdded = true
4546
}
4647

47-
const _groupData = {
48+
const _groupData: ExportPermissionGroupDataType = {
4849
groupName: _group.name,
4950
groupId: _group.id,
5051
description: _group.description || '-',
@@ -75,11 +76,14 @@ const ExportPermissionGroupsToCsv = ({
7576
}
7677

7778
return (
78-
<ExportToCsv
79+
<ExportToCsv<keyof ExportPermissionGroupDataType>
7980
disabled={disabled}
8081
apiPromise={getPermissionGroupDataToExport}
81-
fileName={FILE_NAMES.Groups}
82-
showOnlyIcon
82+
fileName="Devtron Apps Permission Group"
83+
triggerElementConfig={{
84+
showOnlyIcon: true,
85+
}}
86+
headers={GROUP_EXPORT_HEADERS}
8387
/>
8488
)
8589
}

src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/constants.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { ExportToCsvProps } from '@devtron-labs/devtron-fe-common-lib'
18+
1719
import { DEFAULT_SHIMMER_LOADING_TABLE_ROWS } from '../../../../../config'
1820
import { PermissionGroup } from '../../types'
21+
import { ExportPermissionGroupDataType } from './types'
1922

2023
export const permissionGroupLoading: PermissionGroup[] = Array.from(
2124
Array(DEFAULT_SHIMMER_LOADING_TABLE_ROWS).keys(),
@@ -29,3 +32,25 @@ export const permissionGroupLoading: PermissionGroup[] = Array.from(
2932
export enum SortableKeys {
3033
name = 'name',
3134
}
35+
36+
export const GROUP_EXPORT_HEADERS: ExportToCsvProps<keyof ExportPermissionGroupDataType>['headers'] = [
37+
{ label: 'Group Name', key: 'groupName' },
38+
{ label: 'Group ID', key: 'groupId' },
39+
{ label: 'Description', key: 'description' },
40+
{ label: 'Super admin', key: 'superAdmin' },
41+
{ label: 'Project', key: 'project' },
42+
{ label: 'Environment', key: 'environment' },
43+
{ label: 'Application', key: 'application' },
44+
{ label: 'Role', key: 'role' },
45+
]
46+
47+
export const GROUP_EXPORT_HEADER_ROW = {
48+
groupName: 'Group Name',
49+
groupId: 'Group ID',
50+
description: 'Description',
51+
superAdmin: 'Super admin',
52+
project: 'Project',
53+
environment: 'Environment',
54+
application: 'Application',
55+
role: 'Role',
56+
}

src/Pages/GlobalConfigurations/Authorization/PermissionGroups/List/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,14 @@ export interface UserPermissionTableProps
5858
showPagination: boolean
5959
isActionsDisabled: boolean
6060
}
61+
62+
export interface ExportPermissionGroupDataType {
63+
groupName: string
64+
groupId: number
65+
description: string
66+
superAdmin: boolean
67+
project: string
68+
environment: string
69+
application: string
70+
role: string
71+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { ChangeEvent, useEffect } from 'react'
18+
19+
import {
20+
Button,
21+
ButtonStyleType,
22+
ButtonVariantType,
23+
Checkbox,
24+
CHECKBOX_VALUE,
25+
ComponentSizeType,
26+
Icon,
27+
Tooltip,
28+
VisibleModal,
29+
} from '@devtron-labs/devtron-fe-common-lib'
30+
31+
import { ExportConfigurationDialogProps } from './types'
32+
33+
const ExportConfigurationDialog = ({
34+
selectedConfig,
35+
setSelectedConfig,
36+
initialConfig,
37+
exportConfiguration,
38+
proceed,
39+
}: ExportConfigurationDialogProps) => {
40+
const { title, options } = exportConfiguration
41+
42+
const handleConfigSelectionChange = (e: ChangeEvent<HTMLInputElement>) => {
43+
setSelectedConfig((prev) => ({
44+
...prev,
45+
[e.target.name]: e.target.checked,
46+
}))
47+
}
48+
49+
useEffect(() => {
50+
setSelectedConfig(initialConfig)
51+
}, [])
52+
53+
const handleProceedAction = () => {
54+
proceed(true)
55+
}
56+
57+
const handleCancelAction = () => {
58+
proceed(false)
59+
}
60+
61+
return (
62+
<VisibleModal className="export-to-csv-modal" data-testid="export-to-csv-modal">
63+
<div className="modal__body mt-40 p-0">
64+
<h2 className="cn-9 fw-6 fs-16 m-0 dc__border-bottom px-20 py-12">Export to CSV</h2>
65+
<div className="py-16 flex top left dc__overflow-auto mxh-350 px-12">
66+
<div className="fs-13 lh-20 flexbox-col dc__gap-8 flex-grow-1">
67+
<h3 className="fw-6 cn-9 m-0 fs-13 lh-20 dc__truncate px-8">{title}</h3>
68+
<div>
69+
{options.map(({ label, value, description }) => (
70+
<label
71+
className={`m-0 py-6 px-8 flex left dc__gap-8 ${description ? 'top' : ''} dc__hover-n50 br-4 cursor`}
72+
key={value}
73+
htmlFor={value}
74+
>
75+
<Checkbox
76+
value={CHECKBOX_VALUE.CHECKED}
77+
name={value}
78+
dataTestId={`check-${label}`}
79+
id={value}
80+
isChecked={selectedConfig[value] ?? false}
81+
onChange={handleConfigSelectionChange}
82+
rootClassName="m-0 w-20 h-20"
83+
/>
84+
<p className="m-0 flexbox-col">
85+
<Tooltip content={label}>
86+
<span className="fs-13 fw-4 lh-20 cn-9 dc__truncate">{label}</span>
87+
</Tooltip>
88+
{description && (
89+
<Tooltip content={description}>
90+
<span className="fs-12 fw-4 lh-18 cn-7 dc__ellipsis-right__2nd-line">
91+
{description}
92+
</span>
93+
</Tooltip>
94+
)}
95+
</p>
96+
</label>
97+
))}
98+
</div>
99+
</div>
100+
</div>
101+
<div className="flex right dc__gap-12 dc__border-top py-16 px-20">
102+
<Button
103+
variant={ButtonVariantType.secondary}
104+
size={ComponentSizeType.medium}
105+
style={ButtonStyleType.neutral}
106+
onClick={handleCancelAction}
107+
text="Cancel"
108+
dataTestId="cancel-export-csv-button"
109+
/>
110+
<Button
111+
size={ComponentSizeType.medium}
112+
onClick={handleProceedAction}
113+
text="Download"
114+
dataTestId="retry-export-csv-button"
115+
endIcon={<Icon name="ic-download" color={null} />}
116+
disabled={!Object.values(selectedConfig).some((value) => value)}
117+
/>
118+
</div>
119+
</div>
120+
</VisibleModal>
121+
)
122+
}
123+
124+
export default ExportConfigurationDialog

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/ExportUserPermissionsToCsv.tsx

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { getFormattedUTCTimeForExport, UserTypeToFetchType } from '@devtron-labs/devtron-fe-common-lib'
17+
import { useState } from 'react'
1818

19-
import { ExportToCsvProps } from '@Components/common/ExportToCsv/types'
19+
import { ExportToCsv, ExportToCsvProps, getFormattedUTCTimeForExport } from '@devtron-labs/devtron-fe-common-lib'
2020

2121
import { importComponentFromFELibrary } from '../../../../../components/common'
22-
import { FILE_NAMES, USER_EXPORT_HEADER_ROW } from '../../../../../components/common/ExportToCsv/constants'
23-
import ExportToCsv from '../../../../../components/common/ExportToCsv/ExportToCsv'
2422
import { useAuthorizationContext } from '../../AuthorizationProvider'
2523
import { getRoleFiltersToExport } from '../../utils'
2624
import { LAST_LOGIN_TIME_NULL_STATE } from '../constants'
27-
import { UserPermissionListHeaderProps } from './types'
25+
import { USER_EXPORT_HEADER_ROW, USER_EXPORT_HEADERS } from './constants'
26+
import ExportConfigurationDialog from './ExportConfigurationDialog'
27+
import { ExportConfigurationDialogProps, ExportUserPermissionCSVDataType, UserPermissionListHeaderProps } from './types'
2828

2929
const getStatusExportText = importComponentFromFELibrary('getStatusExportText', null, 'function')
3030
const getUserExportToCsvConfiguration = importComponentFromFELibrary(
@@ -39,12 +39,29 @@ const ExportUserPermissionsToCsv = ({
3939
getDataToExport,
4040
}: Pick<UserPermissionListHeaderProps, 'disabled' | 'getDataToExport'>) => {
4141
const { customRoles } = useAuthorizationContext()
42+
const exportConfiguration: ExportConfigurationDialogProps['exportConfiguration'] = getUserExportToCsvConfiguration
43+
? getUserExportToCsvConfiguration()
44+
: null
45+
46+
const getInitialSelectedConfig = (): ExportConfigurationDialogProps['selectedConfig'] =>
47+
exportConfiguration?.options.reduce<ExportConfigurationDialogProps['selectedConfig']>(
48+
(acc, { value }) => {
49+
acc[value] = true
50+
51+
return acc
52+
},
53+
{} as ExportConfigurationDialogProps['selectedConfig'],
54+
) ?? ({} as ExportConfigurationDialogProps['selectedConfig'])
55+
56+
const [selectedConfig, setSelectedConfig] = useState(getInitialSelectedConfig)
4257

4358
/**
4459
* Returns the list of users which have permission to devtron applications
4560
*/
46-
const getUsersDataToExport: ExportToCsvProps<UserTypeToFetchType>['apiPromise'] = async (selectedConfig) => {
47-
const { users } = await getDataToExport(selectedConfig)
61+
const getUsersDataToExport: ExportToCsvProps<keyof ExportUserPermissionCSVDataType>['apiPromise'] = async ({
62+
signal,
63+
}) => {
64+
const { users } = await getDataToExport(selectedConfig, signal)
4865
const userList = users.reduce((_usersList, _user) => {
4966
let isRowAdded = false
5067

@@ -59,7 +76,7 @@ const ExportUserPermissionsToCsv = ({
5976

6077
const updatedOn = getFormattedUTCTimeForExport(_user.updatedOn)
6178

62-
const _userData = {
79+
const _userData: ExportUserPermissionCSVDataType = {
6380
emailId: _user.emailId,
6481
userId: _user.id,
6582
...(showStatus
@@ -130,15 +147,32 @@ const ExportUserPermissionsToCsv = ({
130147
return userList
131148
}
132149

150+
const renderConfigurationModal: ExportToCsvProps<
151+
keyof ExportUserPermissionCSVDataType
152+
>['modalConfig']['renderCustomModal'] = (proceed) => (
153+
<ExportConfigurationDialog
154+
selectedConfig={selectedConfig}
155+
setSelectedConfig={setSelectedConfig}
156+
initialConfig={getInitialSelectedConfig()}
157+
exportConfiguration={exportConfiguration}
158+
proceed={proceed}
159+
/>
160+
)
161+
133162
return (
134-
<ExportToCsv
163+
<ExportToCsv<keyof ExportUserPermissionCSVDataType>
135164
disabled={disabled}
136165
apiPromise={getUsersDataToExport}
137-
fileName={FILE_NAMES.Users}
138-
showOnlyIcon
139-
{...(getUserExportToCsvConfiguration && {
140-
configuration: getUserExportToCsvConfiguration(),
141-
})}
166+
fileName="Devtron Apps Users Data"
167+
triggerElementConfig={{
168+
showOnlyIcon: true,
169+
}}
170+
headers={USER_EXPORT_HEADERS}
171+
modalConfig={
172+
getUserExportToCsvConfiguration
173+
? { renderCustomModal: renderConfigurationModal, hideDialog: false }
174+
: null
175+
}
142176
/>
143177
)
144178
}

src/Pages/GlobalConfigurations/Authorization/UserPermissions/List/UserPermissionList.component.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,24 @@ const UserPermissionList = () => {
108108

109109
const showLoadingState = isLoading || getIsRequestAborted(error)
110110

111-
const getUserDataForExport: UserPermissionContainerProps['getUserDataForExport'] = (selectedConfig) =>
112-
getUserList({
113-
...filterConfig,
114-
showAll: true,
115-
offset: null,
116-
size: null,
117-
sortBy: UserListSortableKeys.email,
118-
sortOrder: SortingOrder.ASC,
119-
...(getUserTypeToFetchFromSelectedConfigOptions && {
120-
typeToFetch: getUserTypeToFetchFromSelectedConfigOptions(selectedConfig),
121-
}),
122-
})
111+
const getUserDataForExport: UserPermissionContainerProps['getUserDataForExport'] = (
112+
selectedConfig,
113+
signal: AbortSignal,
114+
) =>
115+
getUserList(
116+
{
117+
...filterConfig,
118+
showAll: true,
119+
offset: null,
120+
size: null,
121+
sortBy: UserListSortableKeys.email,
122+
sortOrder: SortingOrder.ASC,
123+
...(getUserTypeToFetchFromSelectedConfigOptions && {
124+
typeToFetch: getUserTypeToFetchFromSelectedConfigOptions(selectedConfig),
125+
}),
126+
},
127+
signal,
128+
)
123129

124130
const getSelectAllDialogStatus = () => {
125131
// Set to show the modal, the function is called only if there is an existing selection,

0 commit comments

Comments
 (0)