From ee585d31ebdbc5c084ba012bdec213f9bcef339c Mon Sep 17 00:00:00 2001 From: kat Date: Thu, 17 Sep 2026 12:52:09 +0100 Subject: [PATCH] feat: add column sorting in table header --- admin/src/components/ConfigList/index.jsx | 60 +++++++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/admin/src/components/ConfigList/index.jsx b/admin/src/components/ConfigList/index.jsx index 7b0202a..812d317 100644 --- a/admin/src/components/ConfigList/index.jsx +++ b/admin/src/components/ConfigList/index.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { isEmpty } from 'lodash'; import { useDispatch } from 'react-redux'; @@ -12,7 +12,9 @@ import { Typography, Checkbox, Loader, + Flex, } from '@strapi/design-system'; +import { CaretDown, CaretUp } from '@strapi/icons'; import ConfigDiff from '../ConfigDiff'; import FirstExport from '../FirstExport'; @@ -27,9 +29,20 @@ const ConfigList = ({ diff, isLoading }) => { const [cName, setCname] = useState(''); const [rows, setRows] = useState([]); const [checkedItems, setCheckedItems] = useState([]); + const [sortBy, setSortBy] = useState('configName'); + const [sortOrder, setSortOrder] = useState('asc'); const dispatch = useDispatch(); const { formatMessage } = useIntl(); + const handleSort = (field) => { + if (sortBy === field) { + setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); + } else { + setSortBy(field); + setSortOrder('asc'); + } + }; + const getConfigState = (configName) => { if ( diff.fileConfig[configName] @@ -87,6 +100,19 @@ const ConfigList = ({ diff, isLoading }) => { dispatch(setConfigPartialDiffInState(newPartialDiff)); }, [checkedItems]); + const sortedRows = useMemo(() => { + const rowsWithIndex = rows.map((row, originalIndex) => ({ ...row, originalIndex })); + + return rowsWithIndex.sort((a, b) => { + const aValue = (a[sortBy] || '').toLowerCase(); + const bValue = (b[sortBy] || '').toLowerCase(); + + if (aValue < bValue) return sortOrder === 'asc' ? -1 : 1; + if (aValue > bValue) return sortOrder === 'asc' ? 1 : -1; + return 0; + }); + }, [rows, sortBy, sortOrder]); + if (isLoading) { return (
@@ -106,6 +132,11 @@ const ConfigList = ({ diff, isLoading }) => { const allChecked = checkedItems && checkedItems.every(Boolean); const isIndeterminate = checkedItems.some(Boolean) && !allChecked; + const renderSortIcon = (field) => { + if (sortBy !== field) return null; + return sortOrder === 'asc' ? : ; + }; + return (
@@ -118,19 +149,28 @@ const ConfigList = ({ diff, isLoading }) => { onCheckedChange={(value) => setCheckedItems(checkedItems.map(() => value))} /> - - - - {rows.map((row, index) => ( + {sortedRows.map((row) => ( { trigger={( { - checkedItems[index] = !checkedItems[index]; + checkedItems[row.originalIndex] = !checkedItems[row.originalIndex]; setCheckedItems([...checkedItems]); }} />
- {formatMessage({ id: 'config-sync.ConfigList.ConfigName' })} + handleSort('configName')} style={{ cursor: 'pointer' }}> + + {formatMessage({ id: 'config-sync.ConfigList.ConfigName' })} + {renderSortIcon('configName')} + - {formatMessage({ id: 'config-sync.ConfigList.ConfigType' })} + handleSort('configType')} style={{ cursor: 'pointer' }}> + + {formatMessage({ id: 'config-sync.ConfigList.ConfigType' })} + {renderSortIcon('configType')} + - {formatMessage({ id: 'config-sync.ConfigList.State' })} + handleSort('state')} style={{ cursor: 'pointer' }}> + + {formatMessage({ id: 'config-sync.ConfigList.State' })} + {renderSortIcon('state')} +