reg teble(delete edit)

This commit is contained in:
иосиф брыков 2024-02-18 21:33:40 +05:00
parent 4937328645
commit 33183e250d
34 changed files with 1118 additions and 103 deletions

View File

@ -4,7 +4,7 @@ export const theme = {
secondary: '#003333',
bg: '#F5F5F5',
textColor: '#292929',
focusColor: '#EDEDED 50%',
focusColor: '#EDEDED',
// typography
h1: `
@ -12,11 +12,11 @@ export const theme = {
font-size: 36px;
`,
h2: `
font-weight: 500;
font-weight: 600;
font-size: 24px;
`,
h3: `
font-weight: 500;
font-weight: 600;
font-size: 18px;
`,
paragraph: `
@ -27,5 +27,8 @@ export const theme = {
// shadows
mainShadow: `
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
`
`,
innerShadow: `
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1) inset;
`
}

View File

@ -0,0 +1,66 @@
import React, { useEffect, useRef } from 'react'
import styled, { css } from 'styled-components'
type Props = {
active: boolean
children: React.ReactNode
setActive: (param: boolean) => void
}
interface I_Active {
active: boolean
}
const Container = styled.div<I_Active>`
position: relative;
.target {
cursor: pointer;
}
.content {
z-index: 1;
position: absolute;
top: 30px;
right: 0;
padding: 15px;
background: ${(props) => props.theme.bg};
${(props) => props.theme.mainShadow}
border-radius: 12px;
width: 200px;
flex-direction: column;
gap: 20px;
${(props) =>
props.active
? css`
display: flex;
`
: css`
display: none;
`}
}
`
export default function ContextMenu({ active, children, setActive }: Props) {
const menuRef = useRef<HTMLDivElement>(null)
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setActive(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])
return (
<Container active={active} ref={menuRef}>
{children}
</Container>
)
}

View File

@ -0,0 +1,35 @@
import styled, { css } from 'styled-components'
type Props = {
value: 'off' | 'ok' | 'minus'
onClick: () => void
}
type ContainerProps = {
value: string
}
const Container = styled.div<ContainerProps>`
cursor: pointer;
width: 20px;
height: 20px;
border-radius: 6px;
border: 1px solid ${(props) => props.theme.textColor};
display: flex;
align-items: center;
justify-content: center;
${(props) =>
(props.value === 'ok' || props.value === 'minus') &&
css`
background: ${(props) => props.theme.textColor};
`}
`
export default function Checkbox({ value, onClick }: Props) {
return (
<Container onClick={onClick} value={value}>
{value === 'ok' && <img src='/src/images/ok.svg' />}
{value === 'minus' && <img src='/src/images/minus.svg' />}
</Container>
)
}

View File

@ -28,7 +28,7 @@ const Placeholder = styled.label<PlaceholderProps>`
cursor: text;
position: absolute;
top: ${(props) => (props.showPlaceholder ? '30%' : '-20%')};
left: ${(props) => (props.showPlaceholder ? '25px' : '0px')};
left: ${(props) => (props.showPlaceholder ? '22px' : '0px')};
border-radius: 5px;
transform: translateY(-50%);
transform: scale(${(props) => (props.showPlaceholder ? '1' : '0.85')});
@ -38,7 +38,7 @@ const Placeholder = styled.label<PlaceholderProps>`
`
type Props = {
value: string
value: string | number
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
placeholder: string
type?: string

View File

@ -0,0 +1,42 @@
import React from 'react'
import styled from 'styled-components'
type Props = {
value: string
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
placeholder: string
}
const InputContainer = styled.div`
width: 100%;
display: flex;
input {
width: 100%;
${props => props.theme.innerShadow}
padding: 15px 25px;
border-radius: 12px;
outline: none;
border: none;
background: ${props => props.theme.bg};
${props => props.theme.paragraph}
}
img {
margin-left: -45px;
}
`
export default function SearchInput({ value, onChange, placeholder }: Props) {
return (
<InputContainer>
<input
type='text'
value={value}
onChange={onChange}
placeholder={placeholder}
/>
<img src='/src/images/search.svg' alt='' />
</InputContainer>
)
}

View File

@ -45,7 +45,7 @@ export const LoaderModal = styled.div`
width: 100px;
height: 100px;
display: flex;
background: var(--color-main);
background: ${props => props.theme.bg};
justify-content: center;
align-items: center;
border-radius: 15px;

View File

@ -13,9 +13,18 @@ interface modalContainer {
const Container = styled.div<modalContainer>`
${props => props.modal ? css`display: block;` : css`display: none;`}
.bg {
${(props) =>
props.modal
? css`
display: block;
`
: css`
display: none;
`}
.bg {
z-index: 1;
position: fixed;
bottom: 0px;
left: 0;
@ -26,6 +35,7 @@ const Container = styled.div<modalContainer>`
}
.content {
z-index: 2;
position: fixed;
padding: 20px;
border-radius: 12px;

View File

@ -0,0 +1,38 @@
import React from 'react'
import styled from 'styled-components'
type Props = {
children: React.ReactNode
}
const TableContainer = styled.table`
padding: 10px;
${(props) => props.theme.mainShadow}
border-radius: 12px;
width: 100%;
table {
background: ${(props) => props.theme.bg};
border-collapse: collapse;
width: 100%;
}
td,
th {
border-bottom: 1px solid ${(props) => props.theme.text};
padding: 10px;
text-align: left;
}
tr:last-child td {
border-bottom: none;
}
`
export default function Table({ children }: Props) {
return (
<TableContainer>
<table>{children}</table>
</TableContainer>
)
}

View File

@ -0,0 +1,90 @@
import styled, { css } from 'styled-components'
type Props = {
pageNumber: number
pageCount: number
setPageNumber: (param: number) => void
}
interface I_Active {
active: boolean
}
const Container = styled.div`
display: flex;
gap: 8px;
`
const Button = styled.div<I_Active>`
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
width: 32px;
height: 32px;
${(props) =>
props.active
? css`
border: 1px solid #919eab;
`
: css`
cursor: default;
background: #919eab;
`}
&:last-of-type {
transform: rotate(180deg);
}
`
const Item = styled.div<I_Active>`
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
width: 32px;
height: 32px;
${(props) =>
props.active
? css`
border: 1px solid ${(props) => props.theme.primary};
color: ${(props) => props.theme.primary};
`
: css`
border: 1px solid #919eab;
`}
`
export default function Pagination({
pageNumber,
pageCount,
setPageNumber
}: Props) {
return (
<Container>
<Button active={false}>
<img src='/src/images/arrow.svg' alt='' />
</Button>
<Item active={true}>
1
</Item>
<Item active={false}>
2
</Item>
<Item active={false}>...</Item>
<Item active={false}>
{pageCount - 1}
</Item>
<Item active={false}>
{pageCount}
</Item>
<Button active={true}>
<img src='/src/images/arrow.svg' alt='' />
</Button>
</Container>
)
}

View File

@ -18,13 +18,13 @@ export default function Layout({ children }: Props) {
const currentPath = location.pathname
const navigate = useNavigate()
useEffect(() => {
const hasCookie = document.cookie.includes('_identid')
if (!hasCookie) {
navigate('/login')
return
}
}, [])
// useEffect(() => {
// const hasCookie = document.cookie.includes('_identid')
// if (!hasCookie) {
// navigate('/login')
// return
// }
// }, [])
useEffect(() => {
if (currentPath === '/') {
navigate('/registrations')

View File

@ -1 +1 @@
export const HOST_NAME = 'http://127.0.0.1:8070/api/v1'
export const HOST_NAME = 'http://localhost:8070/api/v1'

3
src/images/arrow.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.65991 1.91L3.07991 6.5L7.65991 11.09L6.24991 12.5L0.249912 6.5L6.24991 0.5L7.65991 1.91Z" fill="#C4CDD5"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

3
src/images/edit.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.32507 14.045L2.23007 16.965C2.18802 17.0774 2.1792 17.1995 2.20468 17.3167C2.23015 17.434 2.28883 17.5414 2.37372 17.6262C2.4586 17.711 2.5661 17.7696 2.68337 17.7949C2.80064 17.8203 2.92274 17.8113 3.03507 17.7692L5.95423 16.6742C6.28835 16.549 6.59182 16.3538 6.84423 16.1017L15.3001 7.64584C15.3001 7.64584 15.0051 6.76167 14.1217 5.87751C13.2384 4.99418 12.3534 4.69918 12.3534 4.69918L3.89757 13.155C3.64541 13.4074 3.45021 13.7109 3.32507 14.045ZM13.5326 3.52001L14.6851 2.36751C14.8917 2.16084 15.1676 2.02918 15.4559 2.07751C15.8617 2.14418 16.4826 2.34584 17.0676 2.93168C17.6534 3.51751 17.8551 4.13751 17.9217 4.54334C17.9701 4.83168 17.8384 5.10751 17.6317 5.31418L16.4784 6.46668C16.4784 6.46668 16.1842 5.58334 15.3001 4.70001C14.4167 3.81501 13.5326 3.52001 13.5326 3.52001Z" fill="#292929"/>
</svg>

After

Width:  |  Height:  |  Size: 963 B

5
src/images/menu.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="25" height="26" viewBox="0 0 25 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7917 14.0417C20.367 14.0417 20.8333 13.5754 20.8333 13C20.8333 12.4247 20.367 11.9584 19.7917 11.9584C19.2164 11.9584 18.75 12.4247 18.75 13C18.75 13.5754 19.2164 14.0417 19.7917 14.0417Z" stroke="#292929" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.4999 14.0417C13.0752 14.0417 13.5416 13.5754 13.5416 13C13.5416 12.4247 13.0752 11.9584 12.4999 11.9584C11.9246 11.9584 11.4583 12.4247 11.4583 13C11.4583 13.5754 11.9246 14.0417 12.4999 14.0417Z" stroke="#292929" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.20841 14.0417C5.78371 14.0417 6.25008 13.5754 6.25008 13C6.25008 12.4247 5.78371 11.9584 5.20841 11.9584C4.63312 11.9584 4.16675 12.4247 4.16675 13C4.16675 13.5754 4.63312 14.0417 5.20841 14.0417Z" stroke="#292929" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 974 B

3
src/images/minus.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="14" height="2" viewBox="0 0 14 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 1C14 1.26522 13.9078 1.51957 13.7437 1.70711C13.5796 1.89464 13.3571 2 13.125 2H0.875C0.642936 2 0.420376 1.89464 0.256282 1.70711C0.0921872 1.51957 0 1.26522 0 1C0 0.734784 0.0921872 0.48043 0.256282 0.292893C0.420376 0.105357 0.642936 0 0.875 0H13.125C13.3571 0 13.5796 0.105357 13.7437 0.292893C13.9078 0.48043 14 0.734784 14 1Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 502 B

9
src/images/off.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

3
src/images/ok.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.7099 1.20986C14.617 1.11613 14.5064 1.04174 14.3845 0.990969C14.2627 0.940201 14.132 0.914062 13.9999 0.914062C13.8679 0.914062 13.7372 0.940201 13.6154 0.990969C13.4935 1.04174 13.3829 1.11613 13.29 1.20986L5.83995 8.66986L2.70995 5.52986C2.61343 5.43662 2.49949 5.36331 2.37463 5.3141C2.24978 5.2649 2.11645 5.24077 1.98227 5.24309C1.84809 5.24541 1.71568 5.27414 1.5926 5.32763C1.46953 5.38113 1.35819 5.45834 1.26495 5.55486C1.17171 5.65138 1.0984 5.76532 1.04919 5.89018C0.999989 6.01503 0.975859 6.14836 0.97818 6.28254C0.980502 6.41672 1.00923 6.54913 1.06272 6.67221C1.11622 6.79529 1.19343 6.90662 1.28995 6.99986L5.12995 10.8399C5.22291 10.9336 5.33351 11.008 5.45537 11.0588C5.57723 11.1095 5.70794 11.1357 5.83995 11.1357C5.97196 11.1357 6.10267 11.1095 6.22453 11.0588C6.34639 11.008 6.45699 10.9336 6.54995 10.8399L14.7099 2.67986C14.8115 2.58622 14.8925 2.47257 14.9479 2.34607C15.0033 2.21957 15.0319 2.08296 15.0319 1.94486C15.0319 1.80676 15.0033 1.67015 14.9479 1.54365C14.8925 1.41715 14.8115 1.3035 14.7099 1.20986Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

9
src/images/on.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

3
src/images/params.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6662 0C14.3125 4.21276e-06 13.9732 0.140441 13.7231 0.390416C13.473 0.640391 13.3325 0.979428 13.3325 1.33294V2.00006H1.33324C0.979518 2.00007 0.640288 2.1405 0.390169 2.39046C0.140049 2.64042 -0.000474239 2.97944 -0.000488281 3.33294C-0.000489881 3.50799 0.0340067 3.68131 0.101032 3.84303C0.168057 4.00475 0.266298 4.1517 0.390146 4.27547C0.513993 4.39925 0.661022 4.49743 0.822838 4.56442C0.984654 4.6314 1.15809 4.66588 1.33324 4.66588H13.3325V5.33379C13.3325 5.6873 13.473 6.02634 13.7231 6.27632C13.9732 6.52629 14.3125 6.66673 14.6662 6.66673C15.0199 6.66673 15.3591 6.52629 15.6093 6.27632C15.8594 6.02634 15.9999 5.6873 15.9999 5.33379V4.66588H18.6657C18.8409 4.66589 19.0143 4.63142 19.1761 4.56444C19.338 4.49745 19.485 4.39927 19.6089 4.27549C19.7327 4.15172 19.831 4.00477 19.898 3.84305C19.965 3.68133 19.9995 3.50799 19.9995 3.33294C19.9995 3.1579 19.965 2.98457 19.898 2.82286C19.8309 2.66114 19.7327 2.5142 19.6088 2.39043C19.485 2.26666 19.3379 2.16849 19.1761 2.10151C19.0143 2.03453 18.8409 2.00006 18.6657 2.00006H15.9999V1.33294C15.9999 0.979428 15.8594 0.640391 15.6093 0.390416C15.3591 0.140441 15.0199 4.21276e-06 14.6662 0ZM4.99951 6.66667C4.82436 6.66666 4.65092 6.70113 4.48909 6.76812C4.32727 6.8351 4.18024 6.93328 4.05638 7.05706C3.93253 7.18083 3.83428 7.32778 3.76725 7.4895C3.70022 7.65123 3.66572 7.82456 3.66572 7.99961V8.66667H1.33363C1.15848 8.66667 0.985044 8.70115 0.823229 8.76813C0.661413 8.83512 0.514384 8.93331 0.390536 9.05708C0.266688 9.18086 0.168447 9.3278 0.101422 9.48952C0.0343973 9.65124 -9.92786e-05 9.82457 -9.76621e-05 9.99961C-9.92786e-05 10.1747 0.0343973 10.348 0.101422 10.5097C0.168447 10.6714 0.266688 10.8184 0.390536 10.9421C0.514384 11.0659 0.661413 11.1641 0.823229 11.2311C0.985044 11.2981 1.15848 11.3326 1.33363 11.3326H3.66572V12.0005C3.66572 12.1755 3.70022 12.3488 3.76725 12.5106C3.83428 12.6723 3.93253 12.8192 4.05638 12.943C4.18024 13.0668 4.32727 13.165 4.48909 13.2319C4.65092 13.2989 4.82436 13.3334 4.99951 13.3334C5.35324 13.3334 5.69248 13.193 5.9426 12.943C6.19272 12.693 6.33324 12.354 6.33324 12.0005V11.3326H18.6662C18.8413 11.3326 19.0148 11.2981 19.1766 11.2311C19.3384 11.1641 19.4854 11.0659 19.6093 10.9421C19.7331 10.8184 19.8314 10.6714 19.8984 10.5097C19.9654 10.348 19.9999 10.1747 19.9999 9.99961C19.9999 9.82457 19.9654 9.65124 19.8984 9.48952C19.8314 9.3278 19.7331 9.18086 19.6093 9.05708C19.4854 8.93331 19.3384 8.83512 19.1766 8.76813C19.0148 8.70115 18.8413 8.66667 18.6662 8.66667H6.33324V7.99961C6.33324 7.64609 6.19272 7.30706 5.9426 7.05708C5.69248 6.80711 5.35324 6.66667 4.99951 6.66667ZM9.33285 13.3334C8.97913 13.3334 8.6399 13.4738 8.38978 13.7238C8.13966 13.9738 7.99914 14.3128 7.99912 14.6663V15.3333H1.33363C1.15848 15.3333 0.985044 15.3678 0.823229 15.4348C0.661413 15.5018 0.514384 15.6 0.390536 15.7237C0.266688 15.8475 0.168447 15.9945 0.101422 16.1562C0.0343973 16.3179 -9.92786e-05 16.4912 -9.76621e-05 16.6663C-8.35946e-05 17.0198 0.140439 17.3588 0.390559 17.6088C0.640679 17.8587 0.979909 17.9992 1.33363 17.9992H7.99912V18.6671C7.99914 19.0206 8.13966 19.3596 8.38978 19.6096C8.6399 19.8596 8.97913 20 9.33285 20C9.68656 20 10.0258 19.8596 10.2759 19.6096C10.526 19.3596 10.6666 19.0206 10.6666 18.6671V17.9992H18.6662C19.0199 17.9992 19.3591 17.8587 19.6092 17.6088C19.8594 17.3588 19.9999 17.0198 19.9999 16.6663C19.9999 16.4912 19.9654 16.3179 19.8984 16.1562C19.8314 15.9945 19.7331 15.8475 19.6093 15.7237C19.4854 15.6 19.3384 15.5018 19.1766 15.4348C19.0148 15.3678 18.8413 15.3333 18.6662 15.3333H10.6666V14.6663C10.6666 14.3128 10.526 13.9738 10.2759 13.7238C10.0258 13.4738 9.68656 13.3334 9.33285 13.3334Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

10
src/images/play.svg Normal file
View File

@ -0,0 +1,10 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_61_2312)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.415 6.61716L5.14475 0.365623C3.42531 -0.680357 1.28564 0.642997 1.28564 2.74871V15.2512C1.28564 17.3596 3.42531 18.6802 5.14475 17.6342L15.415 11.3859C17.1473 10.3316 17.1473 7.67137 15.415 6.61716Z" fill="#292929"/>
</g>
<defs>
<clipPath id="clip0_61_2312">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 515 B

3
src/images/search.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.3672 17.3346L21.875 21.875M19.7917 11.4583C19.7917 16.0607 16.0607 19.7917 11.4583 19.7917C6.85596 19.7917 3.125 16.0607 3.125 11.4583C3.125 6.85596 6.85596 3.125 11.4583 3.125C16.0607 3.125 19.7917 6.85596 19.7917 11.4583Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 422 B

13
src/images/stop.svg Normal file
View File

@ -0,0 +1,13 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_61_2486)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.415 6.61716L5.14475 0.365623C3.42531 -0.680357 1.28564 0.642997 1.28564 2.74871V15.2512C1.28564 17.3596 3.42531 18.6802 5.14475 17.6342L15.415 11.3859C17.1473 10.3316 17.1473 7.67137 15.415 6.61716Z" fill="#292929"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.28711 6.61716L13.4623 0.365623C15.1658 -0.680357 17.2856 0.642997 17.2856 2.74871V15.2512C17.2856 17.3596 15.1658 18.6802 13.4623 17.6342L3.28711 11.3859C1.57082 10.3316 1.57082 7.67137 3.28711 6.61716Z" fill="#292929"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.44895 11.5272L2.30107 3.14838C1.43976 1.74561 2.52948 2.31443e-08 4.26345 9.89385e-08L14.5587 5.48957e-07C16.2949 6.24849e-07 17.3824 1.74561 16.521 3.14838L11.3758 11.5272C10.5077 12.9405 8.31704 12.9405 7.44895 11.5272Z" fill="#292929"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.51423 5.17895L2.30468 14.4982C1.43304 16.0585 2.53582 18 4.29057 18L14.7091 18C16.4661 18 17.5667 16.0585 16.695 14.4982L11.4881 5.17895C10.6096 3.60702 8.39272 3.60702 7.51423 5.17895Z" fill="#292929"/>
</g>
<defs>
<clipPath id="clip0_61_2486">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

11
src/images/trash.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_88_910)">
<path d="M6.66675 1.74996V2.58329H2.50008C2.03985 2.58329 1.66675 2.95639 1.66675 3.41663V4.24996C1.66675 4.71019 2.03985 5.08329 2.50008 5.08329H17.5001C17.9603 5.08329 18.3334 4.71019 18.3334 4.24996V3.41663C18.3334 2.95639 17.9603 2.58329 17.5001 2.58329H13.3334V1.74996C13.3334 1.28972 12.9603 0.916626 12.5001 0.916626H7.50008C7.03985 0.916626 6.66675 1.28972 6.66675 1.74996Z" fill="#292929"/>
<path d="M3.26929 6.75H16.7306L15.9454 17.3513C15.8486 18.6568 14.7613 19.6667 13.4522 19.6667H6.54774C5.23867 19.6667 4.15127 18.6568 4.05457 17.3513L3.26929 6.75Z" fill="#292929"/>
</g>
<defs>
<clipPath id="clip0_88_910">
<rect width="20" height="20" fill="white" transform="translate(0 0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 855 B

View File

@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom'
import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput'
import Loader from 'src/components/UI/loader/Loader'
import UserService from 'src/services/User'
import UserService from 'src/services/userSrvices'
import styled from 'styled-components'
type Props = {}

View File

@ -1,13 +1,30 @@
import { useEffect, useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton'
import Modal from 'src/components/UI/modal/Modal'
import RegistrationsService from 'src/services/Registrations'
import SearchInput from 'src/components/UI/input/SearchInput'
import Pagination from 'src/components/UI/table/pagination/Pagination'
import RegistrationsService from 'src/services/registrationsServices'
import styled from 'styled-components'
import NewRegForm from './blocks/NewRegForm'
import { T_NewRegistration } from './types'
import NewRegModal from './blocks/NewRegModal'
import RegTable from './blocks/RegTable'
import { T_ColumnsState, T_NewRegistration, T_RegistrationItem } from './types'
type Props = {}
const Container = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
align-items: start;
padding: 40px 20px;
.bottom-menu {
display: flex;
align-items: start;
width: 100%;
justify-content: space-between;
}
`
const Notification = styled.div`
position: absolute;
top: 50%;
@ -19,20 +36,58 @@ const Notification = styled.div`
align-items: center;
`
const ModalContainer = styled.div`
const SelectNotification = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
width: 440px;
width: 100%;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
${(props) => props.theme.mainShadow}
border-radius: 12px;
.buttonBlock {
display: flex;
gap: 20px;
img {
padding: 5px;
cursor: pointer;
border-radius: 6px;
transition: 300ms;
&:hover {
background: ${(props) => props.theme.focusColor};
transform: scale(1.1);
}
}
`
export default function Registrations({}: Props) {
const [registrations, setRegistrations] = useState([])
const [pageCount, setPageCount] = useState(10)
const [pageNumber, setPageNumber] = useState(1)
const [registrations, setRegistrations] = useState<T_RegistrationItem[]>([])
const [searchQuery, setSearchQuery] = useState('')
const handleSearchQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value)
}
const [activeColumns, setActiveColumns] = useState<T_ColumnsState>({
RegNum: { name: 'Номер поставки', status: true },
ContNum: { name: 'Номер контракта', status: true },
Email: { name: 'Эл. почта', status: false },
Count: { name: 'Кол-во активаций', status: false },
EndDate: { name: 'Дата окончания', status: false },
CreatedDate: { name: 'Дата создания', status: true },
UpdatedDate: { name: 'Дата изменения', status: true },
Enabled: { name: 'Статус', status: true }
})
const [checkedColumns, setCheckedColumns] = useState<number[]>([])
const [selectAll, setSelectAll] = useState<boolean>(false)
const [notChecked, setNotChecked] = useState<number[]>([])
useEffect(() => {
notChecked.length === registrations.length &&
(setSelectAll(false), setNotChecked([]))
}, [notChecked])
const [newReg, setNewReg] = useState<T_NewRegistration>({
RegNum: '',
@ -44,6 +99,10 @@ export default function Registrations({}: Props) {
const [modal, setModal] = useState(false)
useEffect(() => {
console.log(registrations)
}, [registrations])
useEffect(() => {
setNewReg({
RegNum: '',
@ -63,11 +122,10 @@ export default function Registrations({}: Props) {
useEffect(() => {
getReg()
console.log(registrations)
}, [])
return (
<div>
<Container>
{registrations.length === 0 ? (
<Notification>
<h2>Активных лицензий нет</h2>
@ -76,23 +134,53 @@ export default function Registrations({}: Props) {
</MainButton>
</Notification>
) : (
<></>
)}
<Modal modal={modal} setModal={setModal}>
<ModalContainer>
<NewRegForm newReg={newReg} setNewReg={setNewReg} />
<div className='buttonBlock'>
<MainButton
color='secondary'
fullWidth={true}
onClick={() => setModal(false)}
>
Отмена
</MainButton>
<MainButton fullWidth={true}>Создать</MainButton>
<>
<h2>Система управления и контроля ОСГОС</h2>
<SearchInput
value={searchQuery}
onChange={handleSearchQueryChange}
placeholder='Введите номер контракта, поставки или email'
/>
{(selectAll || checkedColumns.length !== 0) && (
<SelectNotification>
<h3>
{selectAll
? 'Количесво не определено'
: checkedColumns.length !== 0 &&
`Выбранно: ${checkedColumns.length}`}
</h3>
<img src='/src/images/trash.svg' alt='' />
</SelectNotification>
)}
<RegTable
selectAll={selectAll}
notChecked={notChecked}
setNotChecked={setNotChecked}
setSelectAll={setSelectAll}
checkedColumns={checkedColumns}
setCheckedColumns={setCheckedColumns}
activeColumns={activeColumns}
setActiveColumns={setActiveColumns}
registrations={registrations}
getReg={getReg}
/>
<div className='bottom-menu'>
<Pagination
setPageNumber={setPageNumber}
pageNumber={pageNumber}
pageCount={pageCount}
/>
<MainButton onClick={() => setModal(true)}>Добавить</MainButton>
</div>
</ModalContainer>
</Modal>
</div>
</>
)}
<NewRegModal
modal={modal}
setModal={setModal}
newReg={newReg}
setNewReg={setNewReg}
getReg={getReg}
/>
</Container>
)
}

View File

@ -0,0 +1,99 @@
import { useEffect, useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput'
import Modal from 'src/components/UI/modal/Modal'
import styled from 'styled-components'
import { T_NewRegistration } from '../types'
type Props = {
modal: boolean
setModal: (param: boolean) => void
registration: T_NewRegistration
editReg: (param: T_NewRegistration) => void
}
const ModalContainer = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
width: 440px;
.buttonBlock {
display: flex;
gap: 20px;
}
`
export default function EditRegModal({
modal,
setModal,
registration,
editReg
}: Props) {
const [editRegData, setEditRegData] = useState(registration)
const handleContNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...registration, ContNum: e.target.value })
}
const handleRegNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...registration, RegNum: e.target.value })
}
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...registration, Email: e.target.value })
}
const handleCountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...registration, Count: e.target.value })
}
const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setEditRegData({ ...registration, EndDate: e.target.value })
}
useEffect(() => {
console.log(editRegData)
}, [])
return (
<Modal modal={modal} setModal={setModal}>
<ModalContainer>
<MainInput
placeholder='Номер контракта'
value={editRegData.ContNum}
onChange={handleContNumChange}
/>
<MainInput
placeholder='Номер поставки'
value={editRegData.RegNum}
onChange={handleRegNumChange}
/>
<MainInput
placeholder='Email'
value={editRegData.Email}
onChange={handleEmailChange}
/>
<MainInput
placeholder='Кол-во активаций'
value={editRegData.Count}
onChange={handleCountChange}
/>
<MainInput
placeholder='Дата окончания'
value={editRegData.EndDate}
onChange={handleEndDateChange}
type='date'
/>
<div className='buttonBlock'>
<MainButton
color='secondary'
fullWidth={true}
onClick={() => setModal(false)}
>
Отмена
</MainButton>
<MainButton fullWidth={true} onClick={() => editReg(editRegData)}>
Сохранить
</MainButton>
</div>
</ModalContainer>
</Modal>
)
}

View File

@ -1,38 +0,0 @@
import React from 'react'
import { T_NewRegistration } from '../types'
import MainInput from 'src/components/UI/input/MainInput'
type Props = {
newReg: T_NewRegistration
setNewReg: (param: T_NewRegistration) => void
}
export default function NewRegForm({newReg, setNewReg}: Props) {
const handleContNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, ContNum: e.target.value })
}
const handleRegNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, RegNum: e.target.value })
}
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, Email: e.target.value })
}
const handleCountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, Count: e.target.value })
}
const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, EndDate: e.target.value })
}
return (
<>
<MainInput placeholder='Номер контракта' value={newReg.ContNum} onChange={handleContNumChange}/>
<MainInput placeholder='Номер поставки' value={newReg.RegNum} onChange={handleRegNumChange}/>
<MainInput placeholder='Email' value={newReg.Email} onChange={handleEmailChange}/>
<MainInput placeholder='Кол-во активаций' value={newReg.Count} onChange={handleCountChange}/>
<MainInput placeholder='Дата окончания' value={newReg.EndDate} onChange={handleEndDateChange}/>
</>
)
}

View File

@ -0,0 +1,101 @@
import MainButton from 'src/components/UI/button/MainButton'
import MainInput from 'src/components/UI/input/MainInput'
import Modal from 'src/components/UI/modal/Modal'
import RegistrationsService from 'src/services/registrationsServices'
import styled from 'styled-components'
import { T_NewRegistration } from '../types'
const ModalContainer = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
width: 440px;
.buttonBlock {
display: flex;
gap: 20px;
}
`
type Props = {
modal: boolean
setModal: (param: boolean) => void
newReg: T_NewRegistration
setNewReg: (param: T_NewRegistration) => void
getReg: () => void
}
export default function NewRegModal({
modal,
setModal,
newReg,
setNewReg,
getReg
}: Props) {
const CreateReg = async () => {
await RegistrationsService.createRegistration(newReg)
getReg()
setModal(false)
}
const handleContNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, ContNum: e.target.value })
}
const handleRegNumChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, RegNum: e.target.value })
}
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, Email: e.target.value })
}
const handleCountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, Count: e.target.value })
}
const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewReg({ ...newReg, EndDate: e.target.value })
}
return (
<Modal modal={modal} setModal={setModal}>
<ModalContainer>
<MainInput
placeholder='Номер контракта'
value={newReg.ContNum}
onChange={handleContNumChange}
/>
<MainInput
placeholder='Номер поставки'
value={newReg.RegNum}
onChange={handleRegNumChange}
/>
<MainInput
placeholder='Email'
value={newReg.Email}
onChange={handleEmailChange}
/>
<MainInput
placeholder='Кол-во активаций'
value={typeof newReg.Count === 'string' ? newReg.Count : ''}
onChange={handleCountChange}
/>
<MainInput
placeholder='Дата окончания'
value={newReg.EndDate}
onChange={handleEndDateChange}
type='date'
/>
<div className='buttonBlock'>
<MainButton
color='secondary'
fullWidth={true}
onClick={() => setModal(false)}
>
Отмена
</MainButton>
<MainButton fullWidth={true} onClick={CreateReg}>
Создать
</MainButton>
</div>
</ModalContainer>
</Modal>
)
}

View File

@ -0,0 +1,122 @@
import { useEffect, useRef, useState } from 'react'
import Checkbox from 'src/components/UI/input/Checkbox'
import Table from 'src/components/UI/table/Table'
import styled, { css } from 'styled-components'
import { T_ColumnsState, T_RegistrationItem } from '../types'
import RegTableItem from './RegTableItem'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu'
const Item = styled.div`
display: flex;
gap: 10px;
cursor: pointer;
`
type Props = {
selectAll: boolean
notChecked: number[]
setNotChecked: (param: any) => void
setSelectAll: (param: any) => void
checkedColumns: number[]
setCheckedColumns: (param: any) => void
activeColumns: T_ColumnsState
setActiveColumns: (param: any) => void
registrations: T_RegistrationItem[]
getReg: () => void
}
export default function RegTable({
selectAll,
notChecked,
setNotChecked,
setSelectAll,
checkedColumns,
setCheckedColumns,
activeColumns,
setActiveColumns,
registrations,
getReg
}: Props) {
const [contextMenuState, setContextMenuState] = useState(false)
return (
<Table>
<thead>
<tr>
<th>
<Checkbox
value={
selectAll
? notChecked.length === 0
? 'ok'
: 'minus'
: checkedColumns.length === 0
? 'off'
: 'minus'
}
onClick={() => {
selectAll
? (setSelectAll(false),
setNotChecked([]),
setCheckedColumns([]))
: checkedColumns.length === 0
? (setSelectAll(true), setNotChecked([]))
: setCheckedColumns([])
}}
/>
</th>
{Object.entries(activeColumns).map(
([index, item]) =>
item.status === true && <th key={index}>{item.name}</th>
)}
<th>
<ContextMenu active={contextMenuState} setActive={setContextMenuState}>
<div className='target'>
<img
src='/src/images/params.svg'
alt=''
onClick={() => setContextMenuState(!contextMenuState)}
/>
</div>
<div className='content'>
{Object.entries(activeColumns).map(([key, item]) => (
<Item
key={key}
onClick={() => {
const updatedActiveColumns = { ...activeColumns }
updatedActiveColumns[key].status =
!updatedActiveColumns[key].status
setActiveColumns(updatedActiveColumns)
}}
>
<Checkbox
value={item.status ? 'ok' : 'off'}
onClick={() => {}}
/>
<p>{item.name}</p>
</Item>
))}
</div>
</ContextMenu>
</th>
</tr>
</thead>
<tbody>
{registrations.map((registration) => (
<RegTableItem
registration={registration}
selectAll={selectAll}
notChecked={notChecked}
checkedColumns={checkedColumns}
setNotChecked={setNotChecked}
setCheckedColumns={setCheckedColumns}
activeColumns={activeColumns}
getReg={getReg}
/>
))}
</tbody>
</Table>
)
}

View File

@ -0,0 +1,211 @@
import { useState } from 'react'
import MainButton from 'src/components/UI/button/MainButton'
import ContextMenu from 'src/components/UI/contextMenu/ContextMenu'
import Checkbox from 'src/components/UI/input/Checkbox'
import Modal from 'src/components/UI/modal/Modal'
import RegistrationsService from 'src/services/registrationsServices'
import styled from 'styled-components'
import { T_ColumnsState, T_NewRegistration, T_RegistrationItem } from '../types'
import EditRegModal from './EditRegModal'
type Props = {
registration: T_RegistrationItem
selectAll: boolean
notChecked: number[]
checkedColumns: number[]
setNotChecked: (param: any) => void
setCheckedColumns: (param: any) => void
activeColumns: T_ColumnsState
getReg: () => void
}
const Item = styled.div`
display: flex;
gap: 10px;
cursor: pointer;
`
const DeleteNotification = styled.div`
.button-container {
display: flex;
gap: 20px;
margin-top: 20px;
}
`
export default function ({
registration,
selectAll,
notChecked,
checkedColumns,
setNotChecked,
setCheckedColumns,
activeColumns,
getReg
}: Props) {
const [contextMenuState, setContextMenuState] = useState(false)
const [deleteNotificationState, setDeleteNotificationState] = useState(false)
const [editModalState, setEditModalState] = useState(false)
const deleteReg = async () => {
setContextMenuState(false)
const response = await RegistrationsService.deleteRegistration(
registration.Id
)
if (response.status === 200) {
getReg()
}
setDeleteNotificationState(false)
}
const onReg = async () => {
setContextMenuState(false)
const response = await RegistrationsService.onRegistration(registration.Id)
if (response.status === 200) {
getReg()
}
}
const offReg = async () => {
setContextMenuState(false)
const response = await RegistrationsService.offRegistration(registration.Id)
if (response.status === 200) {
getReg()
}
}
const editReg = async (reg: T_NewRegistration) => {
setContextMenuState(false)
const response = await RegistrationsService.editRegistration(
registration.Id,
reg
)
if (response.status === 200) {
getReg()
}
setEditModalState(false)
}
return (
<>
<tr key={registration.Id}>
<td>
<Checkbox
value={
selectAll
? notChecked.includes(registration.Id)
? 'off'
: 'ok'
: checkedColumns.includes(registration.Id)
? 'ok'
: 'off'
}
onClick={() => {
selectAll
? notChecked.includes(registration.Id)
? setNotChecked(
notChecked.filter((item) => item !== registration.Id)
)
: setNotChecked([...notChecked, registration.Id])
: checkedColumns.includes(registration.Id)
? (setCheckedColumns(
checkedColumns.filter((item) => item !== registration.Id)
),
setNotChecked([...notChecked, registration.Id]))
: setCheckedColumns([...checkedColumns, registration.Id])
}}
/>
</td>
{Object.entries(activeColumns).map(([key, item]) =>
activeColumns[key as keyof typeof activeColumns].status ? (
<td key={key}>
<p>
{' '}
{key === 'Enabled' ? (
registration[key as keyof typeof registration] ? (
<img src='/src/images/on.svg' />
) : (
<img src='/src/images/off.svg' />
)
) : (
registration[key as keyof typeof registration]
)}
</p>
</td>
) : null
)}
<td>
<ContextMenu
active={contextMenuState}
setActive={setContextMenuState}
>
<div className='target'>
<img
src='/src/images/menu.svg'
alt=''
onClick={() => setContextMenuState(!contextMenuState)}
/>
</div>
<div className='content'>
<Item
onClick={() => {
setEditModalState(true)
setContextMenuState(false)
}}
>
<img src='/src/images/edit.svg' alt='' />
<p>Редактировать</p>
</Item>
<Item
onClick={() => {
setContextMenuState(false)
setDeleteNotificationState(true)
}}
>
<img src='/src/images/trash.svg' alt='' />
<p>Удалить</p>
</Item>
{registration.Enabled ? (
<Item onClick={offReg}>
<img src='/src/images/stop.svg' alt='' />
<p>Выключить</p>
</Item>
) : (
<Item onClick={onReg}>
<img src='/src/images/play.svg' alt='' />
<p>Включить</p>
</Item>
)}
</div>
</ContextMenu>
</td>
<Modal
modal={deleteNotificationState}
setModal={setDeleteNotificationState}
>
<DeleteNotification>
<h3>Вы действительно хотите удалить лицензию?</h3>
<div className='button-container'>
<MainButton
color={'secondary'}
fullWidth={true}
onClick={() => setDeleteNotificationState(false)}
>
Отмена
</MainButton>
<MainButton fullWidth={true} onClick={() => deleteReg()}>
Удалить
</MainButton>
</div>
</DeleteNotification>
</Modal>
<EditRegModal
modal={editModalState}
setModal={setEditModalState}
registration={registration}
editReg={editReg}
/>
</tr>
</>
)
}

View File

@ -2,6 +2,27 @@ export type T_NewRegistration = {
RegNum: string
ContNum: string
Email: string
Count: string
Count: string | number
EndDate: string
}
export interface T_RegistrationItem {
Id: number;
RegNum: string;
ContNum: string;
Email: string;
Count: number;
EndDate: string;
CreatedDate: string;
UpdatedDate: string;
Enabled: boolean;
}
export type T_Column = {
name: string
status: boolean
}
export type T_ColumnsState = {
[key: string]: T_Column
}

View File

@ -1,15 +0,0 @@
import axios, { AxiosResponse } from 'axios'
import { HOST_NAME } from 'src/constants/host'
export default class RegistrationsService {
static async getRegistration(): Promise<AxiosResponse> {
try {
const response = await axios.get(`${HOST_NAME}/regs`, {
withCredentials: true
})
return response
} catch (error: any) {
return error.response
}
}
}

View File

@ -0,0 +1,67 @@
import axios, { AxiosResponse } from 'axios'
import { HOST_NAME } from 'src/constants/host'
import { T_NewRegistration } from 'src/pages/Registrations/types'
export default class RegistrationsService {
static async getRegistration(): Promise<AxiosResponse> {
try {
const response = await axios.get(`${HOST_NAME}/regs`)
return response
} catch (error: any) {
return error.response
}
}
static async createRegistration(
newReg: T_NewRegistration
): Promise<AxiosResponse> {
const date = new Date(newReg.EndDate)
const isoDateStr = date.toISOString()
newReg.EndDate = isoDateStr
if (typeof newReg.Count === 'string') {
newReg.Count = parseInt(newReg.Count, 10)
}
try {
const response = await axios.post(`${HOST_NAME}/regs`, newReg)
return response
} catch (error: any) {
return error.response
}
}
static async deleteRegistration(id: number): Promise<AxiosResponse> {
try {
const response = await axios.delete(`${HOST_NAME}/regs/${id}`)
return response
} catch (error: any) {
return error.response
}
}
static async onRegistration(id: number): Promise<AxiosResponse> {
try {
const response = await axios.post(`${HOST_NAME}/regs/${id}/state/enable`)
return response
} catch (error: any) {
return error.response
}
}
static async offRegistration(id: number): Promise<AxiosResponse> {
try {
const response = await axios.post(`${HOST_NAME}/regs/${id}/state/disable`)
return response
} catch (error: any) {
return error.response
}
}
static async editRegistration(id: number, reg: T_NewRegistration): Promise<AxiosResponse> {
try {
const response = await axios.post(`${HOST_NAME}/regs/${id}`, reg)
return response
} catch (error: any) {
return error.response
}
}
}