@autobusal/operator-routes 1.0.3 → 1.0.5

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.
@@ -0,0 +1,71 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useForm } from 'react-hook-form';
3
+ import { FiPlus } from 'react-icons/fi';
4
+ import { Button } from '@autobusal/common';
5
+ import { Validate, Display, success } from '@autobusal/utilities';
6
+ import Loading from './Loading';
7
+ import { getStops } from '../utilities';
8
+ import { Container, Title, ContainerAdd, Choose, Notice } from './styles';
9
+ import { AddForm } from '../types';
10
+ import { GetStops, AddLocation } from '../services';
11
+
12
+ interface Props {
13
+ routeId: number
14
+ t: TFunction<'normal'>
15
+ onReload: () => void
16
+ }
17
+
18
+ const Add = ({ routeId, t, onReload }: Props): JSX.Element => {
19
+ const { register, handleSubmit, formState: { errors } } = useForm<AddForm>();
20
+
21
+ const { data, isLoading } = GetStops(routeId);
22
+
23
+ const { mutate: Save, isPending } = AddLocation(routeId);
24
+
25
+ if (isLoading) {
26
+ return <Loading t={ t } />;
27
+ }
28
+
29
+ const onSubmit = (data: AddForm): void => {
30
+ Save(data, {
31
+ onSuccess: () => {
32
+ success(t('locations_manage.messages.added', { ns: 'common' }));
33
+
34
+ onReload();
35
+ }
36
+ });
37
+ };
38
+
39
+ return (
40
+ <Container className="box">
41
+ <Title>{ t('locations_manage.add_city.title', { ns: 'common' }) }</Title>
42
+
43
+ <form onSubmit={ handleSubmit(onSubmit) }>
44
+ <ContainerAdd>
45
+ <Choose>
46
+ <select { ...register('group_id', Validate('required', t)) }>
47
+ { getStops(t, data) }
48
+ </select>
49
+
50
+ { Display(errors.group_id) }
51
+ </Choose>
52
+
53
+ <Button
54
+ loading={ isPending }
55
+ text={
56
+ <>
57
+ <FiPlus />
58
+ { t('locations_manage.add_city.action', { ns: 'common' }) }
59
+ </>
60
+ }
61
+ noMargin
62
+ />
63
+ </ContainerAdd>
64
+
65
+ <Notice>{ t('locations_manage.add_city.notice', { ns: 'common' }) }</Notice>
66
+ </form>
67
+ </Container>
68
+ );
69
+ };
70
+
71
+ export default Add;
@@ -0,0 +1,17 @@
1
+ import { TFunction } from 'i18next';
2
+ import { Inline } from '@autobusal/common';
3
+ import { Container, Title } from './styles';
4
+
5
+ interface Props {
6
+ t: TFunction<'normal'>
7
+ }
8
+
9
+ const Loading = ({ t }: Props): JSX.Element => (
10
+ <Container className="box">
11
+ <Title>{ t('locations_manage.add_city.title', { ns: 'common' }) }</Title>
12
+
13
+ <Inline text={ t('locations_manage.add_city.loading', { ns: 'common' }) } />
14
+ </Container>
15
+ );
16
+
17
+ export default Loading;
@@ -0,0 +1,39 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ margin-bottom: 25px;
5
+ `;
6
+
7
+ export const Title = styled.h5`
8
+ margin-bottom: 10px;
9
+ padding-bottom: 15px;
10
+ font-size: ${ props => props.theme.size.m };
11
+ border-bottom: 1px solid ${ props => props.theme.background.neutral };
12
+ `;
13
+
14
+ export const ContainerAdd = styled.div`
15
+ display: flex;
16
+ align-items: center;
17
+ flex-direction: column;
18
+ gap: 20px;
19
+
20
+ @media (min-width: 640px) {
21
+ flex-direction: row;
22
+ }
23
+ `;
24
+
25
+ export const Choose = styled.div`
26
+ flex: 1;
27
+ width: 100%;
28
+ `;
29
+
30
+ export const Notice = styled.div`
31
+ margin-top: 15px;
32
+ font-size: ${ props => props.theme.size.xs };
33
+ color: ${ props => props.theme.font.info };
34
+ text-align: center;
35
+
36
+ @media (min-width: 640px) {
37
+ text-align: left;
38
+ }
39
+ `;
@@ -0,0 +1,90 @@
1
+ import { TFunction } from 'i18next';
2
+ import { FiArrowDownCircle, FiArrowUpCircle } from 'react-icons/fi';
3
+ import { BiEditAlt, BiTrash } from 'react-icons/bi';
4
+ import { Button } from '@autobusal/common';
5
+ import { ContainerActions, Group } from './styles';
6
+ import { LocationData } from '@autobusal/providers/types/locations';
7
+
8
+ interface Props {
9
+ item: LocationData
10
+ loading: boolean
11
+ t: TFunction<'normal'>
12
+ onMove: (order: number) => void
13
+ onDelete: () => void
14
+ }
15
+
16
+ const Actions = ({ item, loading, t, onMove, onDelete }: Props): JSX.Element => {
17
+ const onChange = (type: 'up' | 'down'): void => {
18
+ if (!window.confirm(t('locations_manage.actions.move', { ns: 'common' }))) {
19
+ return;
20
+ }
21
+
22
+ const order = type === 'up' ? item.order_id - 1 : item.order_id + 1;
23
+
24
+ onMove(order);
25
+ };
26
+
27
+ const onAskDelete = (): void => {
28
+ if (window.confirm(t('locations_manage.actions.delete', { ns: 'common' }))) {
29
+ onDelete();
30
+ }
31
+ };
32
+
33
+ return (
34
+ <ContainerActions>
35
+ <Group>
36
+ <Button
37
+ type="button"
38
+ loading={ loading }
39
+ subtype="secondary"
40
+ size="small"
41
+ text={ <FiArrowUpCircle /> }
42
+ noMargin
43
+ onClick={ () => onChange('up') }
44
+ />
45
+
46
+ <Button
47
+ type="button"
48
+ subtype="secondary"
49
+ loading={ loading }
50
+ size="small"
51
+ text={ <FiArrowDownCircle /> }
52
+ noMargin
53
+ onClick={ () => onChange('down') }
54
+ />
55
+ </Group>
56
+
57
+ <Group>
58
+ <Button
59
+ type="submit"
60
+ loading={ loading }
61
+ size="small"
62
+ text={
63
+ <>
64
+ <BiEditAlt />
65
+ { t('table.actions.update', { ns: 'common' }) }
66
+ </>
67
+ }
68
+ noMargin
69
+ />
70
+
71
+ <Button
72
+ type="button"
73
+ loading={ loading }
74
+ size="small"
75
+ subtype="delete"
76
+ text={
77
+ <>
78
+ <BiTrash />
79
+ { t('table.actions.delete', { ns: 'common' }) }
80
+ </>
81
+ }
82
+ noMargin
83
+ onClick={ onAskDelete }
84
+ />
85
+ </Group>
86
+ </ContainerActions>
87
+ );
88
+ };
89
+
90
+ export default Actions;
@@ -0,0 +1,45 @@
1
+ import { TFunction } from 'i18next';
2
+ import Item from './Item';
3
+ import { Items, NotFound } from './styles';
4
+ import { LocationData } from '@autobusal/providers/types/locations';
5
+
6
+ interface Props {
7
+ routeId: number
8
+ data?: LocationData[]
9
+ t: TFunction<'normal'>
10
+ onReload: () => void
11
+ }
12
+
13
+ const Browse = ({ routeId, data, t, onReload }: Props): JSX.Element => {
14
+ if (data === undefined || data.length === 0) {
15
+ return (
16
+ <NotFound className="box">
17
+ { t('locations_manage.no_locations', { ns: 'common' }) }
18
+ </NotFound>
19
+ );
20
+ }
21
+
22
+ const items = data.map(item => (
23
+ <Item
24
+ key={ item.id }
25
+ item={ item }
26
+ routeId={ routeId }
27
+ t={ t }
28
+ onReload={ onReload }
29
+ />
30
+ ));
31
+
32
+ return (
33
+ <>
34
+ <h2>
35
+ { t('locations_manage.browse', { ns: 'common' }) }
36
+ </h2>
37
+
38
+ <Items>
39
+ { items }
40
+ </Items>
41
+ </>
42
+ );
43
+ };
44
+
45
+ export default Browse;
@@ -0,0 +1,92 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useForm } from 'react-hook-form';
3
+ import { Validate, Display, success } from '@autobusal/utilities';
4
+ import Actions from './Actions';
5
+ import { getTomorrow } from '../utilities';
6
+ import { Data } from './styles';
7
+ import { LocationForm } from '../types';
8
+ import { LocationData } from '@autobusal/providers/types/locations';
9
+ import { PostMove, DeleteLocation, PostUpdate } from '../services';
10
+
11
+ interface Props {
12
+ routeId: number
13
+ item: LocationData
14
+ t: TFunction<'normal'>
15
+ onReload: () => void
16
+ }
17
+
18
+ const Item = ({ routeId, item, t, onReload }: Props): JSX.Element => {
19
+ const { register, handleSubmit, formState: { errors } } = useForm<LocationForm>();
20
+
21
+ const { mutate: Save, isPending: isPendingUpdate } = PostUpdate(routeId, item.id);
22
+
23
+ const { mutate: Move, isPending: isPendingMove } = PostMove(routeId, item.id);
24
+
25
+ const { mutate: Delete, isPending: isPendingDelete } = DeleteLocation(routeId, item.id);
26
+
27
+ const onSubmit = (data: LocationForm): void => {
28
+ Save(data);
29
+ };
30
+
31
+ const onMove = (order: number): void => {
32
+ Move(order, {
33
+ onSuccess: () => {
34
+ success(t('locations_manage.messages.moved', { ns: 'common' }));
35
+
36
+ onReload();
37
+ }
38
+ });
39
+ };
40
+
41
+ const onDelete = (): void => {
42
+ Delete({}, {
43
+ onSuccess: () => {
44
+ success(t('locations_manage.messages.deleted', { ns: 'common' }));
45
+
46
+ onReload();
47
+ }
48
+ });
49
+ };
50
+
51
+ return (
52
+ <div className="box">
53
+ <h4>{ item.city.country?.name } &rsaquo; { item.city.name } &rsaquo; { item.stop?.name }</h4>
54
+
55
+ <form onSubmit={ handleSubmit(onSubmit) }>
56
+ <Data>
57
+ <div className="row">
58
+ { t('locations_manage.city.tomorrow', { ns: 'common' }) }
59
+
60
+ <select defaultValue={ item.tomorrow } { ...register('tomorrow') }>
61
+ { getTomorrow(t) }
62
+ </select>
63
+ </div>
64
+
65
+ <div className="row">
66
+ { t('locations_manage.city.departure', { ns: 'common' }) }
67
+
68
+ <input type="text" defaultValue={ item.departure } { ...register('departure', Validate('required', t)) } />
69
+ { Display(errors.departure) }
70
+ </div>
71
+
72
+ <div className="row">
73
+ { t('locations_manage.city.arrival', { ns: 'common' }) }
74
+
75
+ <input type="text" defaultValue={ item.arrival } { ...register('arrival', Validate('required', t)) } />
76
+ { Display(errors.arrival) }
77
+ </div>
78
+ </Data>
79
+
80
+ <Actions
81
+ item={ item }
82
+ loading={ isPendingMove || isPendingDelete || isPendingUpdate }
83
+ t={ t }
84
+ onMove={ onMove }
85
+ onDelete={ onDelete }
86
+ />
87
+ </form>
88
+ </div>
89
+ );
90
+ };
91
+
92
+ export default Item;
@@ -0,0 +1,36 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Items = styled.div`
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 15px;
7
+ `;
8
+
9
+ export const NotFound = styled.div`
10
+ margin-top: 15px;
11
+ padding: 30px 0;
12
+ color: ${ props => props.theme.font.neutral };
13
+ text-align: center;
14
+ font-size: ${ props => props.theme.size.xs };
15
+ `;
16
+
17
+ export const Data = styled.div`
18
+ display: flex;
19
+ gap: 10px;
20
+ flex-direction: column;
21
+
22
+ @media (min-width: 480px) {
23
+ flex-direction: row;
24
+ gap: 15px;
25
+ }
26
+ `;
27
+
28
+ export const ContainerActions = styled.div`
29
+ display: flex;
30
+ justify-content: space-between;
31
+ `;
32
+
33
+ export const Group = styled.div`
34
+ display: flex;
35
+ gap: 10px;
36
+ `;
@@ -0,0 +1,44 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useParams } from 'react-router-dom';
3
+ import { Meta, BackWithTitle, Report } from '@autobusal/common';
4
+ import { usePage } from '@autobusal/hooks';
5
+ import Add from './Add/Add';
6
+ import Browse from './Browse/Browse';
7
+ import { GetLocations } from './services';
8
+
9
+ interface Props {
10
+ url: string
11
+ t: TFunction<'common'>
12
+ }
13
+
14
+ const Manage = ({ url, t }: Props): JSX.Element => {
15
+ const params = useParams();
16
+
17
+ const id = Number(params.id);
18
+
19
+ url = url.replace('[id]', String(id));
20
+
21
+ usePage('routes');
22
+
23
+ const { data, refetch } = GetLocations(id);
24
+
25
+ const onReload = (): void => {
26
+ refetch();
27
+ };
28
+
29
+ const title = t('locations_manage.title', { name: data?.name, ns: 'common' });
30
+
31
+ return (
32
+ <Meta title={ title }>
33
+ <BackWithTitle title={ title } to={ url } t={ t } />
34
+
35
+ <Add routeId={ id } t={ t } onReload={ onReload } />
36
+
37
+ <Report margin={ 15 } question={ t('locations_manage.report', { ns: 'common' }) } type="location" t={ t } />
38
+
39
+ <Browse routeId={ id } data={ data?.locations } t={ t } onReload={ onReload } />
40
+ </Meta>
41
+ );
42
+ };
43
+
44
+ export default Manage;
@@ -0,0 +1,105 @@
1
+ import { UseMutationResult, UseQueryResult, UseSuspenseQueryResult, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
2
+ import { apiClient } from '@autobusal/providers';
3
+ import { RouteData } from '@autobusal/providers/types/routes';
4
+ import { CountryData } from '@autobusal/providers/types/locations';
5
+ import { AddForm, LocationForm } from './types';
6
+
7
+ export const GetLocations = (id: number): UseSuspenseQueryResult<RouteData> => (
8
+ useSuspenseQuery({
9
+ queryKey: ['operator-locations', { id }],
10
+ queryFn: async () => (
11
+ await apiClient
12
+ .get('/api/locations/operator/get', {
13
+ params: { id }
14
+ })
15
+ .then(response => (
16
+ response.data
17
+ ))
18
+ )
19
+ })
20
+ );
21
+
22
+ export const GetStops = (routeId: number): UseQueryResult<CountryData[]> => (
23
+ useQuery({
24
+ queryKey: ['operator-stops', { routeId }],
25
+ queryFn: async () => (
26
+ await apiClient
27
+ .get('/api/stops/operator/browse', {
28
+ params: {
29
+ route_id: routeId
30
+ }
31
+ })
32
+ .then(response => (
33
+ response.data
34
+ ))
35
+ )
36
+ })
37
+ );
38
+
39
+ export const AddLocation = (id: number): UseMutationResult<void, Error, AddForm, unknown> => (
40
+ useMutation({
41
+ mutationKey: ['operator-locations-add', { id }],
42
+ mutationFn: async (data: AddForm) => {
43
+ await apiClient
44
+ .post('/api/locations/operator/add', {
45
+ ...data,
46
+ id
47
+ })
48
+ .then(response => (
49
+ response.data
50
+ ))
51
+ }
52
+ })
53
+ );
54
+
55
+ export const PostMove = (id: number, location_id: number): UseMutationResult<void, Error, number, unknown> => (
56
+ useMutation({
57
+ mutationKey: ['operator-locations-move', { id, location_id }],
58
+ mutationFn: async (order: number) => (
59
+ await apiClient
60
+ .post('/api/locations/operator/move', {
61
+ order,
62
+ id,
63
+ location_id
64
+ })
65
+ .then(response => (
66
+ response.data
67
+ ))
68
+ )
69
+ })
70
+ );
71
+
72
+ export const DeleteLocation = (id: number, location_id: number): UseMutationResult<void> => (
73
+ useMutation({
74
+ mutationKey: ['operator-locations-delete', { id, location_id }],
75
+ mutationFn: async () => (
76
+ await apiClient
77
+ .delete('/api/locations/operator/delete', {
78
+ params: {
79
+ id,
80
+ location_id
81
+ }
82
+ })
83
+ .then(response => (
84
+ response.data
85
+ ))
86
+ )
87
+ })
88
+ );
89
+
90
+ export const PostUpdate = (id: number, location_id: number): UseMutationResult<void, Error, LocationForm, unknown> => (
91
+ useMutation({
92
+ mutationKey: ['operator-locations-update', { id, location_id }],
93
+ mutationFn: async (data: LocationForm) => (
94
+ await apiClient
95
+ .post('/api/locations/operator/update', {
96
+ ...data,
97
+ id,
98
+ location_id
99
+ })
100
+ .then(response => (
101
+ response.data
102
+ ))
103
+ )
104
+ })
105
+ );
@@ -0,0 +1,9 @@
1
+ export interface AddForm {
2
+ group_id: string
3
+ }
4
+
5
+ export interface LocationForm {
6
+ tomorrow: number
7
+ departure: string
8
+ arrival: string
9
+ }
@@ -0,0 +1,53 @@
1
+ import { TFunction } from 'i18next';
2
+ import { CityData, CountryData } from '@autobusal/providers/types/locations';
3
+
4
+ export const getStops = (t: TFunction<'normal'>, countries?: CountryData[]): JSX.Element[] => {
5
+ const options: JSX.Element[] = [
6
+ <option key="-none=" value="">{ t('locations_manage.add_city.choose', { ns: 'common' }) }</option>
7
+ ];
8
+
9
+ countries?.forEach(country => {
10
+ options.push(
11
+ <optgroup key={ country.id } label={ country.name }>
12
+ { getOptions(country.cities) }
13
+ </optgroup>
14
+ );
15
+ });
16
+
17
+ return options;
18
+ };
19
+
20
+ const getOptions = (cities?: CityData[]): JSX.Element[] => {
21
+ const options: JSX.Element[] = [];
22
+
23
+ cities?.forEach(city => {
24
+ city.stops?.forEach(stop => {
25
+ const value = `${ city.id }#${ stop.id }`;
26
+
27
+ options.push(
28
+ <option key={ value } value={ value }>{ city.name } &rsaquo; { stop.name }</option>
29
+ );
30
+ });
31
+ });
32
+
33
+ return options;
34
+ };
35
+
36
+ export const getTomorrow = (t: TFunction<'normal'>): JSX.Element[] => {
37
+ const available = [
38
+ { name: t('locations_manage.tomorrow.same_day', { ns: 'common' }), value: 0 },
39
+ { name: t('locations_manage.tomorrow.1_day', { ns: 'common' }), value: 1 },
40
+ { name: t('locations_manage.tomorrow.2_days', { ns: 'common' }), value: 2 },
41
+ { name: t('locations_manage.tomorrow.3_days', { ns: 'common' }), value: 3 },
42
+ { name: t('locations_manage.tomorrow.4_days', { ns: 'common' }), value: 4 },
43
+ { name: t('locations_manage.tomorrow.5_days', { ns: 'common' }), value: 5 },
44
+ { name: t('locations_manage.tomorrow.6_days', { ns: 'common' }), value: 6 },
45
+ { name: t('locations_manage.tomorrow.7_days', { ns: 'common' }), value: 7 }
46
+ ];
47
+
48
+ const options = available.map(item => (
49
+ <option key={ item.value } value={ item.value }>{ item.name }</option>
50
+ ));
51
+
52
+ return options;
53
+ };
@@ -17,7 +17,7 @@ const Countries = ({ transiting, t, onDelete }: Props): JSX.Element => {
17
17
  }
18
18
 
19
19
  const onDeleteConfirm = (id: number): void => {
20
- if (window.confirm(t('private.transiting_manage.confirm'))) {
20
+ if (window.confirm(t('transiting_manage.confirm', { ns: 'confirm' }))) {
21
21
  onDelete(id);
22
22
  }
23
23
  };
@@ -0,0 +1,72 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useParams, Navigate } from 'react-router-dom';
3
+ import { Meta, BackWithTitle, Table, Row } from '@autobusal/common';
4
+ import { usePage } from '@autobusal/hooks';
5
+ import { GetUnavailableDates, DeleteUnavailable } from './services';
6
+
7
+ interface Props {
8
+ url: string
9
+ t: TFunction<'common'>
10
+ }
11
+
12
+ const Browse = ({ url, t }: Props): JSX.Element => {
13
+ const params = useParams();
14
+
15
+ const id = Number(params.id);
16
+
17
+ url = url.replace('[id]', String(id));
18
+
19
+ usePage('routes');
20
+
21
+ const { data, isLoading, isFetching, refetch } = GetUnavailableDates(id);
22
+
23
+ const { mutate: Delete } = DeleteUnavailable(id);
24
+
25
+ if (id === 0) {
26
+ return <Navigate to={ url } />;
27
+ }
28
+
29
+ const onDelete = (id: number): void => {
30
+ Delete(id, {
31
+ onSuccess: () => refetch()
32
+ });
33
+ };
34
+
35
+ const rows = data?.unavailable.map(item => (
36
+ <Row
37
+ key={ item.id }
38
+ id={ item.id }
39
+ data={ [
40
+ `${ item.start_inactive } - ${ item.end_inactive }`
41
+ ] }
42
+ />
43
+ ));
44
+
45
+ const title = t('unavailable.browse.title', { name: data?.name, ns: 'common' });
46
+
47
+ return (
48
+ <Meta title={ title }>
49
+ <BackWithTitle title={ title } to={ url } t={ t } />
50
+
51
+ <Table
52
+ url={ `/operator/routes/unavailable/${ id }` }
53
+ columns={ [{
54
+ name: t('unavailable.browse.date', { ns: 'common' }),
55
+ width: 90
56
+ }] }
57
+ rows={ rows }
58
+ loading={ isLoading }
59
+ fetching={ isFetching }
60
+ t={ t }
61
+ actions={ [
62
+ 'create', 'view', 'delete'
63
+ ] }
64
+ handlers={ {
65
+ delete: onDelete
66
+ } }
67
+ />
68
+ </Meta>
69
+ );
70
+ };
71
+
72
+ export default Browse;
@@ -0,0 +1,91 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useParams, Navigate, useNavigate } from 'react-router-dom';
3
+ import { Meta, BackWithTitle, Viewer } from '@autobusal/common';
4
+ import { usePage } from '@autobusal/hooks';
5
+ import { success } from '@autobusal/utilities';
6
+ import { UnavailableData } from '@autobusal/providers/types/routes';
7
+ import { GetUnavailable, PostUnavailable, DeleteUnavailable } from './services';
8
+
9
+ interface Props {
10
+ url: string
11
+ t: TFunction<'common'>
12
+ }
13
+
14
+ const Manage = ({ url, t }: Props): JSX.Element => {
15
+ const params = useParams();
16
+
17
+ const id = Number(params.id);
18
+ const routeId = Number(params.route_id);
19
+
20
+ url = url.replace('[routeId]', String(routeId));
21
+
22
+ usePage('routes');
23
+
24
+ const navigate = useNavigate();
25
+
26
+ const { data } = GetUnavailable(routeId, id);
27
+
28
+ const { mutate: Update, isPending: isPendingSave } = PostUnavailable(routeId, id);
29
+
30
+ const { mutate: Delete, isPending: isPendingDelete } = DeleteUnavailable(routeId);
31
+
32
+ if (routeId === 0) {
33
+ return <Navigate to={ url } />;
34
+ }
35
+
36
+ const onSave = (data: UnavailableData): void => {
37
+ Update(data, {
38
+ onSuccess: () => {
39
+ success(t('unavailable.manage.messages.saved', { ns: 'common' }));
40
+
41
+ navigate(url);
42
+ }
43
+ });
44
+ };
45
+
46
+ const onDelete = (): void => {
47
+ Delete(id, {
48
+ onSuccess: () => {
49
+ success(t('unavailable.manage.messages.deleted', { ns: 'common' }));
50
+
51
+ navigate(url);
52
+ }
53
+ });
54
+ };
55
+
56
+ const title = id > 0 ? t('unavailable.manage.title.update', { name: data?.name, ns: 'common' }) : t('unavailable.manage.title.new', { name: data?.name, ns: 'common' });
57
+
58
+ const unavailable = data?.unavailable !== undefined && data.unavailable.length > 0 ? data.unavailable[0] : undefined;
59
+
60
+ return (
61
+ <Meta title={ title }>
62
+ <BackWithTitle title={ title } to={ url } t={ t } />
63
+
64
+ <Viewer
65
+ id={ id }
66
+ data={ [{
67
+ label: t('unavailable.manage.start_inactive', { ns: 'common' }),
68
+ name: 'start_inactive',
69
+ type: 'picker',
70
+ value: unavailable?.start_inactive,
71
+ rules: 'required'
72
+ }, {
73
+ label: t('unavailable.manage.end_inactive', { ns: 'common' }),
74
+ name: 'end_inactive',
75
+ type: 'picker',
76
+ value: unavailable?.end_inactive,
77
+ rules: 'required'
78
+ }] }
79
+ pending={ isPendingSave || isPendingDelete }
80
+ t={ t }
81
+ actions={ [
82
+ 'save', 'delete'
83
+ ]}
84
+ onSave={ onSave }
85
+ onDelete={ onDelete }
86
+ />
87
+ </Meta>
88
+ );
89
+ };
90
+
91
+ export default Manage;
@@ -0,0 +1,72 @@
1
+ import { UseMutationResult, UseQueryResult, UseSuspenseQueryResult, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
2
+ import { apiClient } from '@autobusal/providers';
3
+ import { RouteData } from '@autobusal/providers/types/routes';
4
+ import { UnavailableData } from '@autobusal/providers/types/routes';
5
+
6
+ export const GetUnavailableDates = (id: number): UseQueryResult<RouteData> => (
7
+ useQuery({
8
+ queryKey: ['operators-unavailable', { id }],
9
+ queryFn: async () => (
10
+ await apiClient
11
+ .get('/api/unavailable/operator/browse', {
12
+ params: { id }
13
+ })
14
+ .then(response => (
15
+ response.data
16
+ ))
17
+ )
18
+ })
19
+ );
20
+
21
+ export const GetUnavailable = (routeId: number, id: number): UseSuspenseQueryResult<RouteData> => (
22
+ useSuspenseQuery({
23
+ queryKey: ['operator-unavailable-manage', { routeId, id }],
24
+ queryFn: async () => (
25
+ await apiClient
26
+ .get('/api/unavailable/operator/get', {
27
+ params: {
28
+ route_id: routeId,
29
+ id
30
+ }
31
+ })
32
+ .then(response => (
33
+ response.data
34
+ ))
35
+ )
36
+ })
37
+ );
38
+
39
+ export const PostUnavailable = (routeId: number, id: number): UseMutationResult<void, Error, UnavailableData, unknown> => (
40
+ useMutation({
41
+ mutationKey: ['unavailable-operator-save', { routeId, id }],
42
+ mutationFn: async (data: UnavailableData) => (
43
+ await apiClient
44
+ .post('/api/unavailable/operator/update', {
45
+ ...data,
46
+ route_id: routeId,
47
+ id
48
+ })
49
+ .then(response => (
50
+ response.data
51
+ ))
52
+ )
53
+ })
54
+ );
55
+
56
+ export const DeleteUnavailable = (routeId: number): UseMutationResult<void, Error, number, unknown> => (
57
+ useMutation({
58
+ mutationKey: ['unavailable-operator-delete', { routeId }],
59
+ mutationFn: async (id: number) => (
60
+ await apiClient
61
+ .delete('/api/unavailable/operator/delete', {
62
+ params: {
63
+ route_id: routeId,
64
+ id
65
+ }
66
+ })
67
+ .then(response => (
68
+ response.data
69
+ ))
70
+ )
71
+ })
72
+ );
package/index.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import Browse from './Browse';
2
2
  import Manage from './Manage';
3
+ import LocationsManage from './Locations/Manage';
3
4
  import SchedulesManage from './Schedule/Manage';
4
5
  import TransitingManage from './Transiting/Manage';
6
+ import UnavailableBrowse from './Unavailable/Browse';
7
+ import UnavailableManage from './Unavailable/Manage';
5
8
 
6
9
  export {
7
10
  Browse,
8
11
  Manage,
12
+ LocationsManage,
9
13
  SchedulesManage,
10
- TransitingManage
14
+ TransitingManage,
15
+ UnavailableBrowse,
16
+ UnavailableManage
11
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/operator-routes",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "main": "index.ts"
6
6
  }