@autobusal/common 0.0.39 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/ApiDetails/ApiDetails.tsx +50 -0
  2. package/ApiDetails/Content.tsx +44 -0
  3. package/ApiDetails/Devpos.tsx +139 -0
  4. package/ApiDetails/services.ts +38 -0
  5. package/ApiDetails/types.ts +13 -0
  6. package/Autocomplete/Autocomplete.tsx +1 -1
  7. package/Avatar.tsx +28 -0
  8. package/BackTo.tsx +28 -4
  9. package/Calendar/Calendar.tsx +1 -1
  10. package/ContactInfo/ContactInfo.tsx +40 -0
  11. package/ContactInfo/styles.ts +33 -0
  12. package/File/File.tsx +88 -0
  13. package/File/styles.ts +23 -0
  14. package/IpLogs/IpLogs.tsx +47 -0
  15. package/IpLogs/View.tsx +48 -0
  16. package/IpLogs/services.ts +27 -0
  17. package/IpLogs/styles.ts +10 -0
  18. package/Loading/Loading.tsx +29 -14
  19. package/Loading/styles.ts +9 -0
  20. package/Modal/Modal.tsx +3 -2
  21. package/Pagination/Pagination.tsx +2 -1
  22. package/Passengers/Passengers.tsx +1 -1
  23. package/Password/Password.tsx +4 -2
  24. package/Report/Report.tsx +39 -0
  25. package/Report/Send.tsx +55 -0
  26. package/Report/services.ts +18 -0
  27. package/Report/styles.ts +31 -0
  28. package/Seats/ChooseSeat.tsx +1 -1
  29. package/Seats/Preview.tsx +2 -2
  30. package/Seats/styles.ts +1 -43
  31. package/Table/Actions/Actions.tsx +40 -0
  32. package/Table/Actions/Get.tsx +49 -0
  33. package/Table/Actions/Icon.tsx +54 -0
  34. package/Table/Actions/styles.ts +22 -0
  35. package/Table/Header/Header.tsx +46 -0
  36. package/Table/Header/Sort.tsx +33 -0
  37. package/Table/Header/styles.ts +48 -0
  38. package/Table/Paginate.tsx +34 -0
  39. package/Table/Row.tsx +20 -0
  40. package/Table/Rows/Loading.tsx +22 -0
  41. package/Table/Rows/NotFound.tsx +22 -0
  42. package/Table/Rows/Rows.tsx +62 -0
  43. package/Table/Rows/styles.ts +31 -0
  44. package/Table/Table.tsx +74 -0
  45. package/Table/Top/Search.tsx +36 -0
  46. package/Table/Top/Top.tsx +44 -0
  47. package/Table/Top/styles.ts +32 -0
  48. package/Table/browseUrl.ts +21 -0
  49. package/Table/styles.ts +28 -0
  50. package/Table/types.ts +11 -0
  51. package/Viewer/Actions.tsx +106 -0
  52. package/Viewer/Data.tsx +124 -0
  53. package/Viewer/Item.tsx +35 -0
  54. package/Viewer/Items/CheckboxList.tsx +53 -0
  55. package/Viewer/Items/DatePicker.tsx +70 -0
  56. package/Viewer/Items/Dropdown.tsx +45 -0
  57. package/Viewer/Items/Linked.tsx +27 -0
  58. package/Viewer/Items/TextareaHtml.tsx +56 -0
  59. package/Viewer/Items/styles.ts +73 -0
  60. package/Viewer/Viewer.tsx +71 -0
  61. package/Viewer/styles.ts +58 -0
  62. package/Viewer/types.ts +22 -0
  63. package/index.ts +21 -3
  64. package/package.json +1 -1
  65. package/Pagination/types.ts +0 -12
@@ -0,0 +1,27 @@
1
+ import { useQuery, UseQueryResult } from '@tanstack/react-query';
2
+ import { apiClient } from '@autobusal/providers';
3
+ import { LogData } from '@modules/Providers/types/users';
4
+ import { PaginationData } from '@autobusal/common/Pagination/types';
5
+
6
+ export const GetLogs = (id: number, type: string, page: number): UseQueryResult<PaginationData<LogData>> => (
7
+ useQuery({
8
+ queryKey: ['ip-logs', { id, type, page }],
9
+ queryFn: async () => {
10
+ if (id === 0) {
11
+ return null;
12
+ }
13
+
14
+ return await apiClient
15
+ .get('/api/logs', {
16
+ params: {
17
+ id,
18
+ type,
19
+ page
20
+ }
21
+ })
22
+ .then(response => (
23
+ response.data
24
+ ))
25
+ }
26
+ })
27
+ );
@@ -0,0 +1,10 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ margin-top: 15px;
5
+ `;
6
+
7
+ export const NotAvailable = styled.div`
8
+ font-size: ${ props => props.theme.size.s };
9
+ color: ${ props => props.theme.font.info };
10
+ `;
@@ -1,8 +1,7 @@
1
1
  import { SpinnerRoundFilled, SpinnerCircularFixed } from 'spinners-react';
2
2
  import { useSystemTheme } from '@autobusal/providers';
3
- import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
4
- import { ContainerGeneral, ContainerInline, ContainerParagraph } from './styles';
5
- import 'react-loading-skeleton/dist/skeleton.css';
3
+ import { getRandomNumber } from '@modules/Utilities';
4
+ import { ContainerGeneral, ContainerInline, ContainerParagraph, ItemParagraph } from './styles';
6
5
 
7
6
  export const General = (): JSX.Element => {
8
7
  const theme = useSystemTheme();
@@ -21,7 +20,7 @@ export const General = (): JSX.Element => {
21
20
  : <SpinnerCircularFixed size={ options.size } color={ options.color } speed={ options.speed } thickness={ options.thickness } />
22
21
  }
23
22
  </ContainerGeneral>
24
- )
23
+ );
25
24
  };
26
25
 
27
26
  interface InlineProps {
@@ -52,18 +51,34 @@ interface ParagraphProps {
52
51
  }
53
52
 
54
53
  export const Paragraph = ({ count = 3 }: ParagraphProps): JSX.Element => {
54
+ const items: JSX.Element[] = [];
55
+
55
56
  const theme = useSystemTheme();
56
57
 
57
- const baseColor = theme === 'light' ? '#EBEBEB' : "#202020";
58
- const highlightColor = theme === 'light' ? '#F5F5F5' : '#444444';
58
+ for (let i = 1; i <= count; i++) {
59
+ items.push(
60
+ <ItemParagraph $color={ theme === 'light' ? '#EBEBEB' : '#202020' } $width={ getRandomNumber(60, 100) } />
61
+ );
62
+ }
59
63
 
60
64
  return (
61
- <SkeletonTheme baseColor={ baseColor } highlightColor={ highlightColor }>
62
- <ContainerParagraph>
63
- <Skeleton count={ count - 1 } /> <br />
64
- <Skeleton count={ count + 1 } /> <br />
65
- <Skeleton count={ count } />
66
- </ContainerParagraph>
67
- </SkeletonTheme>
65
+ <ContainerParagraph>
66
+ { items }
67
+ </ContainerParagraph>
68
68
  );
69
- };
69
+ };
70
+
71
+ interface BoxProps {
72
+ title?: string
73
+ count?: number
74
+ }
75
+
76
+ export const Box = ({ title, count = 3 }: BoxProps): JSX.Element => (
77
+ <>
78
+ <h1>{ title }</h1>
79
+
80
+ <div className="box">
81
+ {/* <Paragraph count={ count } /> */}
82
+ </div>
83
+ </>
84
+ );
package/Loading/styles.ts CHANGED
@@ -18,5 +18,14 @@ export const ContainerInline = styled.div`
18
18
  `;
19
19
 
20
20
  export const ContainerParagraph = styled.div`
21
+ display: flex;
22
+ gap: 5px;
21
23
  padding: 15px 0;
24
+ `;
25
+
26
+ export const ItemParagraph = styled.div<{ $color: string, $width: number }>`
27
+ flex-basis: ${ props => props.$width };
28
+ height: 5px;
29
+ background: ${ props => props.$color };
30
+ border-radius: 5px;
22
31
  `;
package/Modal/Modal.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AiOutlineClose } from 'react-icons/ai';
2
2
  import { useOverflow } from '@autobusal/hooks';
3
- import { Paragraph } from '../index';
3
+ import { Paragraph } from '../Loading/Loading';
4
4
  import { Background, Container, Title, ButtonClose } from './styles';
5
5
 
6
6
  interface Props {
@@ -28,7 +28,8 @@ const Modal = ({ loading, width, title, content, onClose }: Props): JSX.Element
28
28
  </ButtonClose>
29
29
  </Title>
30
30
 
31
- { loading ? <Paragraph /> : content }
31
+ {/* { loading ? <Paragraph /> : content } */}
32
+ <Paragraph />
32
33
  </Container>
33
34
  </>
34
35
  );
@@ -1,6 +1,6 @@
1
1
  import { TFunction } from 'i18next';
2
2
  import { Container, Left, Right, PageText, PageLink } from './styles';
3
- import { PaginationData } from './types';
3
+ import { PaginationData } from '@modules/Providers/types/pagination';
4
4
 
5
5
  interface Props {
6
6
  pages: PaginationData<unknown>
@@ -24,6 +24,7 @@ const Pagination = ({ pages, t }: Props): (JSX.Element | null) => {
24
24
  <Container>
25
25
  <Left>
26
26
  { t('pagination.showing', {
27
+ ns: 'common',
27
28
  current: pages.current_page,
28
29
  max: pages.last_page
29
30
  }) }
@@ -24,7 +24,7 @@ const Passengers = ({ defaultAdults, defaultChildren, defaultBabies, t, refs, on
24
24
  getText(adults, children, babies, t)
25
25
  );
26
26
 
27
- const ref = useRef(null);
27
+ const ref = useRef<HTMLDivElement>(null);
28
28
 
29
29
  useEffect(() => {
30
30
  const text = getText(adults, children, babies, t);
@@ -7,11 +7,13 @@ import { Container } from './styles';
7
7
 
8
8
  interface Props {
9
9
  name: string
10
+ tabIndex?: number
11
+ validation?: string
10
12
  t: TFunction<'common'>
11
13
  refs: UseFormRegister<any>
12
14
  }
13
15
 
14
- const Password = ({ name, refs, t }: Props): JSX.Element => {
16
+ const Password = ({ name, tabIndex, validation, t, refs }: Props): JSX.Element => {
15
17
  const [ show, setShow ] = useState<boolean>(false);
16
18
 
17
19
  const toggle = (): void => {
@@ -20,7 +22,7 @@ const Password = ({ name, refs, t }: Props): JSX.Element => {
20
22
 
21
23
  return (
22
24
  <Container>
23
- <input type={ show ? 'text' : 'password' } { ...refs(name, validate('required|min_length:6|max_length:20', t)) } />
25
+ <input type={ show ? 'text' : 'password' } tabIndex={ tabIndex } { ...refs(name, validate(validation ?? '', t)) } />
24
26
 
25
27
  { show
26
28
  ? <AiFillEye onClick={ toggle } />
@@ -0,0 +1,39 @@
1
+ import { useState } from 'react';
2
+ import { TFunction } from 'i18next';
3
+ import { MdOutlineBugReport } from 'react-icons/md';
4
+ import Modal from '../Modal/Modal';
5
+ import Send from './Send';
6
+ import { Container, ButtonMore } from './styles';
7
+
8
+ interface Props {
9
+ question: string
10
+ type: string
11
+ margin?: number
12
+ t: TFunction<'common'>
13
+ }
14
+
15
+ const Report = ({ question, type, margin, t }: Props): JSX.Element => {
16
+ const [ show, setShow ] = useState<boolean>(false);
17
+
18
+ return (
19
+ <Container $margin={ margin }>
20
+ <ButtonMore type="button" onClick={ () => setShow(true) }>
21
+ <MdOutlineBugReport />
22
+ { question }
23
+ </ButtonMore>
24
+
25
+ { show && (
26
+ <Modal
27
+ width={ 500 }
28
+ title={ t('report.title', { ns: 'common' }) }
29
+ content={
30
+ <Send type={ type } t={ t } />
31
+ }
32
+ onClose={ () => setShow(false) }
33
+ />
34
+ ) }
35
+ </Container>
36
+ );
37
+ };
38
+
39
+ export default Report;
@@ -0,0 +1,55 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useForm } from 'react-hook-form';
3
+ import { Button } from '@autobusal/common';
4
+ import { RiCheckFill } from 'react-icons/ri';
5
+ import { validate, display } from '@autobusal/utilities/form';
6
+ import { Success } from './styles';
7
+ import { PostReport } from './services';
8
+
9
+ interface Props {
10
+ type: string
11
+ t: TFunction<'common'>
12
+ }
13
+
14
+ interface ReportForm {
15
+ about: string
16
+ }
17
+
18
+ const Send = ({ type, t }: Props): JSX.Element => {
19
+ const { register, handleSubmit, formState: { errors } } = useForm<ReportForm>();
20
+
21
+ const { mutate: Send, isPending, isSuccess } = PostReport(type);
22
+
23
+ const onSubmit = (data: ReportForm): void => {
24
+ Send(data);
25
+ };
26
+
27
+ if (isSuccess) {
28
+ return (
29
+ <Success>
30
+ <RiCheckFill />
31
+ { t('report.sent', { ns: 'common' }) }
32
+ </Success>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <form onSubmit={ handleSubmit(onSubmit) }>
38
+ <div className="row">
39
+ { t('report.explain', { ns: 'common' }) }
40
+
41
+ <textarea { ...register('about', validate('required|min_length:3', t)) } />
42
+
43
+ { display(errors.about) }
44
+ </div>
45
+
46
+ <Button
47
+ loading={ isPending }
48
+ type="submit"
49
+ text={ t('report.send', { ns: 'common' }) }
50
+ />
51
+ </form>
52
+ );
53
+ };
54
+
55
+ export default Send;
@@ -0,0 +1,18 @@
1
+ import { UseMutationResult, useMutation } from '@tanstack/react-query';
2
+ import { apiClient } from '@autobusal/providers';
3
+
4
+ export const PostReport = (type: string): UseMutationResult<void> => (
5
+ useMutation({
6
+ mutationKey: ['operator-locations-move', { type }],
7
+ mutationFn: async (data: any) => (
8
+ await apiClient
9
+ .post('/api/reporting/send', {
10
+ ...data,
11
+ type
12
+ })
13
+ .then(response => (
14
+ response.data
15
+ ))
16
+ )
17
+ })
18
+ );
@@ -0,0 +1,31 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const Container = styled.div<{ $margin?: number }>`
4
+ display: flex;
5
+ ${ props => props.$margin && css<{ $margin?: number }>`
6
+ margin-top: ${ props => props.$margin };
7
+ ` }
8
+ `;
9
+
10
+ export const ButtonMore = styled.button`
11
+ display: flex;
12
+ align-items: center;
13
+ gap: 4px;
14
+ margin-left: auto;
15
+ font-size: ${ props => props.theme.size.xs };
16
+ color: ${ props => props.theme.font.info };
17
+
18
+ &:hover {
19
+ text-decoration: underline;
20
+ }
21
+ `;
22
+
23
+ export const Success = styled.div`
24
+ display: flex;
25
+ align-items: center;
26
+ gap: 10px;
27
+ padding: 25px 0;
28
+ font-weight: 700;
29
+ color: ${ props => props.theme.font.success };
30
+ font-size: ${ props => props.theme.size.l };
31
+ `;
@@ -27,7 +27,7 @@ const ChooseSeat = ({ name, type, defaultValue, bus, picked, validation, t, refs
27
27
  const [ selected, setSelected ] = useState<number>(defaultValue ?? 0);
28
28
 
29
29
  // if we don't have a bus, we just show a notification
30
- if (! bus.bus) {
30
+ if (!bus.bus) {
31
31
  return <Unavailable t={ t } />;
32
32
  }
33
33
 
package/Seats/Preview.tsx CHANGED
@@ -9,7 +9,7 @@ interface Props {
9
9
  occupied: number[]
10
10
  picked: number[]
11
11
  selected: number
12
- t: TFunction<'general'>
12
+ t: TFunction<'common'>
13
13
  onSelect: (seat: number, occupied: boolean) => void
14
14
  onClose: () => void
15
15
  }
@@ -38,7 +38,7 @@ const Preview = ({ bus, occupied, picked, selected, t, onSelect, onClose }: Prop
38
38
  return (
39
39
  <Modal
40
40
  width={ 1040 }
41
- title={ t('seats.title', { bus: bus.name }) }
41
+ title={ t('seats.title', { ns: 'common', bus: bus.name }) }
42
42
  content={ (
43
43
  <Seats>
44
44
  <SeatsInner $image={ bus.image_url }>
package/Seats/styles.ts CHANGED
@@ -87,46 +87,4 @@ export const TitleUnavailable = styled.div`
87
87
  align-items: center;
88
88
  font-weight: 700;
89
89
  font-size: ${ props => props.theme.size.s };
90
- `;
91
-
92
- // // @todo: finish adding a seat
93
- // // .choose-seats {
94
- // // .bus-plan {
95
- // // .bus-seat {
96
- // // .remove {
97
- // // position: absolute;
98
- // // top: 0;
99
- // // right: 0;
100
- // // z-index: 1;
101
- // // width: 20px;
102
- // // height: 20px;
103
- // // background-color: $red;
104
- // // border-radius: $border-radius - 3;
105
-
106
- // // svg {
107
- // // margin: 1px auto 0;
108
- // // font-size: $size-info;
109
- // // color: $white;
110
- // // }
111
- // // }
112
- // // }
113
-
114
- // // .add-seat {
115
- // // display: block;
116
- // // position: absolute;
117
- // // z-index: 5;
118
- // // bottom: -58px;
119
- // // width: 50px;
120
- // // height: 38px;
121
- // // text-align: center;
122
- // // line-height: 32px;
123
- // // color: $blue;
124
- // // border: 3px solid $blue;
125
- // // border-radius: 100px;
126
-
127
- // // svg {
128
- // // margin: 0;
129
- // // }
130
- // // }
131
- // // }
132
- // // }
90
+ `;
@@ -0,0 +1,40 @@
1
+ import { TFunction } from 'i18next';
2
+ import { NavigateFunction } from 'react-router-dom';
3
+ import Get from './Get';
4
+ import { Container } from './styles';
5
+ import { Td } from '../styles';
6
+
7
+ interface Props {
8
+ id: number
9
+ url: string
10
+ urlWith?: string
11
+ available?: string[]
12
+ t: TFunction<'common'>
13
+ handlers?: any
14
+ navigate: NavigateFunction
15
+ }
16
+
17
+ const Actions = ({ id, url, urlWith, available, t, handlers, navigate }: Props): JSX.Element => {
18
+ const actions = available?.map((action, index) => (
19
+ <Get
20
+ key={ index }
21
+ id={ id }
22
+ url={ url }
23
+ urlWith={ urlWith }
24
+ type={ action }
25
+ t={ t }
26
+ handlers={ handlers }
27
+ navigate={ navigate }
28
+ />
29
+ ));
30
+
31
+ return (
32
+ <Td>
33
+ <Container>
34
+ { actions }
35
+ </Container>
36
+ </Td>
37
+ );
38
+ };
39
+
40
+ export default Actions;
@@ -0,0 +1,49 @@
1
+ import { TFunction } from 'i18next';
2
+ import { NavigateFunction } from 'react-router-dom';
3
+ import Icon from './Icon';
4
+ import { ButtonAction } from './styles';
5
+
6
+ interface Props {
7
+ id: number
8
+ url: string
9
+ urlWith?: string
10
+ type: string
11
+ t: TFunction<'common'>
12
+ handlers?: any
13
+ navigate: NavigateFunction
14
+ }
15
+
16
+ const Get = ({ id, url, urlWith, type, t, handlers, navigate }: Props): (JSX.Element | null) => {
17
+ if (['create', 'search'].includes(type)) {
18
+ return null;
19
+ }
20
+
21
+ const onAskDeletion = (): void => {
22
+ // ask for the deletion of the object
23
+ if (window.confirm(t('table.confirm', { ns: 'common' }))) {
24
+ handlers.delete(id);
25
+ }
26
+ };
27
+
28
+ const onClick = (event: React.MouseEvent<HTMLButtonElement>): void => {
29
+ event.stopPropagation();
30
+
31
+ if (type === 'view' || type === 'update') {
32
+ navigate(`${ url }/manage/${ id }${ urlWith ? `?${ urlWith }` : '' }`);
33
+ } else if (type === 'delete') {
34
+ onAskDeletion();
35
+ } else {
36
+ if (handlers[type]) {
37
+ handlers[type](id);
38
+ }
39
+ }
40
+ };
41
+
42
+ return (
43
+ <ButtonAction $type={ type } onClick={ (event) => onClick(event) }>
44
+ <Icon type={ type } t={ t } />
45
+ </ButtonAction>
46
+ );
47
+ };
48
+
49
+ export default Get;
@@ -0,0 +1,54 @@
1
+ import { TFunction } from 'i18next';
2
+ import { AiOutlineCheck, AiOutlineCloudDownload } from 'react-icons/ai';
3
+ import { BiEditAlt, BiTransfer, BiTrash } from 'react-icons/bi';
4
+ import { FaTrash } from 'react-icons/fa';
5
+ import { GiMoneyStack } from 'react-icons/gi';
6
+ import { IoDocumentLockOutline } from 'react-icons/io5';
7
+ import { RiBusLine, RiEyeLine, RiLoginCircleLine } from 'react-icons/ri';
8
+
9
+ interface Props {
10
+ type: string
11
+ t: TFunction<'common'>
12
+ }
13
+
14
+ const Icon = ({ type, t }: Props): (JSX.Element | null) => {
15
+ switch (type) {
16
+ case 'view':
17
+ case 'preview':
18
+ return <RiEyeLine title={ t('table.actions.view', { ns: 'common' }) } />;
19
+
20
+ case 'delete':
21
+ return <BiTrash title={ t('table.actions.delete', { ns: 'common' }) } />;
22
+
23
+ case 'login_as':
24
+ return <RiLoginCircleLine title={ t('table.actions.custom.login_as', { ns: 'common' }) } />;
25
+
26
+ case 'approve':
27
+ return <AiOutlineCheck title={ t('table.actions.custom.approve', { ns: 'common' }) } />;
28
+
29
+ case 'deny':
30
+ return <FaTrash title={ t('table.actions.custom.deny', { ns: 'common' }) } />;
31
+
32
+ case 'update':
33
+ return <BiEditAlt title={ t('table.actions.custom.update', { ns: 'common' }) } />;
34
+
35
+ case 'transactions':
36
+ return <BiTransfer title={ t('table.actions.custom.transactions', { ns: 'common' }) } />;
37
+
38
+ case 'comissions':
39
+ return <GiMoneyStack title={ t('table.actions.custom.comissions', { ns: 'common' }) } />;
40
+
41
+ case 'download':
42
+ return <AiOutlineCloudDownload title={ t('table.actions.custom.download', { ns: 'common' }) } />;
43
+
44
+ case 'bus':
45
+ return <RiBusLine title={ t('table.actions.custom.bus', { ns: 'common' }) } />;
46
+
47
+ case 'douane':
48
+ return <IoDocumentLockOutline title={ t('table.actions.custom.douane', { ns: 'common' }) } />;
49
+ }
50
+
51
+ return null;
52
+ };
53
+
54
+ export default Icon;
@@ -0,0 +1,22 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ display: flex;
5
+ gap: 10px;
6
+ justify-content: center;
7
+ `;
8
+
9
+ export const ButtonAction = styled.button<{ $type: string }>`
10
+ display: flex;
11
+ align-items: center;
12
+ font-size: ${ props => props.theme.size.xxl };
13
+ color: ${ props => props.theme.font.info };
14
+
15
+ ${ props => (props.$type === 'delete' || props.$type === 'deny') && css`
16
+ color: ${ props => props.theme.font.error };
17
+ ` }
18
+
19
+ ${ props => props.$type === 'approve' && css`
20
+ color: ${ props => props.theme.font.success };
21
+ ` }
22
+ `;
@@ -0,0 +1,46 @@
1
+ import { TFunction } from 'i18next';
2
+ import Sort from './Sort';
3
+ import { Container, Td, Column, Text } from './styles';
4
+ import { ColumnData, SortingData } from '../types';
5
+ import { getUrl } from '../browseUrl';
6
+
7
+ interface Props {
8
+ url: string
9
+ data: ColumnData[]
10
+ sorting?: SortingData
11
+ t: TFunction<'common'>
12
+ actions?: string[]
13
+ }
14
+
15
+ const Header = ({ url, data, sorting, t, actions }: Props): JSX.Element => {
16
+ const params = getUrl();
17
+
18
+ const columns = data.map((item, index) => (
19
+ <Td key={ index } $width={ item.width }>
20
+ <Column>
21
+ { item.slug
22
+ ? <Sort item={ item } url={ url } params={ params } sorting={ sorting } />
23
+ : <Text>{ item.name }</Text>
24
+ }
25
+ </Column>
26
+ </Td>
27
+ ));
28
+
29
+ return (
30
+ <Container>
31
+ <tr>
32
+ { columns }
33
+
34
+ { (actions !== undefined && actions.length > 0) && (
35
+ <Td $width={ 10 }>
36
+ <Column>
37
+ <Text>{ t('table.options', { ns: 'common' }) }</Text>
38
+ </Column>
39
+ </Td>
40
+ ) }
41
+ </tr>
42
+ </Container>
43
+ );
44
+ };
45
+
46
+ export default Header;
@@ -0,0 +1,33 @@
1
+ import { LinkHeader } from './styles';
2
+ import { ColumnData, SortingData } from '../types';
3
+ import { parseUrl } from '../browseUrl';
4
+ import { BiSortDown, BiSortUp } from 'react-icons/bi';
5
+
6
+ interface Props {
7
+ item: ColumnData
8
+ url: string
9
+ params: any
10
+ sorting?: SortingData
11
+ }
12
+
13
+ const Sort = ({ item, url, params, sorting }: Props): JSX.Element => {
14
+ const isCurrent = params.sort === item.slug || (params.sort === undefined && sorting?.slug === item.slug);
15
+
16
+ const order = params.order ?? (
17
+ isCurrent ? sorting?.order : (item.type === 'az' ? 'asc' : 'desc')
18
+ );
19
+
20
+ const sort = parseUrl({ page: 1, sort: item.slug, order: (
21
+ order === 'desc' ? 'asc' : 'desc'
22
+ ) });
23
+
24
+ return (
25
+ <LinkHeader to={ `${ url }${ sort }` }>
26
+ { item.name }
27
+
28
+ { isCurrent && (order == 'desc' ? <BiSortDown /> : <BiSortUp />) }
29
+ </LinkHeader>
30
+ );
31
+ };
32
+
33
+ export default Sort;