From 18efe90d0d0a3b71374b204cfb58915d9ae0a6e2 Mon Sep 17 00:00:00 2001 From: Andrewakiv Date: Tue, 17 Dec 2024 15:18:52 +0200 Subject: [PATCH 1/4] fixed search for users table --- .../AdminPage/UserProfilesTable/UserTable.jsx | 402 ++++++++++++++++-- .../UserProfilesTable/UserTable.module.scss | 2 +- .../pages/CustomThemes/customAdminTheme.js | 5 +- 3 files changed, 363 insertions(+), 46 deletions(-) diff --git a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx index 760185e1..4cc90c1f 100644 --- a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx +++ b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx @@ -1,4 +1,298 @@ -import { useState, useEffect } from 'react'; +// import { useState, useEffect } from 'react'; +// import { useNavigate, useLocation } from 'react-router-dom'; +// import Highlighter from 'react-highlight-words'; +// import axios from 'axios'; +// import useSWR, { mutate }from 'swr'; +// import { Table, Tag, Tooltip, Pagination, Input, Button, Space } from 'antd'; +// import { CaretUpOutlined, CaretDownOutlined, SearchOutlined } from '@ant-design/icons'; +// import css from './UserTable.module.scss'; +// import UserActions from './UserActions'; + + +// const LENGTH_EMAIL = 14; +// const DEFAULT_PAGE_SIZE = 10; + +// function UserTable() { +// const location = useLocation(); +// const navigate = useNavigate(); +// const queryParams = new URLSearchParams(location.search); +// const pageNumber = Number(queryParams.get('page')) || 1; +// const [currentPage, setCurrentPage] = useState(pageNumber); +// const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); +// const [sortInfo, setSortInfo] = useState({ field: null, order: null }); +// const [statusFilters, setStatusFilters] = useState([]); +// const [searchText, setSearchText] = useState(''); +// const [searchedColumn, setSearchedColumn] = useState(''); + +// useEffect(() => { +// const queryParams = new URLSearchParams(location.search); +// const updatedPageNumber = Number(queryParams.get('page')) || 1; +// setCurrentPage(updatedPageNumber); +// }, [location.search]); + +// const ordering = sortInfo.field ? `&ordering=${sortInfo.order === 'ascend' ? sortInfo.field : '-' + sortInfo.field}` : ''; +// const filtering = statusFilters ? statusFilters.map((filter) => `&${filter}=true`).join('') : ''; +// const url = `${process.env.REACT_APP_BASE_API_URL}/api/admin/users?page=${currentPage}&page_size=${pageSize}${ordering}${filtering}`; + +// async function fetcher(url) { +// const response = await axios.get(url); +// return response.data; +// } +// const { data, isValidating: loading } = useSWR(url, fetcher); +// const users = data ? data.results : []; +// const totalItems = data ? data.total_items : 0; + +// const updateQueryParams = (newPage) => { +// queryParams.set('page', newPage); +// navigate(`?${queryParams.toString()}`); +// }; + +// const handlePageChange = (page, size) => { +// setCurrentPage(page); +// setPageSize(size); +// updateQueryParams(page); +// }; + +// const handleTableChange = (pagination, filters, sorter) => { +// if (sorter.field) { +// const newSortInfo = +// sorter.order === null || sorter.order === undefined +// ? { field: null, order: null } +// : { field: sorter.field, order: sorter.order }; + +// setSortInfo(newSortInfo); +// } else { +// setSortInfo({ field: null, order: null }); +// } + +// setStatusFilters(filters.status); +// setCurrentPage(1); +// updateQueryParams(1); +// }; + +// const getSortIcon = (sortOrder) => { +// if (!sortOrder) return ; +// return sortOrder === 'ascend' ? ( +// +// ) : ( +// +// ); +// }; +// const handleSearch = (selectedKeys, confirm, dataIndex) => { +// confirm(); +// setSearchText(selectedKeys[0]); +// setSearchedColumn(dataIndex); +// }; +// const handleReset = (clearFilters) => { +// clearFilters(); +// setSearchText(''); +// }; +// const getColumnSearchProps = (dataIndex) => ({ +// filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => ( +//
+// setSelectedKeys(e.target.value ? [e.target.value]: [])} +// onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} +// className={css['antInput']} +// > +// +// +// +// +//
+// ), +// filterIcon: (filtered) => , +// onFilter: (value, record) => +// record[dataIndex]?.toString().toLowerCase().includes(value.toLowerCase()), +// render: (text) => +// searchedColumn === dataIndex ? ( +// +// ) : ( +// text +// ), +// }); + +// const renderStatusTags = (status) => { +// const tags = []; + +// if (status.is_active) { +// tags.push(Активний); +// } +// if (status.is_staff) { +// tags.push(Адміністратор); +// } +// if (status.is_superuser) { +// tags.push(Суперадмін); +// } +// if (status.is_deleted) { +// tags.push(Видалений); +// } + +// return <>{tags}; +// }; + +// const columns = [ +// { +// title: 'Прізвище', +// dataIndex: 'surname', +// key: 'surname', +// sorter: true, +// sortOrder: sortInfo.field === 'surname' ? sortInfo.order : null, +// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), +// render: (_, record) => `${record.surname}`, +// ...getColumnSearchProps('surname'), +// }, +// { +// title: 'Ім\'я', +// dataIndex: 'name', +// key: 'name', +// sorter: true, +// sortOrder: sortInfo.field === 'name' ? sortInfo.order : null, +// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), +// render: (_, record) => `${record.name}`, +// ...getColumnSearchProps('name'), +// }, +// { +// title: 'Email', +// dataIndex: 'email', +// key: 'email', +// sorter: true, +// sortOrder: sortInfo.field === 'email' ? sortInfo.order : null, +// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), +// render: (email) => ( +//

+// {email.length > LENGTH_EMAIL ? ( +// +// {`${email.slice(0, LENGTH_EMAIL)}...`} +// +// ) : ( +// {email} +// )} +//

+// ), +// ...getColumnSearchProps('email'), +// }, +// { +// title: 'Компанія', +// dataIndex: 'company_name', +// key: 'company_name', +// sorter: true, +// sortOrder: sortInfo.field === 'company_name' ? sortInfo.order : null, +// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), +// render: (company_name) => ( +//

+// {company_name && company_name.length > LENGTH_EMAIL ? ( +// +// {`${company_name.slice(0, LENGTH_EMAIL)}...`} +// +// ) : ( +// {company_name} +// )} +//

+// ), +// ...getColumnSearchProps('company_name'), +// }, +// { +// title: 'Статус', +// dataIndex: 'status', +// key: 'status', +// render: (status) => renderStatusTags(status), +// filters: [ +// { text: 'Aктивні', value: 'is_active' }, +// { text: 'Aдміністратори', value: 'is_staff' }, +// { text: 'Суперюзер', value: 'is_superuser' }, +// { text: 'Видалені', value: 'is_deleted' }, +// ], +// onFilter: (value) => value +// }, +// { +// title: 'Дата реєстрації', +// dataIndex: 'registration_date', +// key: 'registration_date', +// sorter: true, +// sortOrder: sortInfo.field === 'registration_date' ? sortInfo.order : null, +// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), +// ...getColumnSearchProps('registration_date'), +// }, +// { +// title: 'Дії', +// dataIndex: 'actions', +// key: 'actions', +// render: (_, user) => ( +// { +// mutate(url); +// }} +// /> +// ), +// }, +// ]; + +// return ( +//
+// +// +// +// +// ); +// } + +// export default UserTable; + + +import { useState, useEffect, useRef } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import Highlighter from 'react-highlight-words'; import axios from 'axios'; @@ -21,8 +315,8 @@ function UserTable() { const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); const [sortInfo, setSortInfo] = useState({ field: null, order: null }); const [statusFilters, setStatusFilters] = useState([]); - const [searchText, setSearchText] = useState(''); - const [searchedColumn, setSearchedColumn] = useState(''); + const [searchParams, setSearchParams] = useState({}); + const searchInput = useRef(null); useEffect(() => { const queryParams = new URLSearchParams(location.search); @@ -32,7 +326,8 @@ function UserTable() { const ordering = sortInfo.field ? `&ordering=${sortInfo.order === 'ascend' ? sortInfo.field : '-' + sortInfo.field}` : ''; const filtering = statusFilters ? statusFilters.map((filter) => `&${filter}=true`).join('') : ''; - const url = `${process.env.REACT_APP_BASE_API_URL}/api/admin/users?page=${currentPage}&page_size=${pageSize}${ordering}${filtering}`; + const query = new URLSearchParams(searchParams).toString(); + const url = `${process.env.REACT_APP_BASE_API_URL}/api/admin/users?page=${currentPage}&page_size=${pageSize}${ordering}${filtering}&${query}`; async function fetcher(url) { const response = await axios.get(url); @@ -78,59 +373,78 @@ function UserTable() { ); }; + const handleSearch = (selectedKeys, confirm, dataIndex) => { confirm(); - setSearchText(selectedKeys[0]); - setSearchedColumn(dataIndex); - }; - const handleReset = (clearFilters) => { + + if (selectedKeys[0]) { + setSearchParams((prev) => ({ ...prev, [dataIndex]: selectedKeys[0] })); + } else { + setSearchParams((prev) => { + const updatedParams = { ...prev }; + delete updatedParams[dataIndex]; + return updatedParams; + }); + } + }; + + const handleReset = (clearFilters, confirm, dataIndex) => { clearFilters(); - setSearchText(''); + setSearchParams((prev) => { + const updatedParams = { ...prev }; + delete updatedParams[dataIndex]; + return updatedParams; + }); + confirm(); }; + const getColumnSearchProps = (dataIndex) => ({ - filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => ( -
- setSelectedKeys(e.target.value ? [e.target.value]: [])} - onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} - className={css['antInput']} - > - - - + filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( +
+ setSelectedKeys(e.target.value ? [e.target.value] : [])} + onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} + className={css['antInput']} + /> + + +
- ), - filterIcon: (filtered) => , - onFilter: (value, record) => - record[dataIndex]?.toString().toLowerCase().includes(value.toLowerCase()), - render: (text) => - searchedColumn === dataIndex ? ( + ), + filterIcon: (filtered) => ( + + ), + render: (text) => { + const searchValue = searchParams[dataIndex] || ''; + return searchValue ? ( ) : ( - text - ), + text + ); + } }); const renderStatusTags = (status) => { diff --git a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.module.scss b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.module.scss index 13882c5b..70d197be 100644 --- a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.module.scss +++ b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.module.scss @@ -42,5 +42,5 @@ .filteredIcon { font-size: 25px; font-weight: bold; - color: #46c636; + color: #1f9a7c; } \ No newline at end of file diff --git a/FrontEnd/src/pages/CustomThemes/customAdminTheme.js b/FrontEnd/src/pages/CustomThemes/customAdminTheme.js index 545c6783..630dca75 100644 --- a/FrontEnd/src/pages/CustomThemes/customAdminTheme.js +++ b/FrontEnd/src/pages/CustomThemes/customAdminTheme.js @@ -5,7 +5,7 @@ const customAdminTheme = { colorPrimaryHover: '#0b6c61', fontWeight: 600, contentFontSize: 16, - fontFamilyCode: 'Inter', + fontFamilyCode: 'Geologica', colorLink: '#1f9a7c', colorLinkActive: '#1f9a7c', colorLinkHover: '#0b6c61' @@ -32,6 +32,9 @@ const customAdminTheme = { }, token: { fontFamily: 'Geologica', + colorPrimary: '#1f9a7c', + colorPrimaryHover: '#1f9a7c', + controlOutline: 'rgba(31, 154, 124, 0.4)' } }; From be9ef8b4b11cd6b664d765dd5e0e49b4bcf1d7de Mon Sep 17 00:00:00 2001 From: Andrewakiv Date: Tue, 17 Dec 2024 15:29:21 +0200 Subject: [PATCH 2/4] deleted comments --- .../AdminPage/UserProfilesTable/UserTable.jsx | 294 ------------------ 1 file changed, 294 deletions(-) diff --git a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx index 4cc90c1f..ff1bfcbd 100644 --- a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx +++ b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx @@ -1,297 +1,3 @@ -// import { useState, useEffect } from 'react'; -// import { useNavigate, useLocation } from 'react-router-dom'; -// import Highlighter from 'react-highlight-words'; -// import axios from 'axios'; -// import useSWR, { mutate }from 'swr'; -// import { Table, Tag, Tooltip, Pagination, Input, Button, Space } from 'antd'; -// import { CaretUpOutlined, CaretDownOutlined, SearchOutlined } from '@ant-design/icons'; -// import css from './UserTable.module.scss'; -// import UserActions from './UserActions'; - - -// const LENGTH_EMAIL = 14; -// const DEFAULT_PAGE_SIZE = 10; - -// function UserTable() { -// const location = useLocation(); -// const navigate = useNavigate(); -// const queryParams = new URLSearchParams(location.search); -// const pageNumber = Number(queryParams.get('page')) || 1; -// const [currentPage, setCurrentPage] = useState(pageNumber); -// const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); -// const [sortInfo, setSortInfo] = useState({ field: null, order: null }); -// const [statusFilters, setStatusFilters] = useState([]); -// const [searchText, setSearchText] = useState(''); -// const [searchedColumn, setSearchedColumn] = useState(''); - -// useEffect(() => { -// const queryParams = new URLSearchParams(location.search); -// const updatedPageNumber = Number(queryParams.get('page')) || 1; -// setCurrentPage(updatedPageNumber); -// }, [location.search]); - -// const ordering = sortInfo.field ? `&ordering=${sortInfo.order === 'ascend' ? sortInfo.field : '-' + sortInfo.field}` : ''; -// const filtering = statusFilters ? statusFilters.map((filter) => `&${filter}=true`).join('') : ''; -// const url = `${process.env.REACT_APP_BASE_API_URL}/api/admin/users?page=${currentPage}&page_size=${pageSize}${ordering}${filtering}`; - -// async function fetcher(url) { -// const response = await axios.get(url); -// return response.data; -// } -// const { data, isValidating: loading } = useSWR(url, fetcher); -// const users = data ? data.results : []; -// const totalItems = data ? data.total_items : 0; - -// const updateQueryParams = (newPage) => { -// queryParams.set('page', newPage); -// navigate(`?${queryParams.toString()}`); -// }; - -// const handlePageChange = (page, size) => { -// setCurrentPage(page); -// setPageSize(size); -// updateQueryParams(page); -// }; - -// const handleTableChange = (pagination, filters, sorter) => { -// if (sorter.field) { -// const newSortInfo = -// sorter.order === null || sorter.order === undefined -// ? { field: null, order: null } -// : { field: sorter.field, order: sorter.order }; - -// setSortInfo(newSortInfo); -// } else { -// setSortInfo({ field: null, order: null }); -// } - -// setStatusFilters(filters.status); -// setCurrentPage(1); -// updateQueryParams(1); -// }; - -// const getSortIcon = (sortOrder) => { -// if (!sortOrder) return ; -// return sortOrder === 'ascend' ? ( -// -// ) : ( -// -// ); -// }; -// const handleSearch = (selectedKeys, confirm, dataIndex) => { -// confirm(); -// setSearchText(selectedKeys[0]); -// setSearchedColumn(dataIndex); -// }; -// const handleReset = (clearFilters) => { -// clearFilters(); -// setSearchText(''); -// }; -// const getColumnSearchProps = (dataIndex) => ({ -// filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => ( -//
-// setSelectedKeys(e.target.value ? [e.target.value]: [])} -// onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} -// className={css['antInput']} -// > -// -// -// -// -//
-// ), -// filterIcon: (filtered) => , -// onFilter: (value, record) => -// record[dataIndex]?.toString().toLowerCase().includes(value.toLowerCase()), -// render: (text) => -// searchedColumn === dataIndex ? ( -// -// ) : ( -// text -// ), -// }); - -// const renderStatusTags = (status) => { -// const tags = []; - -// if (status.is_active) { -// tags.push(Активний); -// } -// if (status.is_staff) { -// tags.push(Адміністратор); -// } -// if (status.is_superuser) { -// tags.push(Суперадмін); -// } -// if (status.is_deleted) { -// tags.push(Видалений); -// } - -// return <>{tags}; -// }; - -// const columns = [ -// { -// title: 'Прізвище', -// dataIndex: 'surname', -// key: 'surname', -// sorter: true, -// sortOrder: sortInfo.field === 'surname' ? sortInfo.order : null, -// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), -// render: (_, record) => `${record.surname}`, -// ...getColumnSearchProps('surname'), -// }, -// { -// title: 'Ім\'я', -// dataIndex: 'name', -// key: 'name', -// sorter: true, -// sortOrder: sortInfo.field === 'name' ? sortInfo.order : null, -// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), -// render: (_, record) => `${record.name}`, -// ...getColumnSearchProps('name'), -// }, -// { -// title: 'Email', -// dataIndex: 'email', -// key: 'email', -// sorter: true, -// sortOrder: sortInfo.field === 'email' ? sortInfo.order : null, -// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), -// render: (email) => ( -//

-// {email.length > LENGTH_EMAIL ? ( -// -// {`${email.slice(0, LENGTH_EMAIL)}...`} -// -// ) : ( -// {email} -// )} -//

-// ), -// ...getColumnSearchProps('email'), -// }, -// { -// title: 'Компанія', -// dataIndex: 'company_name', -// key: 'company_name', -// sorter: true, -// sortOrder: sortInfo.field === 'company_name' ? sortInfo.order : null, -// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), -// render: (company_name) => ( -//

-// {company_name && company_name.length > LENGTH_EMAIL ? ( -// -// {`${company_name.slice(0, LENGTH_EMAIL)}...`} -// -// ) : ( -// {company_name} -// )} -//

-// ), -// ...getColumnSearchProps('company_name'), -// }, -// { -// title: 'Статус', -// dataIndex: 'status', -// key: 'status', -// render: (status) => renderStatusTags(status), -// filters: [ -// { text: 'Aктивні', value: 'is_active' }, -// { text: 'Aдміністратори', value: 'is_staff' }, -// { text: 'Суперюзер', value: 'is_superuser' }, -// { text: 'Видалені', value: 'is_deleted' }, -// ], -// onFilter: (value) => value -// }, -// { -// title: 'Дата реєстрації', -// dataIndex: 'registration_date', -// key: 'registration_date', -// sorter: true, -// sortOrder: sortInfo.field === 'registration_date' ? sortInfo.order : null, -// sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), -// ...getColumnSearchProps('registration_date'), -// }, -// { -// title: 'Дії', -// dataIndex: 'actions', -// key: 'actions', -// render: (_, user) => ( -// { -// mutate(url); -// }} -// /> -// ), -// }, -// ]; - -// return ( -//
-// -//
-// -// -// ); -// } - -// export default UserTable; - - import { useState, useEffect, useRef } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import Highlighter from 'react-highlight-words'; From fd289f4eff08c1afa002ecb37ffb9d86534496ff Mon Sep 17 00:00:00 2001 From: Andrewakiv Date: Tue, 17 Dec 2024 22:32:31 +0200 Subject: [PATCH 3/4] removed ref --- FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx index ff1bfcbd..85f81996 100644 --- a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx +++ b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import { useState, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import Highlighter from 'react-highlight-words'; import axios from 'axios'; @@ -22,7 +22,6 @@ function UserTable() { const [sortInfo, setSortInfo] = useState({ field: null, order: null }); const [statusFilters, setStatusFilters] = useState([]); const [searchParams, setSearchParams] = useState({}); - const searchInput = useRef(null); useEffect(() => { const queryParams = new URLSearchParams(location.search); @@ -108,7 +107,6 @@ function UserTable() { filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
setSelectedKeys(e.target.value ? [e.target.value] : [])} From c96dbfbafb30b2a45e1c55a9f685921b88f29479 Mon Sep 17 00:00:00 2001 From: Andrewakiv Date: Thu, 19 Dec 2024 14:23:24 +0200 Subject: [PATCH 4/4] removed search for the date field --- FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx index 85f81996..0a1d4838 100644 --- a/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx +++ b/FrontEnd/src/pages/AdminPage/UserProfilesTable/UserTable.jsx @@ -250,8 +250,7 @@ function UserTable() { key: 'registration_date', sorter: true, sortOrder: sortInfo.field === 'registration_date' ? sortInfo.order : null, - sortIcon: ({ sortOrder }) => getSortIcon(sortOrder), - ...getColumnSearchProps('registration_date'), + sortIcon: ({ sortOrder }) => getSortIcon(sortOrder) }, { title: 'Дії',