Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* This source file is available under the terms of the
* Pimcore Open Core License (POCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
* @license Pimcore Open Core License (POCL)
*/

import React, { useState, useEffect } from 'react'
import { Modal } from '@Pimcore/components/modal/modal'
import { Input } from 'antd'
import { type ColumnDef } from '@tanstack/react-table'
import { useTranslation } from 'react-i18next'

export interface CsvImportModalProps {
open: boolean
columns: Array<ColumnDef<any>>
value: any[]
onConfirm: (newRows: any[]) => void
onCancel: () => void
}

const getColumnKeys = (columns: Array<ColumnDef<any>>): string[] => {
return columns
.filter(col => 'accessorKey' in col)
.map(col => ('accessorKey' in col ? (col.accessorKey as string) : ''))
.filter(key => key !== '')
}

const rowsToCsv = (rows: any[], colKeys: string[]): string => {
return rows.map(row => colKeys.map(k => row[k] ?? '').join(',')).join('\n')
}

const csvToRows = (csv: string, colKeys: string[]): any[] => {
return csv.split('\n')
.filter(line => line.trim() !== '')
.map(line => {
const cells = line.split(',')
return Object.fromEntries(colKeys.map((k, i) => [k, cells[i]?.trim() ?? '']))
})
}

export const CsvImportModal = (props: CsvImportModalProps): React.JSX.Element => {
const { open, columns, value, onConfirm, onCancel } = props
const { t } = useTranslation()
const [csvText, setCsvText] = useState('')

const colKeys = getColumnKeys(columns)

useEffect(() => {
if (open) {
setCsvText(rowsToCsv(value, colKeys))
}
}, [open])

const handleOk = (): void => {
const newRows = csvToRows(csvText, colKeys)
onConfirm(newRows)
}

return (
<Modal
cancelText={ t('operational-grid.csv-import.cancel') }
okText={ t('operational-grid.csv-import.ok') }
onCancel={ onCancel }
onOk={ handleOk }
open={ open }
title={ t('operational-grid.csv-import.title') }
>
<p>{t('operational-grid.csv-import.description')}</p>
<Input.TextArea
onChange={ (e) => { setCsvText(e.target.value) } }
rows={ 10 }
value={ csvText }
/>
</Modal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import { useOperationalGridContext } from '../provider/operational-grid-provider'
import { type ColumnDef } from '@tanstack/react-table'
import { isNil } from 'lodash'
import React, { useState } from 'react'
import { CsvImportModal } from '../csv-import-modal/csv-import-modal'

export interface UseOperationsReturn {
addRow: (data?: any) => void
Expand All @@ -21,13 +23,16 @@ export interface UseOperationsReturn {
addColumn: (column: ColumnDef<any>, defaultValue?: any) => void
removeColumn: (columnId: string) => void
updateColumn: (columnId: string, updatedColumn: ColumnDef<any>) => void
openCsvImport?: () => void
csvImportModal: React.JSX.Element | null
}

export const useOperations = (): UseOperationsReturn => {
const { value, onChange, finalGridProps, columns, onColumnsChange } = useOperationalGridContext()
const { value, onChange, finalGridProps, columns, onColumnsChange, disableCsvImport } = useOperationalGridContext()
const selectedRows = finalGridProps.selectedRows
const onSelectedRowsChange = finalGridProps.onSelectedRowsChange
const setRowId = finalGridProps.setRowId
const [isCsvImportOpen, setIsCsvImportOpen] = useState(false)

const getRowId = (row: any, index: number): string => {
if (!isNil(setRowId)) {
Expand Down Expand Up @@ -122,6 +127,33 @@ export const useOperations = (): UseOperationsReturn => {
onColumnsChange?.(newColumns)
}

const handleCsvImportConfirm = (newRows: any[]): void => {
onChange?.(newRows)
setIsCsvImportOpen(false)
}

const handleCsvImportCancel = (): void => {
setIsCsvImportOpen(false)
}

const csvEnabled = disableCsvImport !== true

const openCsvImport = csvEnabled
? (): void => { setIsCsvImportOpen(true) }
: undefined

const csvImportModal = csvEnabled
? (
<CsvImportModal
columns={ columns }
onCancel={ handleCsvImportCancel }
onConfirm={ handleCsvImportConfirm }
open={ isCsvImportOpen }
value={ value }
/>
)
: null

return {
addRow,
clearAll,
Expand All @@ -130,6 +162,8 @@ export const useOperations = (): UseOperationsReturn => {
getSelectedRowsData,
addColumn,
removeColumn,
updateColumn
updateColumn,
openCsvImport,
csvImportModal
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ const config: Meta = {
</IconButton>
</>
)}
{operations.openCsvImport !== undefined && (
<IconButton
icon={ { value: 'edit-pen' } }
onClick={ operations.openCsvImport }
>
Import CSV
</IconButton>
)}
{operations.csvImportModal}
</Space>
)
}}
Expand Down Expand Up @@ -322,6 +331,15 @@ export const WithColumnOperations = {
</IconButton>
</Space>
</div>
{operations.openCsvImport !== undefined && (
<IconButton
icon={ { value: 'edit-pen' } }
onClick={ operations.openCsvImport }
>
Import CSV
</IconButton>
)}
{operations.csvImportModal}
</Space>
)
}}
Expand Down Expand Up @@ -437,6 +455,15 @@ export const WithDragAndDrop = {
Reset Order
</IconButton>
</div>
{operations.openCsvImport !== undefined && (
<IconButton
icon={ { value: 'edit-pen' } }
onClick={ operations.openCsvImport }
>
Import CSV
</IconButton>
)}
{operations.csvImportModal}
</Space>
)
}}
Expand All @@ -450,3 +477,17 @@ export const WithDragAndDrop = {
return <ComponentWrapper />
}
}

export const WithCsvImportDisabled = {
args: {
value: initialData,
columns,
enableSorting: true,
enableRowSelection: true,
enableMultipleRowSelection: true,
disableCsvImport: true,
onChange: (value: Item[]) => {
console.log('Data changed:', value)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ export interface OperationalGridProps extends Omit<GridProps, 'data' | 'onUpdate
onColumnsChange?: (columns: Array<ColumnDef<any>>) => void
children: React.ReactNode
onUpdateCellData?: GridProps['onUpdateCellData']
disableCsvImport?: boolean
}

const OperationalGrid = (props: OperationalGridProps): React.JSX.Element => {
const { value = [], onChange, onColumnsChange, children, onUpdateCellData, columns = [], ...gridProps } = props
const { value = [], onChange, onColumnsChange, children, onUpdateCellData, columns = [], disableCsvImport, ...gridProps } = props

const defaultOnUpdateCellData: GridProps['onUpdateCellData'] = (event) => {
const { columnId, rowIndex, value: newCellValue } = event
Expand All @@ -44,6 +45,7 @@ const OperationalGrid = (props: OperationalGridProps): React.JSX.Element => {
return (
<OperationalGridProvider
columns={ columns }
disableCsvImport={ disableCsvImport }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Randomly came across this one. It feels a bit strange to see these changes directly in the operational grid. Can't we just an button that then consumes the necessary information from the context instead?

At least I wouldn't change the behavior of the operational grid here, since it does not feel like something that is directly connected to the API of the grid.

finalGridProps={ finalGridProps }
onChange={ onChange }
onColumnsChange={ onColumnsChange }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface IOperationalGridContext {
columns: Array<ColumnDef<any>>
onColumnsChange?: (columns: Array<ColumnDef<any>>) => void
finalGridProps: GridProps
disableCsvImport?: boolean
}

export const OperationalGridContext = createContext<IOperationalGridContext | undefined>(undefined)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ export const SelectOptionEntriesGrid = ({ value = [], onChange }: SelectOptionEn
tooltip={ { title: t('add') } }
type="default"
/>
{operations.openCsvImport !== undefined && (
<IconButton
icon={ { value: 'edit-pen' } }
onClick={ operations.openCsvImport }
tooltip={ { title: t('operational-grid.csv-import.title') } }
type="default"
/>
)}
{operations.csvImportModal}
</Space>
)
}}
Expand Down
23 changes: 23 additions & 0 deletions public/build/0e160efe-80f6-4ebe-bf5a-b8495300e89b/entrypoints.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading