Revize 9c55d3bb
Přidáno uživatelem Schwobik před asi 2 roky(ů)
package.json | ||
---|---|---|
22 | 22 |
"react-native": "0.71.6", |
23 | 23 |
"react-native-deck-swiper": "^2.0.13", |
24 | 24 |
"react-native-gesture-handler": "~2.9.0", |
25 |
"react-native-multiple-select": "^0.5.12", |
|
25 | 26 |
"react-native-reanimated": "~2.14.4", |
26 | 27 |
"react-native-safe-area-context": "4.5.0", |
27 | 28 |
"react-native-screens": "~3.20.0", |
28 | 29 |
"react-native-svg": "13.4.0", |
30 |
"react-native-vector-icons": "^9.2.0", |
|
29 | 31 |
"react-redux": "^8.0.5", |
30 | 32 |
"redux": "^4.2.1", |
31 | 33 |
"typescript": "^4.9.4" |
src/api/searchFormService.ts | ||
---|---|---|
1 |
import { axiosInstance } from "./api" |
|
2 |
|
|
3 |
export const fetchInventoriesRequest = async () => { |
|
4 |
return await axiosInstance.get("/inventories") |
|
5 |
} |
|
6 |
|
|
7 |
export const fetchCountriesRequest = async () => { |
|
8 |
return await axiosInstance.get("/form/country") |
|
9 |
} |
|
10 |
|
|
11 |
export const fetchInstitutionsRequest = async () => { |
|
12 |
return await axiosInstance.get("/form/institution") |
|
13 |
} |
|
14 |
|
|
15 |
export const fetchCitiesRequest = async () => { |
|
16 |
return await axiosInstance.get("/form/city") |
|
17 |
} |
|
18 |
|
|
19 |
export const fetchNationalitiesRequest = async () => { |
|
20 |
return await axiosInstance.get("/form/nationality") |
|
21 |
} |
|
22 |
|
|
23 |
export const fetchTechniquesRequest = async () => { |
|
24 |
return await axiosInstance.get("/form/technique") |
|
25 |
} |
|
26 |
|
|
27 |
export const fetchSubjectsRequest = async () => { |
|
28 |
return await axiosInstance.get("/subject") |
|
29 |
} |
|
30 |
|
|
31 |
export const fetchArtistNamesRequest = async () => { |
|
32 |
return await axiosInstance.get("/name/all") |
|
33 |
} |
|
34 |
|
|
35 |
export const fetchPlanRequest = async () => { |
|
36 |
return await axiosInstance.get("/plan/list") |
|
37 |
} |
src/api/searchService.ts | ||
---|---|---|
1 |
import { axiosInstance } from "./api" |
|
2 |
|
|
3 |
export interface SearchParams { |
|
4 |
inventory?: string |
|
5 |
//TODO: add other search params |
|
6 |
} |
|
7 |
|
|
8 |
export const searchRequest = async (params: SearchParams) => { |
|
9 |
return await axiosInstance.get(`/api/search_v2/${ (params.inventory ? `inventory=${ params.inventory }` : "")}`) |
|
10 |
} |
src/components/SearchFormControl.tsx | ||
---|---|---|
1 |
import { CheckIcon, FormControl, Select } from "native-base" |
|
2 |
import MultiSelect from "react-native-multiple-select" |
|
3 |
|
|
4 |
export interface SearchFormProps { |
|
5 |
data: { label: string, value: string, key: string }[], |
|
6 |
label: string, |
|
7 |
placeholder: string, |
|
8 |
} |
|
9 |
|
|
10 |
const SearchFormControl = (props: SearchFormProps) => { |
|
11 |
return ( |
|
12 |
<FormControl key={props.label}> |
|
13 |
<FormControl.Label>{props.label}</FormControl.Label> |
|
14 |
<Select |
|
15 |
placeholder={props.placeholder} |
|
16 |
minWidth="200" |
|
17 |
accessibilityLabel="Choose Service" |
|
18 |
_selectedItem={{ |
|
19 |
bg: "teal.600", |
|
20 |
endIcon: <CheckIcon size={5} /> |
|
21 |
}} |
|
22 |
mt="1" |
|
23 |
key={props.label} |
|
24 |
> |
|
25 |
{props.data.map((row) => ( |
|
26 |
<Select.Item label={row.label} value={row.value} key={row.key}/> |
|
27 |
))} |
|
28 |
</Select> |
|
29 |
</FormControl> |
|
30 |
) |
|
31 |
} |
|
32 |
|
|
33 |
export default SearchFormControl |
src/components/listView/ItemPreview.tsx | ||
---|---|---|
1 |
import { Center } from "native-base" |
|
2 |
|
|
3 |
interface ItemPreviewProps { |
|
4 |
caption: string |
|
5 |
title: string |
|
6 |
name: string |
|
7 |
image: string |
|
8 |
} |
|
9 |
|
|
10 |
const ItemPreview = (props: ItemPreviewProps) => { |
|
11 |
|
|
12 |
return ( |
|
13 |
<> |
|
14 |
<Center> |
|
15 |
|
|
16 |
</Center> |
|
17 |
</> |
|
18 |
) |
|
19 |
} |
|
20 |
|
|
21 |
export default ItemPreview |
src/components/listView/ListView.tsx | ||
---|---|---|
1 |
import { useEffect, useState } from "react" |
|
2 |
import { Text } from "native-base" |
|
3 |
import { useSelector } from "react-redux" |
|
4 |
import { RootState } from "../../stores/store" |
|
5 |
|
|
6 |
const ListView = () => { |
|
7 |
const items = useSelector((state: RootState) => state.listView.data) |
|
8 |
|
|
9 |
return ( |
|
10 |
<> |
|
11 |
<Text>ListView</Text> |
|
12 |
</> |
|
13 |
) |
|
14 |
} |
|
15 |
|
|
16 |
export default ListView |
src/components/search/SearchForm.tsx | ||
---|---|---|
1 |
import { Button, Center, CheckIcon, FormControl, KeyboardAvoidingView, ScrollView, Select, Text } from "native-base" |
|
2 |
import { useDispatch, useSelector } from "react-redux" |
|
3 |
import React, { useEffect, useState } from "react" |
|
4 |
import { AppDispatch, RootState } from "../../stores/store" |
|
5 |
import { Inventory } from "../../types/searchFormTypes" |
|
6 |
import { |
|
7 |
fetchArtists, fetchCities, fetchCountries, fetchInstitutions, |
|
8 |
fetchInventories, |
|
9 |
fetchNationalities, |
|
10 |
fetchPlans, |
|
11 |
fetchSubjects |
|
12 |
} from "../../stores/actions/searchFormThunks" |
|
13 |
import SearchFormControl from "./SearchFormControl" |
|
14 |
import { Platform } from "react-native" |
|
15 |
import { search } from "../../stores/actions/listViewThunks" |
|
16 |
|
|
17 |
|
|
18 |
const SearchForm = () => { |
|
19 |
const inventories: Inventory[] = useSelector((state: RootState) => state.searchForm.inventories) |
|
20 |
const nationalities = useSelector((state: RootState) => state.searchForm.nationalities) |
|
21 |
const artists = useSelector((state: RootState) => state.searchForm.artists) |
|
22 |
const subjects = useSelector((state: RootState) => state.searchForm.subjects) |
|
23 |
const rooms = useSelector((state: RootState) => state.searchForm.rooms) |
|
24 |
const countries = useSelector((state: RootState) => state.searchForm.countries) |
|
25 |
const cities = useSelector((state: RootState) => state.searchForm.cities) |
|
26 |
const institutions = useSelector((state: RootState) => state.searchForm.institutions) |
|
27 |
|
|
28 |
|
|
29 |
const [selectedInventories, setSelectedInventories] = useState<string[]>([]) |
|
30 |
const [selectedNationalities, setSelectedNationalities] = useState<string[]>([]) |
|
31 |
const [selectedArtists, setSelectedArtists] = useState<string[]>([]) |
|
32 |
const [selectedSubjects, setSelectedSubjects] = useState<string[]>([]) |
|
33 |
const [selectedRooms, setSelectedRooms] = useState<string[]>([]) |
|
34 |
const [selectedCountries, setSelectedCountries] = useState<string[]>([]) |
|
35 |
const [selectedCities, setSelectedCities] = useState<string[]>([]) |
|
36 |
const [selectedInstitutions, setSelectedInstitutions] = useState<string[]>([]) |
|
37 |
|
|
38 |
const dispatch = useDispatch<AppDispatch>() |
|
39 |
|
|
40 |
useEffect(() => { |
|
41 |
dispatch(fetchInventories()) |
|
42 |
dispatch(fetchNationalities()) |
|
43 |
dispatch(fetchArtists()) |
|
44 |
dispatch(fetchSubjects()) |
|
45 |
dispatch(fetchPlans()) |
|
46 |
dispatch(fetchCountries()) |
|
47 |
dispatch(fetchCities()) |
|
48 |
dispatch(fetchInstitutions()) |
|
49 |
}, [dispatch]) |
|
50 |
|
|
51 |
|
|
52 |
const searchSubmit = () => { |
|
53 |
console.log("search") |
|
54 |
dispatch(search({inventory: selectedInventories ? selectedInventories[0] : undefined})) |
|
55 |
} |
|
56 |
|
|
57 |
return ( |
|
58 |
<Center> |
|
59 |
<ScrollView> |
|
60 |
<SearchFormControl |
|
61 |
data={ inventories.map(inventory => { |
|
62 |
return {label: inventory.label, value: inventory.name, key: inventory.name} |
|
63 |
}) } |
|
64 |
label="Inventory" |
|
65 |
placeholder="Choose Inventory" |
|
66 |
selectedItems={ selectedInventories } |
|
67 |
onSelectedItemsChange={ setSelectedInventories } |
|
68 |
/> |
|
69 |
<SearchFormControl |
|
70 |
data={ rooms.map(room => { |
|
71 |
return {label: room.label, value: room.id.toString(), key: room.id.toString()} |
|
72 |
}) } |
|
73 |
label="Rooms" |
|
74 |
placeholder="Room..." |
|
75 |
selectedItems={ selectedRooms } |
|
76 |
onSelectedItemsChange={ setSelectedRooms } |
|
77 |
/> |
|
78 |
<SearchFormControl |
|
79 |
data={ artists.map(art => { |
|
80 |
return {label: art.display_name, value: art.display_name, key: art.display_name} |
|
81 |
}) } |
|
82 |
label="Artists/Copyists" |
|
83 |
placeholder="Artist/Copyist..." |
|
84 |
selectedItems={ selectedArtists } |
|
85 |
onSelectedItemsChange={ setSelectedArtists } |
|
86 |
/> |
|
87 |
<SearchFormControl |
|
88 |
data={ nationalities.map(nat => { |
|
89 |
return {label: nat, value: nat, key: nat} |
|
90 |
}) } |
|
91 |
label="Artist`s Origin" |
|
92 |
placeholder="Nationality..." |
|
93 |
selectedItems={ selectedNationalities } |
|
94 |
onSelectedItemsChange={ setSelectedNationalities } |
|
95 |
/> |
|
96 |
<FormControl key={ "lab" }> |
|
97 |
<FormControl.Label>label</FormControl.Label> |
|
98 |
<Select |
|
99 |
placeholder={ "Select" } |
|
100 |
minWidth="100" |
|
101 |
accessibilityLabel="Choose Service" |
|
102 |
_selectedItem={ { |
|
103 |
bg: "teal.600", |
|
104 |
endIcon: <CheckIcon size={ 5 }/> |
|
105 |
} } |
|
106 |
mt="1" |
|
107 |
key={ "props.label" } |
|
108 |
|
|
109 |
> |
|
110 |
{ inventories.map((row) => ( |
|
111 |
<Select.Item label={ row.label } value={ row.name } key={ row.name }/> |
|
112 |
)) } |
|
113 |
</Select> |
|
114 |
</FormControl> |
|
115 |
<Button |
|
116 |
onPress={ () => searchSubmit()} |
|
117 |
colorScheme="primary.100" |
|
118 |
> |
|
119 |
Submit |
|
120 |
</Button> |
|
121 |
</ScrollView> |
|
122 |
|
|
123 |
</Center> |
|
124 |
) |
|
125 |
} |
|
126 |
|
|
127 |
export default SearchForm |
src/components/search/SearchFormControl.tsx | ||
---|---|---|
1 |
import { CheckIcon, FormControl, Select, View } from "native-base" |
|
2 |
import MultiSelect from "react-native-multiple-select" |
|
3 |
|
|
4 |
export interface SearchFormProps { |
|
5 |
data: { label: string, value: string, key: string }[], |
|
6 |
label: string, |
|
7 |
placeholder: string, |
|
8 |
selectedItems: string[], |
|
9 |
onSelectedItemsChange: (selectedItems: string[]) => void, |
|
10 |
} |
|
11 |
|
|
12 |
const SearchFormControl = (props: SearchFormProps) => { |
|
13 |
return ( |
|
14 |
<View w={350}> |
|
15 |
<MultiSelect |
|
16 |
items={ props.data } |
|
17 |
uniqueKey="key" |
|
18 |
onSelectedItemsChange={ props.onSelectedItemsChange } |
|
19 |
selectedItems={ props.selectedItems } |
|
20 |
selectText={ props.label } |
|
21 |
searchInputPlaceholderText={ props.placeholder } |
|
22 |
displayKey="label" |
|
23 |
submitButtonText={ "Submit" } |
|
24 |
onChangeInput={ (text: string) => console.log(text) } |
|
25 |
tagRemoveIconColor="#CCC" |
|
26 |
tagBorderColor="#CCC" |
|
27 |
tagTextColor="#CCC" |
|
28 |
selectedItemTextColor="#CCC" |
|
29 |
selectedItemIconColor="#CCC" |
|
30 |
itemTextColor="#000" |
|
31 |
searchInputStyle={ { color: "#CCC" } } |
|
32 |
submitButtonColor="#CCC" |
|
33 |
|
|
34 |
/> |
|
35 |
</View> |
|
36 |
) |
|
37 |
} |
|
38 |
|
|
39 |
export default SearchFormControl |
src/pages/SearchPage.tsx | ||
---|---|---|
1 |
import { Center, Text } from "native-base" |
|
1 |
import { Center, KeyboardAvoidingView, Text } from "native-base" |
|
2 |
import ListView from "../components/listView/ListView" |
|
3 |
import SearchForm from "../components/search/SearchForm" |
|
4 |
import { Platform } from "react-native" |
|
2 | 5 |
|
3 | 6 |
const SearchPage = () => { |
4 | 7 |
|
5 | 8 |
return ( |
6 |
<Center> |
|
7 |
<Text>Search Page</Text> |
|
8 |
</Center> |
|
9 |
<KeyboardAvoidingView |
|
10 |
h={ { |
|
11 |
base: "400px", |
|
12 |
lg: "auto" |
|
13 |
} } |
|
14 |
behavior={ Platform.OS === "ios" ? "padding" : "height" } |
|
15 |
> |
|
16 |
<Center> |
|
17 |
<SearchForm/> |
|
18 |
<ListView/> |
|
19 |
</Center> |
|
20 |
</KeyboardAvoidingView> |
|
9 | 21 |
) |
10 | 22 |
} |
11 | 23 |
|
src/stores/actions/listViewThunks.ts | ||
---|---|---|
1 |
import { createAsyncThunk } from "@reduxjs/toolkit" |
|
2 |
import { SearchParams, searchRequest } from "../../api/searchService" |
|
3 |
import { SearchResponse } from "../../types/listViewTypes" |
|
4 |
|
|
5 |
|
|
6 |
export const search = createAsyncThunk( |
|
7 |
"listView/search", |
|
8 |
async (searchParams: SearchParams, thunkAPI) => { |
|
9 |
try { |
|
10 |
const response = await searchRequest(searchParams) |
|
11 |
if (response.status === 200) { |
|
12 |
return response.data as SearchResponse |
|
13 |
} else { |
|
14 |
return Promise.reject(response.data ? response.data : "Search failed") |
|
15 |
} |
|
16 |
} catch (err: any) { |
|
17 |
return Promise.reject(err.response.data) |
|
18 |
} |
|
19 |
} |
|
20 |
) |
src/stores/actions/searchFormThunks.ts | ||
---|---|---|
1 |
import { createAsyncThunk } from "@reduxjs/toolkit" |
|
2 |
import { |
|
3 |
fetchArtistNamesRequest, |
|
4 |
fetchCitiesRequest, |
|
5 |
fetchCountriesRequest, |
|
6 |
fetchInstitutionsRequest, |
|
7 |
fetchInventoriesRequest, |
|
8 |
fetchNationalitiesRequest, |
|
9 |
fetchPlanRequest, |
|
10 |
fetchSubjectsRequest, |
|
11 |
fetchTechniquesRequest |
|
12 |
} from "../../api/searchFormService" |
|
13 |
|
|
14 |
export const fetchInventories = createAsyncThunk( |
|
15 |
"searchForm/fetchInventories", |
|
16 |
async () => { |
|
17 |
try { |
|
18 |
const response = await fetchInventoriesRequest() |
|
19 |
if (response.status === 200) { |
|
20 |
return response.data |
|
21 |
} else { |
|
22 |
return Promise.reject(response.data ? response.data : "Fetch inventories failed") |
|
23 |
} |
|
24 |
} catch (err: any) { |
|
25 |
return Promise.reject(err.response.data) |
|
26 |
} |
|
27 |
} |
|
28 |
) |
|
29 |
|
|
30 |
export const fetchCountries = createAsyncThunk( |
|
31 |
"searchForm/fetchCountries", |
|
32 |
async () => { |
|
33 |
try { |
|
34 |
const response = await fetchCountriesRequest() |
|
35 |
if (response.status === 200) { |
|
36 |
return response.data |
|
37 |
} else { |
|
38 |
return Promise.reject(response.data ? response.data : "Fetch countries failed") |
|
39 |
} |
|
40 |
} catch (err: any) { |
|
41 |
return Promise.reject(err.response.data) |
|
42 |
} |
|
43 |
} |
|
44 |
) |
|
45 |
|
|
46 |
export const fetchInstitutions = createAsyncThunk( |
|
47 |
"searchForm/fetchInstitutions", |
|
48 |
async () => { |
|
49 |
try { |
|
50 |
const response = await fetchInstitutionsRequest() |
|
51 |
if (response.status === 200) { |
|
52 |
return response.data |
|
53 |
} else { |
|
54 |
return Promise.reject(response.data ? response.data : "Fetch institutions failed") |
|
55 |
} |
|
56 |
} catch (err: any) { |
|
57 |
return Promise.reject(err.response.data) |
|
58 |
} |
|
59 |
} |
|
60 |
) |
|
61 |
|
|
62 |
export const fetchArtists = createAsyncThunk( |
|
63 |
"searchForm/fetchArtists", |
|
64 |
async () => { |
|
65 |
try { |
|
66 |
const response = await fetchArtistNamesRequest() |
|
67 |
if (response.status === 200) { |
|
68 |
return response.data |
|
69 |
} else { |
|
70 |
return Promise.reject(response.data ? response.data : "Fetch artists failed") |
|
71 |
} |
|
72 |
} catch (err: any) { |
|
73 |
return Promise.reject(err.response.data) |
|
74 |
} |
|
75 |
} |
|
76 |
) |
|
77 |
|
|
78 |
export const fetchTechniques = createAsyncThunk( |
|
79 |
"searchForm/fetchTechniques", |
|
80 |
async () => { |
|
81 |
try { |
|
82 |
const response = await fetchTechniquesRequest() |
|
83 |
if (response.status === 200) { |
|
84 |
return response.data |
|
85 |
} else { |
|
86 |
return Promise.reject(response.data ? response.data : "Fetch techniques failed") |
|
87 |
} |
|
88 |
} catch (err: any) { |
|
89 |
return Promise.reject(err.response.data) |
|
90 |
} |
|
91 |
} |
|
92 |
) |
|
93 |
|
|
94 |
export const fetchNationalities = createAsyncThunk( |
|
95 |
"searchForm/fetchNationalities", |
|
96 |
async () => { |
|
97 |
try { |
|
98 |
const response = await fetchNationalitiesRequest() |
|
99 |
if (response.status === 200) { |
|
100 |
return response.data |
|
101 |
} else { |
|
102 |
return Promise.reject(response.data ? response.data : "Fetch nationalities failed") |
|
103 |
} |
|
104 |
} catch (err: any) { |
|
105 |
return Promise.reject(err.response.data) |
|
106 |
} |
|
107 |
} |
|
108 |
) |
|
109 |
|
|
110 |
export const fetchCities = createAsyncThunk( |
|
111 |
"searchForm/fetchCities", |
|
112 |
async () => { |
|
113 |
try { |
|
114 |
const response = await fetchCitiesRequest() |
|
115 |
if (response.status === 200) { |
|
116 |
return response.data |
|
117 |
} else { |
|
118 |
return Promise.reject(response.data ? response.data : "Fetch cities failed") |
|
119 |
} |
|
120 |
} catch (err: any) { |
|
121 |
return Promise.reject(err.response.data) |
|
122 |
} |
|
123 |
} |
|
124 |
) |
|
125 |
|
|
126 |
export const fetchSubjects = createAsyncThunk( |
|
127 |
"searchForm/fetchSubjects", |
|
128 |
async () => { |
|
129 |
try { |
|
130 |
const response = await fetchSubjectsRequest() |
|
131 |
if (response.status === 200) { |
|
132 |
return response.data |
|
133 |
} else { |
|
134 |
return Promise.reject(response.data ? response.data : "Fetch subjects failed") |
|
135 |
} |
|
136 |
} catch (err: any) { |
|
137 |
return Promise.reject(err.response.data) |
|
138 |
} |
|
139 |
} |
|
140 |
) |
|
141 |
|
|
142 |
export const fetchPlans = createAsyncThunk( |
|
143 |
"searchForm/fetchPlans", |
|
144 |
async () => { |
|
145 |
try { |
|
146 |
const response = await fetchPlanRequest() |
|
147 |
if (response.status === 200) { |
|
148 |
return response.data |
|
149 |
} else { |
|
150 |
return Promise.reject(response.data ? response.data : "Fetch plans failed") |
|
151 |
} |
|
152 |
} catch (err: any) { |
|
153 |
return Promise.reject(err.response.data) |
|
154 |
} |
|
155 |
} |
|
156 |
) |
|
157 |
|
src/stores/reducers/listViewSlice.ts | ||
---|---|---|
1 |
import { createSlice } from "@reduxjs/toolkit" |
|
2 |
import { Inventory } from "../../types/searchFormTypes" |
|
3 |
import { ItemPreview } from "../../types/listViewTypes" |
|
4 |
import { search } from "../actions/listViewThunks" |
|
5 |
|
|
6 |
export interface ListViewState { |
|
7 |
inventories: Inventory[] |
|
8 |
data: ItemPreview[] |
|
9 |
loading: boolean |
|
10 |
lastError?: string |
|
11 |
} |
|
12 |
|
|
13 |
const initialState: ListViewState = { |
|
14 |
inventories: [], |
|
15 |
data: [], |
|
16 |
loading: false, |
|
17 |
} |
|
18 |
|
|
19 |
export const listViewSlice = createSlice({ |
|
20 |
name: "listView", |
|
21 |
initialState: initialState, |
|
22 |
reducers: { |
|
23 |
resetListView: () => initialState, |
|
24 |
}, |
|
25 |
extraReducers: (builder) => { |
|
26 |
builder.addCase(search.fulfilled, (state, action) => { |
|
27 |
state.inventories = action.payload.pagination.inventories |
|
28 |
state.data = action.payload.data |
|
29 |
state.loading = false |
|
30 |
}) |
|
31 |
builder.addCase(search.pending, (state, action) => { |
|
32 |
state.loading = true |
|
33 |
}) |
|
34 |
builder.addCase(search.rejected, (state, action) => { |
|
35 |
state.lastError = action.error.message |
|
36 |
state.loading = false |
|
37 |
}) |
|
38 |
|
|
39 |
|
|
40 |
} |
|
41 |
}) |
|
42 |
|
|
43 |
export const { resetListView } = listViewSlice.actions |
|
44 |
export default listViewSlice.reducer |
src/stores/reducers/searchFormSlice.ts | ||
---|---|---|
1 |
import { createSlice } from "@reduxjs/toolkit" |
|
2 |
import { Artist, Inventory, Room } from "../../types/searchFormTypes" |
|
3 |
import { |
|
4 |
fetchArtists, fetchCities, |
|
5 |
fetchCountries, |
|
6 |
fetchInstitutions, |
|
7 |
fetchInventories, |
|
8 |
fetchNationalities, fetchPlans, fetchSubjects, fetchTechniques |
|
9 |
} from "../actions/searchFormThunks" |
|
10 |
|
|
11 |
export interface SearchFormState { |
|
12 |
inventories: Inventory[] |
|
13 |
rooms: Room[] |
|
14 |
artists: Artist[] |
|
15 |
nationalities: string[] |
|
16 |
techniques: string[] |
|
17 |
institutions: string[] |
|
18 |
countries: string[] |
|
19 |
cities: string[] |
|
20 |
subjects: string[] |
|
21 |
lastError?: string |
|
22 |
} |
|
23 |
|
|
24 |
const initialState: SearchFormState = { |
|
25 |
inventories: [], |
|
26 |
rooms: [], |
|
27 |
artists: [], |
|
28 |
nationalities: [], |
|
29 |
techniques: [], |
|
30 |
institutions: [], |
|
31 |
countries: [], |
|
32 |
cities: [], |
|
33 |
subjects: [], |
|
34 |
} |
|
35 |
|
|
36 |
export const searchFormSlice = createSlice({ |
|
37 |
name: "searchForm", |
|
38 |
initialState: initialState, |
|
39 |
reducers: { |
|
40 |
resetSearchForm: () => initialState, |
|
41 |
setInventories: (state, action) => { |
|
42 |
state.inventories = action.payload |
|
43 |
}, |
|
44 |
setRooms: (state, action) => { |
|
45 |
state.rooms = action.payload |
|
46 |
}, |
|
47 |
setArtists: (state, action) => { |
|
48 |
state.artists = action.payload |
|
49 |
}, |
|
50 |
setNationalities: (state, action) => { |
|
51 |
state.nationalities = action.payload |
|
52 |
}, |
|
53 |
}, |
|
54 |
extraReducers: (builder) => { |
|
55 |
// ------------ Inventories ------------ |
|
56 |
builder.addCase(fetchInventories.fulfilled, (state, action) => { |
|
57 |
state.inventories = action.payload |
|
58 |
state.lastError = "" |
|
59 |
}) |
|
60 |
builder.addCase(fetchInventories.rejected, (state, action) => { |
|
61 |
state.lastError = action.error.message |
|
62 |
}) |
|
63 |
|
|
64 |
// ------------ Institutions ------------ |
|
65 |
builder.addCase(fetchInstitutions.fulfilled, (state, action) => { |
|
66 |
state.institutions = action.payload |
|
67 |
state.lastError = "" |
|
68 |
}) |
|
69 |
builder.addCase(fetchInstitutions.rejected, (state, action) => { |
|
70 |
state.lastError = action.error.message |
|
71 |
}) |
|
72 |
|
|
73 |
// ------------ Nationalities ------------ |
|
74 |
builder.addCase(fetchNationalities.fulfilled, (state, action) => { |
|
75 |
state.nationalities = action.payload |
|
76 |
state.lastError = "" |
|
77 |
}) |
|
78 |
builder.addCase(fetchNationalities.rejected, (state, action) => { |
|
79 |
state.lastError = action.error.message |
|
80 |
}) |
|
81 |
|
|
82 |
// ------------ Countries ------------ |
|
83 |
builder.addCase(fetchCountries.fulfilled, (state, action) => { |
|
84 |
state.countries = action.payload |
|
85 |
state.lastError = "" |
|
86 |
}) |
|
87 |
builder.addCase(fetchCountries.rejected, (state, action) => { |
|
88 |
state.lastError = action.error.message |
|
89 |
}) |
|
90 |
|
|
91 |
// ------------ Artists ------------ |
|
92 |
builder.addCase(fetchArtists.fulfilled, (state, action) => { |
|
93 |
state.artists = action.payload |
|
94 |
state.lastError = "" |
|
95 |
}) |
|
96 |
builder.addCase(fetchArtists.rejected, (state, action) => { |
|
97 |
state.lastError = action.error.message |
|
98 |
}) |
|
99 |
|
|
100 |
// ------------ Rooms ------------ |
|
101 |
builder.addCase(fetchPlans.fulfilled, (state, action) => { |
|
102 |
state.rooms = action.payload |
|
103 |
state.lastError = "" |
|
104 |
}) |
|
105 |
builder.addCase(fetchPlans.rejected, (state, action) => { |
|
106 |
state.lastError = action.error.message |
|
107 |
}) |
|
108 |
|
|
109 |
// ------------ Subjects ------------ |
|
110 |
builder.addCase(fetchSubjects.fulfilled, (state, action) => { |
|
111 |
state.subjects = action.payload |
|
112 |
state.lastError = "" |
|
113 |
}) |
|
114 |
builder.addCase(fetchSubjects.rejected, (state, action) => { |
|
115 |
state.lastError = action.error.message |
|
116 |
}) |
|
117 |
|
|
118 |
// ------------ Cities ------------ |
|
119 |
builder.addCase(fetchCities.fulfilled, (state, action) => { |
|
120 |
state.cities = action.payload |
|
121 |
state.lastError = "" |
|
122 |
}) |
|
123 |
builder.addCase(fetchCities.rejected, (state, action) => { |
|
124 |
state.lastError = action.error.message |
|
125 |
}) |
|
126 |
|
|
127 |
// ------------ Techniques ------------ |
|
128 |
builder.addCase(fetchTechniques.fulfilled, (state, action) => { |
|
129 |
state.techniques = action.payload |
|
130 |
state.lastError = "" |
|
131 |
}) |
|
132 |
builder.addCase(fetchTechniques.rejected, (state, action) => { |
|
133 |
state.lastError = action.error.message |
|
134 |
}) |
|
135 |
} |
|
136 |
}) |
|
137 |
|
|
138 |
export const { resetSearchForm, setInventories, setRooms, setArtists, setNationalities } = searchFormSlice.actions |
|
139 |
|
|
140 |
export default searchFormSlice.reducer |
src/stores/store.ts | ||
---|---|---|
1 | 1 |
import { configureStore } from "@reduxjs/toolkit" |
2 | 2 |
import userReducer from "./reducers/userSlice" |
3 |
import searchFormReducer from "./reducers/searchFormSlice" |
|
4 |
import listViewReducer from "./reducers/listViewSlice" |
|
3 | 5 |
|
4 | 6 |
const store = configureStore({ |
5 | 7 |
reducer: { |
6 | 8 |
user: userReducer, |
9 |
searchForm: searchFormReducer, |
|
10 |
listView: listViewReducer, |
|
7 | 11 |
}, |
8 | 12 |
}) |
9 | 13 |
|
src/types/listViewTypes.ts | ||
---|---|---|
1 |
import { Inventory } from "./searchFormTypes" |
|
2 |
|
|
3 |
|
|
4 |
export type SearchResponse = { |
|
5 |
pagination: Pagination |
|
6 |
data: ItemPreview[] |
|
7 |
} |
|
8 |
|
|
9 |
export type Pagination = { |
|
10 |
cursor: number |
|
11 |
records: number |
|
12 |
items: number |
|
13 |
inventories: Inventory[] |
|
14 |
} |
|
15 |
|
|
16 |
export type ItemPreview = { |
|
17 |
xml_id: string |
|
18 |
iconclass_external_id: string |
|
19 |
text: string |
|
20 |
title: string |
|
21 |
object: Item |
|
22 |
inventory: Inventory |
|
23 |
src?: ItemSource |
|
24 |
} |
|
25 |
|
|
26 |
export type Item = { |
|
27 |
caption: string |
|
28 |
type: string |
|
29 |
name?: PersonName[] |
|
30 |
images?: ItemImage[] |
|
31 |
origDate?: string |
|
32 |
|
|
33 |
} |
|
34 |
|
|
35 |
export type PersonName = { |
|
36 |
value?: string |
|
37 |
role: string |
|
38 |
getty_external_id: string |
|
39 |
getty_data: GettyData |
|
40 |
} |
|
41 |
|
|
42 |
export type GettyData = { |
|
43 |
display_name: string |
|
44 |
} |
|
45 |
|
|
46 |
export type ItemImage = { |
|
47 |
file: string |
|
48 |
text: string |
|
49 |
} |
|
50 |
|
|
51 |
export type ItemSource = { |
|
52 |
value: string |
|
53 |
} |
src/types/searchFormTypes.ts | ||
---|---|---|
1 |
export type Inventory = { |
|
2 |
count?: number |
|
3 |
name: string |
|
4 |
label: string |
|
5 |
order: number |
|
6 |
} |
|
7 |
|
|
8 |
export type Room = { |
|
9 |
id: number |
|
10 |
in_plan: boolean |
|
11 |
label: string |
|
12 |
places?: RoomPlace[] |
|
13 |
} |
|
14 |
|
|
15 |
export type RoomPlace = { |
|
16 |
id: number |
|
17 |
label: string |
|
18 |
} |
|
19 |
|
|
20 |
export type Artist = { |
|
21 |
display_name: string |
|
22 |
getty_id: string |
|
23 |
} |
Také k dispozici: Unified diff
SearchPage implementations with base of list view for results of the search
re #10342