Исправил все баги и ошибки в репозитории + добавил обрадотку ошибок в регистрации + параметра в ссылках работают на всех страницах

This commit is contained in:
иосиф брыков 2024-02-29 02:29:28 +05:00
parent 013fe0c082
commit 7c247316ae
13 changed files with 535 additions and 317 deletions

View File

@ -6,35 +6,33 @@ export const GlobalStyles = createGlobalStyle`
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
} }
body { body {
font-family: "Roboto"; font-family: "Roboto";
color: ${props => props.theme.textColor}; color: ${(props) => props.theme.textColor};
background: ${props => props.theme.bg}; background: ${(props) => props.theme.bg};
position: relative; position: relative;
min-height: 100vh; min-height: 100vh;
} }
a { a {
text-decoration: none; text-decoration: none;
color: ${props => props.theme.textColor}; color: ${(props) => props.theme.textColor};
} }
h1 { h1 {
${props => props.theme.h1} ${(props) => props.theme.h1}
} }
h2 { h2 {
${props => props.theme.h2} ${(props) => props.theme.h2}
} }
h3 { h3 {
${props => props.theme.h3} ${(props) => props.theme.h3}
} }
p { p {
${props => props.theme.paragraph} ${(props) => props.theme.paragraph}
} }
` `

View File

@ -9,15 +9,16 @@ const TextareaContainer = styled.div`
border-radius: 12px; border-radius: 12px;
background: ${(props) => props.theme.bg}; background: ${(props) => props.theme.bg};
border: 1px solid ${(props) => props.theme.text}; border: 1px solid ${(props) => props.theme.text};
font-size: 18px;
font-weight: 500;
outline: none; outline: none;
resize: none; resize: none;
height: 110px; height: 110px;
width: 100%; width: 100%;
overflow: -moz-scrollbars-none; overflow: -moz-scrollbars-none;
scrollbar-width: none; scrollbar-width: none;
} font-family: Roboto;
font-size: 18px;
font-weight: 400;
}
` `
interface PlaceholderProps { interface PlaceholderProps {

View File

@ -15,6 +15,7 @@ import {
SearchNotification SearchNotification
} from './styles' } from './styles'
import { T_ColumnsState, T_NewRegistration, T_RegistrationItem } from './types' import { T_ColumnsState, T_NewRegistration, T_RegistrationItem } from './types'
import { useLocation, useNavigate } from 'react-router-dom'
type Props = {} type Props = {}
@ -26,17 +27,36 @@ export default function Registrations({}: Props) {
const [registrations, setRegistrations] = useState<T_RegistrationItem[]>([]) const [registrations, setRegistrations] = useState<T_RegistrationItem[]>([])
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => { const location = useLocation()
setQueryParams({ ...queryParams, search: e.target.value }) const searchParams = new URLSearchParams(location.search)
} const navigate = useNavigate()
const [queryParams, setQueryParams] = useState<I_QueryParams>({ const [queryParams, setQueryParams] = useState<I_QueryParams>({
page: 1, page: 1,
count: 10, count: 10,
search: '', search: '',
order: undefined order: 'email'
}) })
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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 getReg = async () => { const getReg = async () => {
setSearchNotification(false) setSearchNotification(false)
setErrorState(false) setErrorState(false)
@ -54,6 +74,21 @@ export default function Registrations({}: Props) {
setLoadingState(false) setLoadingState(false)
} }
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
}))
getReg()
}, [])
useEffect(() => { useEffect(() => {
getReg() getReg()
}, [queryParams]) }, [queryParams])
@ -66,7 +101,7 @@ export default function Registrations({}: Props) {
EndDate: { name: 'Дата окончания', status: true }, EndDate: { name: 'Дата окончания', status: true },
CreatedDate: { name: 'Дата создания', status: false }, CreatedDate: { name: 'Дата создания', status: false },
UpdatedDate: { name: 'Дата изменения', status: true }, UpdatedDate: { name: 'Дата изменения', status: true },
Enabled: { name: 'Статус', status: true } Enabled: { name: 'Состояние', status: true }
}) })
const [checkedColumns, setCheckedColumns] = useState<number[]>([]) const [checkedColumns, setCheckedColumns] = useState<number[]>([])
@ -127,10 +162,6 @@ export default function Registrations({}: Props) {
}) })
}, [modal]) }, [modal])
useEffect(() => {
getReg()
}, [])
return ( return (
<> <>
{loadingState ? ( {loadingState ? (
@ -167,9 +198,9 @@ export default function Registrations({}: Props) {
<RegSelectNotification> <RegSelectNotification>
<h3> <h3>
{selectAll {selectAll
? `Выбранно: ${totalCount - notChecked.length}` ? `Выбрано: ${totalCount - notChecked.length}`
: checkedColumns.length !== 0 && : checkedColumns.length !== 0 &&
`Выбранно: ${checkedColumns.length}`} `Выбрано: ${checkedColumns.length}`}
</h3> </h3>
<div className='menu'> <div className='menu'>
<img <img

View File

@ -2,14 +2,15 @@ import { useEffect, useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton' import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput' import MainInput from 'src/components/UI/input/MainInput'
import Modal from 'src/components/UI/modal/Modal' import Modal from 'src/components/UI/modal/Modal'
import RegistrationsService from 'src/services/registrationsServices'
import styled from 'styled-components' import styled from 'styled-components'
import { T_NewRegistration } from '../types' import { T_RegistrationItem } from '../types'
type Props = { type Props = {
modal: boolean modal: boolean
setModal: (param: boolean) => void setModal: (param: boolean) => void
registration: T_NewRegistration registration: T_RegistrationItem
editReg: (param: T_NewRegistration) => void getReg: () => void
} }
const ModalContainer = styled.div` const ModalContainer = styled.div`
@ -24,16 +25,45 @@ const ModalContainer = styled.div`
} }
` `
const ErrorContainer = styled.div`
position: relative;
z-index: 4;
.content {
display: flex;
flex-direction: column;
align-items: end;
width:420px;
gap: 20px;
}
`
export default function EditRegModal({ export default function EditRegModal({
modal, modal,
setModal, setModal,
registration, registration,
editReg getReg
}: Props) { }: Props) {
const [editRegData, setEditRegData] = useState(registration) const [editRegData, setEditRegData] = useState(registration)
const [errorMessage, setErrorMessage] = useState(false) const [errorMessage, setErrorMessage] = useState(false)
const editReg = async () => {
try {
const response = await RegistrationsService.editRegistration(
registration.Id,
editRegData
)
if (response.status === 200) {
getReg()
setModal(false)
} else if (response.status === 406) {
console.log(1)
setErrorMessage(true)
}
} catch (error) {}
}
const handleContNumChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleContNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...editRegData, ContNum: e.target.value }) setEditRegData({ ...editRegData, ContNum: e.target.value })
} }
@ -44,12 +74,24 @@ export default function EditRegModal({
setEditRegData({ ...editRegData, Email: e.target.value }) setEditRegData({ ...editRegData, Email: e.target.value })
} }
const handleCountChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleCountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...editRegData, Count: e.target.value }) setEditRegData({ ...editRegData, Count: parseInt(e.target.value) })
} }
const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...editRegData, EndDate: e.target.value }) setEditRegData({ ...editRegData, EndDate: e.target.value })
} }
useEffect(() => {
function formatDate(isoDateString: string): string {
const date = new Date(isoDateString)
const year = date.getFullYear()
const month = ('0' + (date.getMonth() + 1)).slice(-2)
const day = ('0' + date.getDate()).slice(-2)
return `${year}-${month}-${day}`
}
const formattedDate = formatDate(registration.EndDate)
setEditRegData({ ...registration, EndDate: formattedDate })
}, [modal])
useEffect(() => { useEffect(() => {
function formatDate(isoDateString: string): string { function formatDate(isoDateString: string): string {
const date = new Date(isoDateString) const date = new Date(isoDateString)
@ -63,47 +105,57 @@ export default function EditRegModal({
}, []) }, [])
return ( return (
<Modal modal={modal} setModal={setModal}> <>
<ModalContainer> <Modal modal={modal} setModal={setModal}>
<MainInput <ModalContainer>
placeholder='Номер контракта' <MainInput
value={editRegData.ContNum} placeholder='Номер контракта'
onChange={handleContNumChange} value={editRegData.ContNum}
/> onChange={handleContNumChange}
<MainInput />
placeholder='Номер поставки' <MainInput
value={editRegData.RegNum} placeholder='Номер поставки'
onChange={handleRegNumChange} value={editRegData.RegNum}
/> onChange={handleRegNumChange}
<MainInput />
placeholder='Email' <MainInput
value={editRegData.Email} placeholder='Email'
onChange={handleEmailChange} value={editRegData.Email}
/> onChange={handleEmailChange}
<MainInput />
placeholder='Кол-во активаций' <MainInput
value={editRegData.Count} placeholder='Кол-во активаций'
onChange={handleCountChange} value={editRegData.Count}
/> onChange={handleCountChange}
<MainInput />
placeholder='Дата окончания' <MainInput
value={editRegData.EndDate} placeholder='Дата окончания'
onChange={handleEndDateChange} value={editRegData.EndDate}
type='date' onChange={handleEndDateChange}
/> type='date'
<div className='buttonBlock'> />
<MainButton <div className='buttonBlock'>
color='secondary' <MainButton
fullWidth={true} color='secondary'
onClick={() => setModal(false)} fullWidth={true}
> onClick={() => setModal(false)}
Отмена >
</MainButton> Отмена
<MainButton fullWidth={true} onClick={() => editReg(editRegData)}> </MainButton>
Сохранить <MainButton fullWidth={true} onClick={editReg}>
</MainButton> Сохранить
</div> </MainButton>
</ModalContainer> </div>
</Modal> </ModalContainer>
</Modal>
<ErrorContainer>
<Modal setModal={setErrorMessage} modal={errorMessage}>
<div className="content">
<h3>Номер контракта/поставки уже существует</h3>
<MainButton onClick={() => setErrorMessage(false)}>Ok</MainButton>
</div>
</Modal>
</ErrorContainer>
</>
) )
} }

View File

@ -1,4 +1,5 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu' 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'
@ -71,28 +72,65 @@ export default function RegTable({
}: Props) { }: Props) {
const [contextMenuState, setContextMenuState] = useState(false) const [contextMenuState, setContextMenuState] = useState(false)
const [activeSort, setActiveSort] = useState('') const navigate = useNavigate()
const [activeSort, setActiveSort] = useState('email')
const [sortState, setSortState] = useState(false) const [sortState, setSortState] = useState(false)
const sort = (value: string) => { const sort = (value: string) => {
const sortOptions: { [key: string]: string } = { const sortOptions: { [key: string]: string } = {
'Номер поставки': 'reg', 'Номер поставки': 'reg',
'Номер контракта': 'cont', 'Номер контракта': 'cont',
'Эл. почта': 'email', 'Эл. почта': 'email',
'Кол-во активаций': 'count', 'Кол-во активаций': 'count',
'Дата окончания': 'end', 'Дата окончания': 'end',
'Дата изменения': 'updated', 'Дата изменения': 'updated',
'Статус': '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()}`)
}
useEffect(() => {
const searchParams = new URLSearchParams(location.search)
const orderParam = searchParams.get('order')
// Проверяем наличие параметра order в URL
if (orderParam) {
const sortOptions: { [key: string]: string } = {
reg: 'Номер поставки',
cont: 'Номер контракта',
email: 'Эл. почта',
count: 'Кол-во активаций',
end: 'Дата окончания',
updated: 'Дата изменения',
enabled: 'Состояние',
}
const activeSortValue = sortOptions[orderParam.replace('!', '')]
const sortStateValue = orderParam[0] === '!'
setActiveSort(activeSortValue || '')
setSortState(sortStateValue)
}
}, [])
return ( return (
<Table> <Table>

View File

@ -74,17 +74,7 @@ export default function RrgTableItem ({
} }
} }
const editReg = async (reg: T_NewRegistration) => {
setContextMenuState(false)
const response = await RegistrationsService.editRegistration(
registration.Id,
reg
)
if (response.status === 200) {
getReg()
}
setEditModalState(false)
}
return ( return (
<> <>
@ -116,7 +106,7 @@ export default function RrgTableItem ({
}} }}
/> />
</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>
@ -203,7 +193,7 @@ export default function RrgTableItem ({
modal={editModalState} modal={editModalState}
setModal={setEditModalState} setModal={setEditModalState}
registration={registration} registration={registration}
editReg={editReg} getReg={getReg}
/> />
</tr> </tr>
</> </>

View File

@ -16,6 +16,7 @@ import {
SearchNotification SearchNotification
} from './styles' } from './styles'
import { T_ColumnsState, T_RepositoryItem } from './types' import { T_ColumnsState, T_RepositoryItem } from './types'
import { useLocation, useNavigate } from 'react-router-dom'
type Props = {} type Props = {}
@ -29,9 +30,9 @@ export default function Repository({}: Props) {
const [Repository, setRepository] = useState<T_RepositoryItem[]>([]) const [Repository, setRepository] = useState<T_RepositoryItem[]>([])
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => { const location = useLocation()
setQueryParams({ ...queryParams, search: e.target.value }) const searchParams = new URLSearchParams(location.search)
} const navigate = useNavigate()
const [queryParams, setQueryParams] = useState<I_QueryParams>({ const [queryParams, setQueryParams] = useState<I_QueryParams>({
page: 1, page: 1,
@ -40,6 +41,23 @@ export default function Repository({}: Props) {
order: undefined order: undefined
}) })
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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 getRep = async () => { const getRep = async () => {
if (!firstLoadingState) setLoadingState(true) if (!firstLoadingState) setLoadingState(true)
setErrorState(false) setErrorState(false)
@ -59,7 +77,19 @@ export default function Repository({}: Props) {
setLoadingState(false) setLoadingState(false)
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
}))
getRep() getRep()
}, []) }, [])
@ -73,8 +103,8 @@ export default function Repository({}: Props) {
Description: { name: 'Полное описание', status: true }, Description: { name: 'Полное описание', status: true },
ShortDescription: { name: 'Краткое описание', status: true }, ShortDescription: { name: 'Краткое описание', status: true },
Category: { name: 'Категория', status: true }, Category: { name: 'Категория', status: true },
UpdatedDate: { name: 'Дата изменения', status: true }, UpdatedDate: { name: 'Дата обновления', status: true },
Enabled: { name: 'Статус', status: true } Enabled: { name: 'Состояние', status: true }
}) })
const [checkedColumns, setCheckedColumns] = useState<number[]>([]) const [checkedColumns, setCheckedColumns] = useState<number[]>([])
@ -122,7 +152,7 @@ export default function Repository({}: Props) {
return ( return (
<> <>
{loadingState ? ( {firstLoadingState ? (
<RepLoaderContainer> <RepLoaderContainer>
<Loader /> <Loader />
</RepLoaderContainer> </RepLoaderContainer>
@ -190,6 +220,7 @@ export default function Repository({}: Props) {
getRep={getRep} getRep={getRep}
queryParams={queryParams} queryParams={queryParams}
setQueryParams={setQueryParams} setQueryParams={setQueryParams}
loadingState={loadingState}
/> />
<div className='bottom-menu'> <div className='bottom-menu'>
<Pagination <Pagination
@ -206,7 +237,6 @@ export default function Repository({}: Props) {
</> </>
</> </>
)} )}
</RepContainer> </RepContainer>
)} )}
<NewRepModal modal={modal} setModal={setModal} getRep={getRep} /> <NewRepModal modal={modal} setModal={setModal} getRep={getRep} />

View File

@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton' import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput' import MainInput from 'src/components/UI/input/MainInput'
import Modal from 'src/components/UI/modal/Modal'
import Textarea from 'src/components/UI/input/Textarea' import Textarea from 'src/components/UI/input/Textarea'
import Modal from 'src/components/UI/modal/Modal'
import RepositoryService from 'src/services/repositoryServices' import RepositoryService from 'src/services/repositoryServices'
import styled from 'styled-components' import styled from 'styled-components'
import { T_NewRepository, T_RepositoryItem } from '../types' import { T_NewRepository, T_RepositoryItem } from '../types'
@ -52,7 +52,12 @@ const NewRepFileBlock = styled.div`
font-weight: 600; font-weight: 600;
} }
` `
export default function EditRepModal({ modal, setModal, repository, getRep }: Props){ export default function EditRepModal({
modal,
setModal,
repository,
getRep
}: Props) {
const [error, setError] = useState({ const [error, setError] = useState({
state: false, state: false,
message: '' message: ''
@ -75,10 +80,14 @@ export default function EditRepModal({ modal, setModal, repository, getRep }: Pr
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRepData({ ...editRepData, Name: e.target.value }) setEditRepData({ ...editRepData, Name: e.target.value })
} }
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { const handleDescriptionChange = (
e: React.ChangeEvent<HTMLTextAreaElement>
) => {
setEditRepData({ ...editRepData, Description: e.target.value }) setEditRepData({ ...editRepData, Description: e.target.value })
} }
const handleShortDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleShortDescriptionChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setEditRepData({ ...editRepData, ShortDescription: e.target.value }) setEditRepData({ ...editRepData, ShortDescription: e.target.value })
} }
const handleVersionChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleVersionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -93,15 +102,26 @@ export default function EditRepModal({ modal, setModal, repository, getRep }: Pr
useEffect(() => { useEffect(() => {
setFormValid( setFormValid(
editRepData.Name !== '' && editRepData.Name !== '' &&
editRepData.Description !== '' && editRepData.Description !== '' &&
editRepData.ShortDescription !== '' && editRepData.ShortDescription !== '' &&
editRepData.Version !== '' && editRepData.Version !== '' &&
editRepData.Category !== '' editRepData.Category !== ''
) )
}, [editRepData]) }, [editRepData])
const fileInputRef = useRef<HTMLInputElement>(null);
const [selectedFile, setSelectedFile] = useState<File | null>(null); useEffect(() => {
setEditRepData({
Name: repository.Name,
Description: repository.Description,
ShortDescription: repository.ShortDescription,
Version: repository.Version,
Category: repository.Category
})
}, [modal])
const fileInputRef = useRef<HTMLInputElement>(null)
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const handleFileChange = () => { const handleFileChange = () => {
const file = fileInputRef.current?.files?.[0] const file = fileInputRef.current?.files?.[0]
if (file) { if (file) {
@ -114,70 +134,75 @@ export default function EditRepModal({ modal, setModal, repository, getRep }: Pr
} }
const updateRep = async () => { const updateRep = async () => {
setLoading(true) setLoading(true)
try { try {
if ( if (
!( !(
editRepData.Category === repository.Category && editRepData.Category === repository.Category &&
editRepData.Description === repository.Description && editRepData.Description === repository.Description &&
editRepData.Name === repository.Name && editRepData.Name === repository.Name &&
editRepData.ShortDescription === repository.ShortDescription && editRepData.ShortDescription === repository.ShortDescription &&
editRepData.Version === repository.Version editRepData.Version === repository.Version
) )
) { ) {
const response = await RepositoryService.editRepository(repository.Id, editRepData) const response = await RepositoryService.editRepository(
if (response.status === 406) { repository.Id,
setError({ state: true, message: response.data }) editRepData
setLoading(false) )
return if (response.status === 406) {
} setError({ state: true, message: response.data })
setLoading(false)
return
} }
}
if (repository.Url !== selectedFile && selectedFile instanceof File) {
const response = await RepositoryService.addFile(selectedFile, repository.Id) if (repository.Url !== selectedFile && selectedFile instanceof File) {
if (response.status === 406) { const response = await RepositoryService.addFile(
setError({ state: true, message: response.data }) selectedFile,
setLoading(false) repository.Id
return )
} if (response.status === 406) {
setError({ state: true, message: response.data })
setLoading(false)
return
} }
setLoading(false) }
setModal(false) setLoading(false)
getRep() setModal(false)
} catch (error) {} getRep()
} } catch (error) {}
}
return ( return (
<> <>
<Modal modal={modal} setModal={setModal}> <Modal modal={modal} setModal={setModal}>
<ModalContainer> <ModalContainer>
<h2>Изменение пакета в репозитории</h2>
<h2>Изменение пакета в репозитории</h2> <MainInput
<MainInput placeholder='Название'
placeholder='Название' value={editRepData.Name}
value={editRepData.Name} onChange={handleNameChange}
onChange={handleNameChange} />
/> <MainInput
<MainInput placeholder='Версия'
placeholder='Версия' value={editRepData.Version}
value={editRepData.Version} onChange={handleVersionChange}
onChange={handleVersionChange} />
/> <MainInput
<MainInput placeholder='Краткое описание'
placeholder='Краткое описание' value={editRepData.ShortDescription}
value={editRepData.Description} onChange={handleShortDescriptionChange}
onChange={handleShortDescriptionChange} />
/> <Textarea
<Textarea placeholder='Полное описание'
placeholder='Полное описание' value={editRepData.Description}
value={editRepData.Description} onChange={handleDescriptionChange}
onChange={handleDescriptionChange} />
/> <MainInput
<MainInput placeholder='Категория'
placeholder='Категория' value={editRepData.Category}
value={editRepData.Category} onChange={handleCategoryChange}
onChange={handleCategoryChange} />
/> <div>
<div>
<NewRepFileBlock> <NewRepFileBlock>
<input <input
ref={fileInputRef} ref={fileInputRef}
@ -198,21 +223,21 @@ export default function EditRepModal({ modal, setModal, repository, getRep }: Pr
<MainButton onClick={handleFileClick}>Выбрать файл</MainButton> <MainButton onClick={handleFileClick}>Выбрать файл</MainButton>
</NewRepFileBlock> </NewRepFileBlock>
</div> </div>
<div className='buttonBlock'> <div className='buttonBlock'>
<MainButton <MainButton
color='secondary' color='secondary'
fullWidth fullWidth
onClick={() => setModal(false)} onClick={() => setModal(false)}
> >
Отмена Отмена
</MainButton> </MainButton>
<MainButton fullWidth onClick={updateRep} disabled={!formValid}> <MainButton fullWidth onClick={updateRep} disabled={!formValid}>
Сохранить Сохранить
</MainButton> </MainButton>
</div> </div>
</ModalContainer> </ModalContainer>
</Modal> </Modal>
<NewRepErrorMessageContainer> <NewRepErrorMessageContainer>
<Modal modal={error.state} setModal={setErrorState}> <Modal modal={error.state} setModal={setErrorState}>
<NewRepErrorMessage> <NewRepErrorMessage>
<h3>{error.message}</h3> <h3>{error.message}</h3>
@ -220,7 +245,6 @@ export default function EditRepModal({ modal, setModal, repository, getRep }: Pr
</NewRepErrorMessage> </NewRepErrorMessage>
</Modal> </Modal>
</NewRepErrorMessageContainer> </NewRepErrorMessageContainer>
</> </>
) )
} }

View File

@ -1,8 +1,8 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton' import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput' import MainInput from 'src/components/UI/input/MainInput'
import Modal from 'src/components/UI/modal/Modal'
import Textarea from 'src/components/UI/input/Textarea' import Textarea from 'src/components/UI/input/Textarea'
import Modal from 'src/components/UI/modal/Modal'
import RepositoryService from 'src/services/repositoryServices' import RepositoryService from 'src/services/repositoryServices'
import styled from 'styled-components' import styled from 'styled-components'
import { T_NewRepository } from '../types' import { T_NewRepository } from '../types'
@ -79,23 +79,26 @@ export default function NewRepModal({ modal, setModal, getRep }: Props) {
useEffect(() => { useEffect(() => {
setFormValid( setFormValid(
newRep.Name !== '' && newRep.Name !== '' &&
newRep.Description !== '' && newRep.Description !== '' &&
newRep.ShortDescription !== '' && newRep.ShortDescription !== '' &&
newRep.Version !== '' && newRep.Version !== '' &&
newRep.Category !== '' newRep.Category !== ''
) )
}, [newRep]) }, [newRep])
const [errorState, setErrorState] = useState(false) const [errorState, setErrorState] = useState(false)
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewRep({ ...newRep, Name: e.target.value }) setNewRep({ ...newRep, Name: e.target.value })
} }
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { const handleDescriptionChange = (
e: React.ChangeEvent<HTMLTextAreaElement>
) => {
setNewRep({ ...newRep, Description: e.target.value }) setNewRep({ ...newRep, Description: e.target.value })
} }
const handleShortDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleShortDescriptionChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setNewRep({ ...newRep, ShortDescription: e.target.value }) setNewRep({ ...newRep, ShortDescription: e.target.value })
} }
const handleVersionChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleVersionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -121,7 +124,6 @@ export default function NewRepModal({ modal, setModal, getRep }: Props) {
try { try {
const response = await RepositoryService.createRep(newRep) const response = await RepositoryService.createRep(newRep)
if (response.status === 200) { if (response.status === 200) {
selectedFile !== null && selectedFile !== null &&
(await RepositoryService.addFile(selectedFile, response.data.Id)) (await RepositoryService.addFile(selectedFile, response.data.Id))
} else if (response.status === 406) { } else if (response.status === 406) {
@ -138,31 +140,31 @@ export default function NewRepModal({ modal, setModal, getRep }: Props) {
<Modal modal={modal} setModal={setModal}> <Modal modal={modal} setModal={setModal}>
<ModalContainer> <ModalContainer>
<h2>Внесение пакета в репозиторий</h2> <h2>Внесение пакета в репозиторий</h2>
<MainInput <MainInput
placeholder='Название' placeholder='Название'
value={newRep.Name} value={newRep.Name}
onChange={handleNameChange} onChange={handleNameChange}
/> />
<MainInput <MainInput
placeholder='Версия' placeholder='Версия'
value={newRep.Version} value={newRep.Version}
onChange={handleVersionChange} onChange={handleVersionChange}
/> />
<MainInput <MainInput
placeholder='Краткое описание' placeholder='Краткое описание'
value={newRep.ShortDescription} value={newRep.ShortDescription}
onChange={handleShortDescriptionChange} onChange={handleShortDescriptionChange}
/> />
<Textarea <Textarea
placeholder='Полное описание' placeholder='Полное описание'
value={newRep.Description} value={newRep.Description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
/> />
<MainInput <MainInput
placeholder='Категория' placeholder='Категория'
value={newRep.Category} value={newRep.Category}
onChange={handleCategoryChange} onChange={handleCategoryChange}
/> />
<div> <div>
<NewAppFileBlock> <NewAppFileBlock>
<input <input

View File

@ -1,4 +1,5 @@
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu' 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'
@ -53,6 +54,7 @@ type Props = {
getRep: () => void getRep: () => void
queryParams: I_QueryParams queryParams: I_QueryParams
setQueryParams: (param: I_QueryParams) => void setQueryParams: (param: I_QueryParams) => void
loadingState?: boolean
} }
export default function RepTable({ export default function RepTable({
@ -67,33 +69,68 @@ export default function RepTable({
repository, repository,
getRep, getRep,
queryParams, queryParams,
setQueryParams setQueryParams,
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 } = {
'Статус': 'enabled', Название: 'name',
'Название': 'string', 'Дата обновления': 'updated',
'Версия': 'string', Версия: 'version',
'Описание': 'string', 'Краткое описание': 'short',
'Дата изменения': 'updated', Состояние: '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> <Table loaderState={loadingState}>
<thead> <thead>
<tr> <tr>
<th> <th>

View File

@ -1,10 +1,10 @@
import { useState } from 'react' import { useState } from 'react'
import { HOST_NAME } from 'src/constants/host'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import MainButton from 'src/components/UI/button/MainButton' import MainButton from 'src/components/UI/button/MainButton'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu' 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 Modal from 'src/components/UI/modal/Modal' import Modal from 'src/components/UI/modal/Modal'
import { HOST_NAME } from 'src/constants/host'
import RepositoryService from 'src/services/repositoryServices' import RepositoryService from 'src/services/repositoryServices'
import styled from 'styled-components' import styled from 'styled-components'
import { T_ColumnsState, T_RepositoryItem } from '../types' import { T_ColumnsState, T_RepositoryItem } from '../types'
@ -51,16 +51,13 @@ export default function ({
const deleteRep = async () => { const deleteRep = async () => {
setContextMenuState(false) setContextMenuState(false)
const response = await RepositoryService.deleteRepository( const response = await RepositoryService.deleteRepository(repository.Id)
repository.Id
)
if (response.status === 200) { if (response.status === 200) {
getRep() getRep()
} }
setDeleteNotificationState(false) setDeleteNotificationState(false)
} }
const onRep = async () => { const onRep = async () => {
setContextMenuState(false) setContextMenuState(false)
const response = await RepositoryService.onRepository(repository.Id) const response = await RepositoryService.onRepository(repository.Id)
@ -76,11 +73,10 @@ export default function ({
getRep() getRep()
} }
} }
const repRep = async () => { const repackRep = async () => {
setContextMenuState(false) setContextMenuState(false)
const response = await RepositoryService.RepackReposytory(repository.Id) const response = await RepositoryService.RepackRepository(repository.Id)
if (response.status === 200) { if (response.status === 200) {
getRep() getRep()
} }
@ -88,12 +84,14 @@ export default function ({
const depRep = async () => { const depRep = async () => {
setContextMenuState(false) setContextMenuState(false)
const response = await RepositoryService.DependenciesReposytory(repository.Id) const response = await RepositoryService.DependenciesRepository(
repository.Id
)
if (response.status === 200) { if (response.status === 200) {
getRep() getRep()
} }
} }
const dowRep = async () => { const dowRep = async () => {
setContextMenuState(false) setContextMenuState(false)
const response = await RepositoryService.DownloadFile(repository.Id) const response = await RepositoryService.DownloadFile(repository.Id)
@ -101,7 +99,6 @@ export default function ({
getRep() getRep()
} }
} }
return ( return (
<> <>
@ -133,7 +130,7 @@ export default function ({
}} }}
/> />
</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>
@ -156,15 +153,15 @@ export default function ({
active={contextMenuState} active={contextMenuState}
setActive={setContextMenuState} setActive={setContextMenuState}
> >
<div className='target'> <div className='target'>
<img <img
src='/src/images/menu.svg' src='/src/images/menu.svg'
alt='' alt=''
onClick={() => setContextMenuState(!contextMenuState)} onClick={() => setContextMenuState(!contextMenuState)}
/> />
</div> </div>
<div className='content'> <div className='content'>
{repository.Enabled ? ( {repository.Enabled ? (
<Item onClick={offRep}> <Item onClick={offRep}>
<img src='/src/images/stop.svg' alt='' /> <img src='/src/images/stop.svg' alt='' />
<p>Выключить</p> <p>Выключить</p>
@ -175,34 +172,39 @@ export default function ({
<p>Включить</p> <p>Включить</p>
</Item> </Item>
)} )}
<Item onClick={repRep}> <Item onClick={repackRep}>
<img src='/src/images/reload.svg' alt='' /> <img src='/src/images/reload.svg' alt='' />
<p>Перепаковать</p> <p>Перепаковать</p>
</Item> </Item>
<Item onClick={depRep}> <Item onClick={depRep}>
<img src='/src/images/trash.svg' alt='' /> <img src='/src/images/trash.svg' alt='' />
<p>Очистить зависимости</p> <p>Очистить зависимости</p>
</Item> </Item>
{repository.Url !== '' && ( {repository.Url !== '' && (
<Link <Link
to={`${HOST_NAME}/${repository.Url}`} to={`${HOST_NAME}/${repository.Url}`}
onClick={() => setContextMenuState(false)} onClick={() => setContextMenuState(false)}
>
<Item onClick={dowRep}>
<img
src='/src/images/download.svg'
alt=''
style={{ width: '20px' }}
/>
<p>Скачать</p>
</Item>
</Link>
)}
<Item
onClick={() => (
setEditModalState(true), setContextMenuState(false)
)}
> >
<Item onClick={dowRep}> <img src='/src/images/edit.svg' alt='' />
<img <p>Редактировать</p>
src='/src/images/download.svg' </Item>
alt='' <Item
style={{ width: '20px' }} onClick={() => {
/>
<p>Скачать</p>
</Item>
</Link>
)}
<Item onClick={() => (setEditModalState(true), setContextMenuState(false))}>
<img src='/src/images/edit.svg' alt='' />
<p>Редактировать</p>
</Item>
<Item onClick={() => {
setContextMenuState(false) setContextMenuState(false)
setDeleteNotificationState(true) setDeleteNotificationState(true)
}} }}
@ -210,7 +212,7 @@ export default function ({
<img src='/src/images/trash.svg' alt='' /> <img src='/src/images/trash.svg' alt='' />
<p>Удалить</p> <p>Удалить</p>
</Item> </Item>
</div> </div>
</ContextMenu> </ContextMenu>
</td> </td>
<Modal <Modal

View File

@ -169,7 +169,7 @@ export default function Store({}: Props) {
<SearchInput <SearchInput
value={queryParams.search} value={queryParams.search}
onChange={handleSearchQueryChange} onChange={handleSearchQueryChange}
placeholder='Введите номер контракта, поставки или email' placeholder='Введите название приложения, версию, категорию или краткое описание'
/> />
<> <>
{searchNotification ? ( {searchNotification ? (

View File

@ -18,18 +18,18 @@ export default class RepositoryService {
try { try {
const response = await axios.get(url, { params }) const response = await axios.get(url, { params })
console.log(response.data)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
} }
} }
static async createRep( static async createRep(newRep: T_NewRepository): Promise<AxiosResponse> {
newRep: T_NewRepository
): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages`, newRep) const response = await axios.post(
`${API_HOST_NAME}/repository/packages`,
newRep
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
@ -48,10 +48,12 @@ export default class RepositoryService {
return error.response return error.response
} }
} }
static async onRepository(id: number): Promise<AxiosResponse> { static async onRepository(id: number): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages/${id}/enable`) const response = await axios.post(
`${API_HOST_NAME}/repository/packages/${id}/enable`
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
@ -60,23 +62,29 @@ export default class RepositoryService {
static async offRepository(id: number): Promise<AxiosResponse> { static async offRepository(id: number): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages/${id}/disable`) const response = await axios.post(
`${API_HOST_NAME}/repository/packages/${id}/disable`
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
} }
} }
static async RepackReposytory(id: number): Promise<AxiosResponse> { static async RepackRepository(id: number): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages/${id}/repack`) const response = await axios.post(
`${API_HOST_NAME}/repository/packages/${id}/repack`
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
} }
} }
static async DependenciesReposytory(id: number): Promise<AxiosResponse> { static async DependenciesRepository(id: number): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages/${id}/dependencies`) const response = await axios.post(
`${API_HOST_NAME}/repository/packages/${id}/dependencies`
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
@ -87,18 +95,20 @@ export default class RepositoryService {
const response = await axios.get( const response = await axios.get(
`${API_HOST_NAME}/repository/packages/${id}/package/download`, `${API_HOST_NAME}/repository/packages/${id}/package/download`,
{ {
responseType: 'arraybuffer', responseType: 'arraybuffer'
} }
) )
return response; return response
} catch (error: any) { } catch (error: any) {
return error.response; return error.response
} }
} }
static async deleteRepository(id: number): Promise<AxiosResponse> { static async deleteRepository(id: number): Promise<AxiosResponse> {
try { try {
const response = await axios.delete(`${API_HOST_NAME}/repository/packages/${id}`) const response = await axios.delete(
`${API_HOST_NAME}/repository/packages/${id}`
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response
@ -110,7 +120,10 @@ export default class RepositoryService {
rep: T_NewRepository rep: T_NewRepository
): Promise<AxiosResponse> { ): Promise<AxiosResponse> {
try { try {
const response = await axios.post(`${API_HOST_NAME}/repository/packages/${id}`, rep) const response = await axios.post(
`${API_HOST_NAME}/repository/packages/${id}`,
rep
)
return response return response
} catch (error: any) { } catch (error: any) {
return error.response return error.response