параметны в ссылках + фикс пагинации

This commit is contained in:
иосиф брыков 2024-02-29 00:09:19 +05:00
commit 013fe0c082
7 changed files with 157 additions and 115 deletions

8
package-lock.json generated
View File

@ -17,7 +17,7 @@
"styled-components": "^6.1.8" "styled-components": "^6.1.8"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.55", "@types/react": "^18.2.60",
"@types/react-dom": "^18.2.19", "@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
@ -2671,9 +2671,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "18.2.55", "version": "18.2.60",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.60.tgz",
"integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", "integrity": "sha512-dfiPj9+k20jJrLGOu9Nf6eqxm2EyJRrq2NvwOFsfbb7sFExZ9WELPs67UImHj3Ayxg8ruTtKtNnbjaF8olPq0A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",

View File

@ -19,7 +19,7 @@
"styled-components": "^6.1.8" "styled-components": "^6.1.8"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.55", "@types/react": "^18.2.60",
"@types/react-dom": "^18.2.19", "@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",

View File

@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { I_QueryParams } from 'src/services/type' import { I_QueryParams } from 'src/services/type'
import styled, { css } from 'styled-components' import styled, { css } from 'styled-components'
@ -50,8 +51,9 @@ const Menu = styled.div<I_Active>`
left: -30%; left: -30%;
transform: translate(-50% -50%); transform: translate(-50% -50%);
background: ${(props) => props.theme.bg}; background: ${(props) => props.theme.bg};
cursor: pointer;
${(props) => ${(props) =>
props.active props.active
? css` ? css`
display: flex; display: flex;
@ -124,6 +126,8 @@ export default function Pagination({
const menuRef = useRef<HTMLDivElement>(null) const menuRef = useRef<HTMLDivElement>(null)
const navigate = useNavigate()
useEffect(() => { useEffect(() => {
function handleClickOutside(event: MouseEvent) { function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) { if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
@ -140,6 +144,28 @@ export default function Pagination({
const changeRowsPerPage = (value: number) => { const changeRowsPerPage = (value: number) => {
setQueryParams({ ...queryParams, count: value, page: 1 }) setQueryParams({ ...queryParams, count: value, page: 1 })
setMenuState(false) setMenuState(false)
const searchParams = new URLSearchParams(window.location.search)
if (searchParams.has('count')) {
searchParams.delete('count')
}
searchParams.append('count', value.toString())
if (searchParams.has('page')) {
searchParams.delete('page')
}
searchParams.append('page', '1')
navigate(`?${searchParams.toString()}`)
}
const handleChangePage = (pageNum: number) => {
setQueryParams({ ...queryParams, page: pageNum })
const searchParams = new URLSearchParams(window.location.search)
if (searchParams.has('page')) {
searchParams.delete('page')
}
searchParams.append('page', pageNum.toString())
navigate(`?${searchParams.toString()}`)
} }
return ( return (
@ -148,8 +174,7 @@ export default function Pagination({
<Button <Button
active={queryParams.page !== 1} active={queryParams.page !== 1}
onClick={() => { onClick={() => {
queryParams.page !== 1 && queryParams.page !== 1 && handleChangePage(queryParams.page - 1)
setQueryParams({ ...queryParams, page: queryParams.page - 1 })
}} }}
> >
<img src='/src/images/arrow.svg' alt='' /> <img src='/src/images/arrow.svg' alt='' />
@ -160,9 +185,7 @@ export default function Pagination({
<Item <Item
key={item} key={item}
active={item + 1 === queryParams.page} active={item + 1 === queryParams.page}
onClick={() => onClick={() => handleChangePage(item + 1)}
setQueryParams({ ...queryParams, page: item + 1 })
}
> >
{item + 1} {item + 1}
</Item> </Item>
@ -175,9 +198,7 @@ export default function Pagination({
{[...Array(5).keys()].map((item) => ( {[...Array(5).keys()].map((item) => (
<Item <Item
active={item + 1 === queryParams.page} active={item + 1 === queryParams.page}
onClick={() => onClick={() => handleChangePage(item + 1)}
setQueryParams({ ...queryParams, page: item + 1 })
}
> >
{item + 1} {item + 1}
</Item> </Item>
@ -186,10 +207,7 @@ export default function Pagination({
<Item <Item
active={totalCount / queryParams.count === queryParams.page} active={totalCount / queryParams.count === queryParams.page}
onClick={() => onClick={() =>
setQueryParams({ handleChangePage(totalCount / queryParams.count)
...queryParams,
page: totalCount / queryParams.count
})
} }
> >
{totalCount / queryParams.count} {totalCount / queryParams.count}
@ -198,40 +216,25 @@ export default function Pagination({
) : queryParams.page > 4 && ) : queryParams.page > 4 &&
queryParams.page <= totalCount / queryParams.count - 4 ? ( queryParams.page <= totalCount / queryParams.count - 4 ? (
<> <>
<Item <Item active={false} onClick={() => handleChangePage(1)}>
active={false}
onClick={() => setQueryParams({ ...queryParams, page: 1 })}
>
1 1
</Item> </Item>
<Item active={false}>...</Item> <Item active={false}>...</Item>
<Item <Item
active={false} active={false}
onClick={() => onClick={() => handleChangePage(queryParams.page - 1)}
setQueryParams({
...queryParams,
page: queryParams.page - 1
})
}
> >
{queryParams.page - 1} {queryParams.page - 1}
</Item> </Item>
<Item <Item
active={true} active={true}
onClick={() => onClick={() => handleChangePage(queryParams.page)}
setQueryParams({ ...queryParams, page: queryParams.page })
}
> >
{queryParams.page} {queryParams.page}
</Item> </Item>
<Item <Item
active={false} active={false}
onClick={() => onClick={() => handleChangePage(queryParams.page + 1)}
setQueryParams({
...queryParams,
page: queryParams.page + 1
})
}
> >
{queryParams.page + 1} {queryParams.page + 1}
</Item> </Item>
@ -239,10 +242,7 @@ export default function Pagination({
<Item <Item
active={false} active={false}
onClick={() => onClick={() =>
setQueryParams({ handleChangePage(totalCount / queryParams.count)
...queryParams,
page: totalCount / queryParams.count
})
} }
> >
10 10
@ -250,15 +250,7 @@ export default function Pagination({
</> </>
) : ( ) : (
<> <>
<Item <Item active={false} onClick={() => handleChangePage(1)}>
active={false}
onClick={() =>
setQueryParams({
...queryParams,
page: 1
})
}
>
1 1
</Item> </Item>
<Item active={false}>...</Item> <Item active={false}>...</Item>
@ -268,9 +260,7 @@ export default function Pagination({
{item + 1 > totalCount / queryParams.count - 5 ? ( {item + 1 > totalCount / queryParams.count - 5 ? (
<Item <Item
active={item + 1 === queryParams.page} active={item + 1 === queryParams.page}
onClick={() => onClick={() => handleChangePage(item + 1)}
setQueryParams({ ...queryParams, page: item + 1 })
}
> >
{item + 1} {item + 1}
</Item> </Item>
@ -285,10 +275,10 @@ export default function Pagination({
</> </>
)} )}
<Button <Button
active={queryParams.page !== totalCount / queryParams.count} active={queryParams.page !== Math.ceil(totalCount / queryParams.count)}
onClick={() => { onClick={() => {
queryParams.page !== totalCount / queryParams.count && queryParams.page !== totalCount / queryParams.count &&
setQueryParams({ ...queryParams, page: queryParams.page + 1 }) handleChangePage(queryParams.page + 1)
}} }}
> >
<img src='/src/images/arrow.svg' alt='' /> <img src='/src/images/arrow.svg' alt='' />
@ -302,9 +292,6 @@ export default function Pagination({
<img src='/src/images/bottom-arrow.svg' alt='' /> <img src='/src/images/bottom-arrow.svg' alt='' />
</div> </div>
<Menu active={menuState} ref={menuRef}> <Menu active={menuState} ref={menuRef}>
{/* for test */}
<p onClick={() => changeRowsPerPage(1)}>1</p>
<p onClick={() => changeRowsPerPage(5)}>5</p> <p onClick={() => changeRowsPerPage(5)}>5</p>
<p onClick={() => changeRowsPerPage(10)}>10</p> <p onClick={() => changeRowsPerPage(10)}>10</p>
<p onClick={() => changeRowsPerPage(15)}>15</p> <p onClick={() => changeRowsPerPage(15)}>15</p>

View File

@ -1,4 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import MainButton from 'src/components/UI/button/MainButton' import MainButton from 'src/components/UI/button/MainButton'
import SearchInput from 'src/components/UI/input/SearchInput' import SearchInput from 'src/components/UI/input/SearchInput'
import Loader from 'src/components/UI/loader/Loader' import Loader from 'src/components/UI/loader/Loader'
@ -35,8 +36,25 @@ export default function Store({}: Props) {
order: undefined order: undefined
}) })
const location = useLocation()
const searchParams = new URLSearchParams(location.search)
const navigate = useNavigate()
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQueryParams({ ...queryParams, search: e.target.value }) const newSearchQuery = e.target.value
setQueryParams({ ...queryParams, search: e.target.value, page: 1 })
const searchParams = new URLSearchParams(window.location.search)
if (searchParams.has('page')) {
searchParams.delete('page')
}
searchParams.append('page', '1')
if (searchParams.has('search')) {
searchParams.delete('search')
}
if (newSearchQuery.trim() !== '') {
searchParams.append('search', newSearchQuery.trim())
}
navigate(`?${searchParams.toString()}`)
} }
const getApps = async () => { const getApps = async () => {
@ -59,6 +77,17 @@ export default function Store({}: Props) {
setFirstLoadingState(false) setFirstLoadingState(false)
} }
useEffect(() => { useEffect(() => {
const pageParam = searchParams.get('page')
const countParam = searchParams.get('count')
const searchParam = searchParams.get('search')
const orderParam = searchParams.get('order')
setQueryParams((prevState) => ({
...prevState,
page: pageParam ? parseInt(pageParam, 10) : prevState.page,
count: countParam ? parseInt(countParam, 10) : prevState.count,
search: searchParam || prevState.search,
order: orderParam || prevState.order
}))
getApps() getApps()
}, []) }, [])
@ -92,17 +121,14 @@ export default function Store({}: Props) {
const [modal, setModal] = useState(false) const [modal, setModal] = useState(false)
const groupDelete = async () => { const groupDelete = async () => {
const Promises = checkedRows.map((item) => const Promises = checkedRows.map((item) => StoreService.delete(item))
StoreService.delete(item)
)
await Promise.all(Promises) await Promise.all(Promises)
getApps() getApps()
} }
const groupStart = async () => { const groupStart = async () => {
const Promises = checkedRows.map((item) => const Promises = checkedRows.map((item) =>
StoreService.interaction('enable', item) StoreService.interaction('enable', item)
) )
await Promise.all(Promises) await Promise.all(Promises)
@ -111,7 +137,7 @@ export default function Store({}: Props) {
const groupStop = async () => { const groupStop = async () => {
const Promises = checkedRows.map((item) => const Promises = checkedRows.map((item) =>
StoreService.interaction('disable', item) StoreService.interaction('disable', item)
) )
await Promise.all(Promises) await Promise.all(Promises)
@ -133,7 +159,7 @@ export default function Store({}: Props) {
{Apps.length === 0 && queryParams.search === '' ? ( {Apps.length === 0 && queryParams.search === '' ? (
<StoreNotification> <StoreNotification>
<h2>Список приложений пуст</h2> <h2>Список приложений пуст</h2>
<MainButton onClick={() => setModal(true)}> <MainButton onClick={() => setModal(true)}>
Добавить приложение Добавить приложение
</MainButton> </MainButton>
</StoreNotification> </StoreNotification>
@ -156,9 +182,9 @@ export default function Store({}: Props) {
<StoreSelectNotification> <StoreSelectNotification>
<h3> <h3>
{selectAll {selectAll
? `Выбранно: ${totalCount - notChecked.length}` ? `Выбрано: ${totalCount - notChecked.length}`
: checkedRows.length !== 0 && : checkedRows.length !== 0 &&
`Выбранно: ${checkedRows.length}`} `Выбрано: ${checkedRows.length}`}
</h3> </h3>
<div className='menu'> <div className='menu'>
<img <img

View File

@ -6,7 +6,7 @@ import Loader from 'src/components/UI/loader/Loader'
import Modal from 'src/components/UI/modal/Modal' import Modal from 'src/components/UI/modal/Modal'
import { HOST_NAME } from 'src/constants/host' import { HOST_NAME } from 'src/constants/host'
import StoreService from 'src/services/storeServices' import StoreService from 'src/services/storeServices'
import { T_App, T_NewApp } from '../../type' import { T_App, T_NewApp } from '../type'
import { import {
LoaderContainer, LoaderContainer,
NewAppButtonBlock, NewAppButtonBlock,
@ -16,7 +16,7 @@ import {
NewAppIconBlock, NewAppIconBlock,
NewAppModalContainer, NewAppModalContainer,
NewAppScreenshotsBlock NewAppScreenshotsBlock
} from '../newAppModal/styles' } from './newAppModal/styles'
type Props = { type Props = {
modal: boolean modal: boolean
@ -103,7 +103,7 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
) )
setSelectedImage(app.IconUrl) setSelectedImage(app.IconUrl)
setSelectedFile(app.Url) setSelectedFile(app.Url)
}, []) }, [modal])
// Иконка // Иконка
const handleImageChange = () => { const handleImageChange = () => {
@ -155,11 +155,6 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
screenshotsInputRef.current?.click() screenshotsInputRef.current?.click()
} }
useEffect(() => {
console.log(selectedScreenshots)
console.log(deleteScreenshots)
}, [selectedScreenshots])
// Файл // Файл
const handleFileChange = () => { const handleFileChange = () => {
const file = fileInputRef.current?.files?.[0] const file = fileInputRef.current?.files?.[0]
@ -173,7 +168,7 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
} }
const updateApp = async () => { const updateApp = async () => {
setLoading(true) setLoading(true)
try { try {
// Данные // Данные
if ( if (
@ -188,7 +183,7 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
const response = await StoreService.editData(app.Id, editApp) const response = await StoreService.editData(app.Id, editApp)
if (response.status === 406) { if (response.status === 406) {
setError({ state: true, message: response.data }) setError({ state: true, message: response.data })
setLoading(false) setLoading(false)
return return
} }
} }
@ -199,7 +194,7 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
(await StoreService.addIcon(selectedImage, app.Id)) (await StoreService.addIcon(selectedImage, app.Id))
if (response && response.status === 406) { if (response && response.status === 406) {
setError({ state: true, message: response.data }) setError({ state: true, message: response.data })
setLoading(false) setLoading(false)
return return
} }
} }
@ -227,11 +222,11 @@ export default function EditAppModal({ modal, setModal, app, getApps }: Props) {
const response = await StoreService.addFile(selectedFile, app.Id) const response = await StoreService.addFile(selectedFile, app.Id)
if (response.status === 406) { if (response.status === 406) {
setError({ state: true, message: response.data }) setError({ state: true, message: response.data })
setLoading(false) setLoading(false)
return return
} }
} }
setLoading(false) setLoading(false)
setModal(false) setModal(false)
getApps() getApps()
} catch (error) {} } catch (error) {}

View File

@ -1,12 +1,12 @@
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu'
import Checkbox from 'src/components/UI/input/Checkbox' import Checkbox from 'src/components/UI/input/Checkbox'
import Table from 'src/components/UI/table/Table' import Table from 'src/components/UI/table/Table'
import { T_ColumnsState } from 'src/pages/Registrations/types' import { T_ColumnsState } from 'src/pages/Registrations/types'
import { T_App } from '../type'
import styled, { css } from 'styled-components'
import { I_QueryParams } from 'src/services/type' import { I_QueryParams } from 'src/services/type'
import { useState } from 'react' import styled, { css } from 'styled-components'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu' import { T_App } from '../type'
import App from 'src/App'
import StoreTableItem from './StoreTableItem' import StoreTableItem from './StoreTableItem'
interface I_TableHeaderItem { interface I_TableHeaderItem {
@ -52,10 +52,10 @@ type Props = {
notChecked: number[] notChecked: number[]
setNotChecked: (param: any) => void setNotChecked: (param: any) => void
setSelectAll: (param: any) => void setSelectAll: (param: any) => void
getApps: () => void getApps: () => void
queryParams: I_QueryParams queryParams: I_QueryParams
setQueryParams: (param: I_QueryParams) => void setQueryParams: (param: I_QueryParams) => void
loadingState?: boolean loadingState?: boolean
} }
export default function StoreTable({ export default function StoreTable({
@ -68,35 +68,67 @@ export default function StoreTable({
notChecked, notChecked,
setNotChecked, setNotChecked,
setSelectAll, setSelectAll,
getApps, getApps,
queryParams, queryParams,
setQueryParams, setQueryParams,
loadingState loadingState
}: Props) { }: Props) {
const [contextMenuState, setContextMenuState] = useState(false) const [contextMenuState, setContextMenuState] = useState(false)
const [activeSort, setActiveSort] = useState('') const [activeSort, setActiveSort] = useState('')
const [sortState, setSortState] = useState(false) const [sortState, setSortState] = useState(false)
const navigate = useNavigate()
useEffect(() => {
const searchParams = new URLSearchParams(location.search)
const orderParam = searchParams.get('order')
// Проверяем наличие параметра order в URL
if (orderParam) {
const sortOptions: { [key: string]: string } = {
name: 'Название',
updated: 'Дата обновления',
version: 'Версия',
short: 'Краткое описание',
enabled: 'Состояние'
}
const activeSortValue = sortOptions[orderParam.replace('!', '')]
const sortStateValue = orderParam[0] === '!'
setActiveSort(activeSortValue || '')
setSortState(sortStateValue)
}
}, [])
const sort = (value: string) => { const sort = (value: string) => {
const sortOptions: { [key: string]: string } = { const sortOptions: { [key: string]: string } = {
'Название': 'name', Название: 'name',
'Дата обновления': 'updated', 'Дата обновления': 'updated',
'Версия': 'version', Версия: 'version',
// возможен косяк с short 'Краткое описание': 'short',
'Краткое описание': 'short', Состояние: 'enabled'
'Состояние': 'enabled', }
};
const sortParam = sortOptions[value]; const sortParam = sortOptions[value]
if (!sortParam) return; if (!sortParam) return
setActiveSort(value); setActiveSort(value)
const newOrder = queryParams.order === sortParam ? `!${sortParam}` : sortParam; const newOrder =
setQueryParams({ ...queryParams, order: newOrder }); queryParams.order === sortParam ? `!${sortParam}` : sortParam
setSortState(queryParams.order === sortParam); setQueryParams({ ...queryParams, order: newOrder })
}; setSortState(queryParams.order === sortParam)
const searchParams = new URLSearchParams(window.location.search)
if (searchParams.has('order')) {
searchParams.delete('order')
}
if (newOrder.trim() !== '') {
searchParams.append('order', newOrder.trim())
}
navigate(`?${searchParams.toString()}`)
}
return ( return (
<Table loaderState={loadingState}> <Table loaderState={loadingState}>
@ -115,9 +147,7 @@ export default function StoreTable({
} }
onClick={() => { onClick={() => {
selectAll selectAll
? (setSelectAll(false), ? (setSelectAll(false), setNotChecked([]), setCheckedRows([]))
setNotChecked([]),
setCheckedRows([]))
: checkedRows.length === 0 : checkedRows.length === 0
? (setSelectAll(true), setNotChecked([])) ? (setSelectAll(true), setNotChecked([]))
: setCheckedRows([]) : setCheckedRows([])
@ -179,7 +209,7 @@ export default function StoreTable({
<tbody> <tbody>
{Apps.map((app) => ( {Apps.map((app) => (
<StoreTableItem <StoreTableItem
app={app} app={app}
selectAll={selectAll} selectAll={selectAll}
notChecked={notChecked} notChecked={notChecked}
checkedRows={checkedRows} checkedRows={checkedRows}

View File

@ -9,7 +9,7 @@ import { T_ColumnsState } from 'src/pages/Registrations/types'
import StoreService from 'src/services/storeServices' import StoreService from 'src/services/storeServices'
import styled from 'styled-components' import styled from 'styled-components'
import { T_App } from '../type' import { T_App } from '../type'
import EditAppModal from './editAppModal/EditAppModal' import EditAppModal from './EditAppModal'
type Props = { type Props = {
app: T_App app: T_App
@ -99,7 +99,7 @@ export default function StoreTableItem({
}} }}
/> />
</td> </td>
{Object.entries(activeColumns).map(([key, item]) => {Object.entries(activeColumns).map(([key, _]) =>
activeColumns[key as keyof typeof activeColumns].status ? ( activeColumns[key as keyof typeof activeColumns].status ? (
<td key={key}> <td key={key}>
<p> <p>
@ -170,7 +170,11 @@ export default function StoreTableItem({
<img src='/src/images/reload.svg' alt='' /> <img src='/src/images/reload.svg' alt='' />
<p>Перепаковать</p> <p>Перепаковать</p>
</Item> </Item>
<Item onClick={() => (setEditModalState(true), setContextMenuState(false))}> <Item
onClick={() => (
setEditModalState(true), setContextMenuState(false)
)}
>
<img src='/src/images/edit.svg' alt='' /> <img src='/src/images/edit.svg' alt='' />
<p>Редактировать</p> <p>Редактировать</p>
</Item> </Item>