diff --git a/src/App.tsx b/src/App.tsx
index 23c5316..411946f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,12 +1,14 @@
import React from 'react';
import './App.css';
import Header from 'components/Header';
-import Pokedex from './features/Pokedex';
+import Pokedex from 'features/Pokedex';
+import Filters from 'features/Filters';
function App() {
return (
);
diff --git a/src/app/store.ts b/src/app/store.ts
index bf908e1..f67aa1b 100644
--- a/src/app/store.ts
+++ b/src/app/store.ts
@@ -1,19 +1,21 @@
import { configureStore } from '@reduxjs/toolkit';
import { listenerMiddleware } from './listenerMiddleware';
-import { pokedexApi } from 'features/Pokedex/pokedexApi';
import { pokedexSlice } from 'features/Pokedex/pokedexSlice';
+import { filterSlice } from 'features/Filters/filterSlice';
+import { filterApi } from 'features/Filters/filterApi';
export const store = configureStore({
reducer: {
// component slices
pokedex: pokedexSlice.reducer,
+ filter: filterSlice.reducer,
- // api
- [pokedexApi.reducerPath]: pokedexApi.reducer,
+ // api slices
+ [filterApi.reducerPath]: filterApi.reducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
- pokedexApi.middleware,
+ filterApi.middleware,
listenerMiddleware.middleware,
),
devTools: true,
diff --git a/src/components/PokemonCard/PokemonCard.css b/src/components/PokemonCard/PokemonCard.css
index c348dc4..1ccead3 100644
--- a/src/components/PokemonCard/PokemonCard.css
+++ b/src/components/PokemonCard/PokemonCard.css
@@ -28,7 +28,7 @@ html {
@font-face {
font-family: 'Press Start 2P';
- src: url('../../assets/PressStart2P-Regular.ttf') format('truetype');
+ src: url('assets/PressStart2P-Regular.ttf') format('truetype');
}
.thumbnail__container {
diff --git a/src/features/Pokedex/Filters/Filters.css b/src/features/Filters/Filters.css
similarity index 100%
rename from src/features/Pokedex/Filters/Filters.css
rename to src/features/Filters/Filters.css
diff --git a/src/features/Pokedex/Filters/Filters.test.ts b/src/features/Filters/Filters.test.ts
similarity index 100%
rename from src/features/Pokedex/Filters/Filters.test.ts
rename to src/features/Filters/Filters.test.ts
diff --git a/src/features/Pokedex/Filters/Filters.tsx b/src/features/Filters/Filters.tsx
similarity index 87%
rename from src/features/Pokedex/Filters/Filters.tsx
rename to src/features/Filters/Filters.tsx
index abdc808..0a44f2d 100644
--- a/src/features/Pokedex/Filters/Filters.tsx
+++ b/src/features/Filters/Filters.tsx
@@ -1,17 +1,18 @@
import React, { useEffect } from 'react';
-import { useGetTypeListQuery } from 'features/Pokedex/pokedexApi';
+import { useAppDispatch, useAppSelector } from 'app/hooks';
+import { fetchPokemonsInTheRegion } from 'features/Pokedex/pokedexSlice';
+
import {
setSelectedRegion,
setSelectedType,
setSelectedSort,
- fetchPokemonsInTheRegion,
setRegionOptions,
- setSortOptions,
setTypeOptions,
+ setSortOptions,
setSearchInput,
-} from 'features/Pokedex/pokedexSlice';
-import { RegionPokemonRange } from 'features/Pokedex/types/slice';
-import { useAppDispatch, useAppSelector } from 'app/hooks';
+} from './filterSlice';
+import { useGetTypeListQuery } from './filterApi';
+import { RegionPokemonRange } from './types/slice';
import './Filters.css';
export const createRegionPokemonListOptionElements = (
@@ -55,14 +56,12 @@ const useGetSortOptions = () => {
const Filters = () => {
const dispatch = useAppDispatch();
- const selectedRegion = useAppSelector(state => state.pokedex.selectedRegion);
- const selectedType = useAppSelector(state => state.pokedex.selectedType);
- const selectedSort = useAppSelector(state => state.pokedex.selectedSort);
- const searchInput = useAppSelector(state => state.pokedex.searchInput);
+ const selectedRegion = useAppSelector(state => state.filter.selectedRegion);
+ const selectedType = useAppSelector(state => state.filter.selectedType);
+ const selectedSort = useAppSelector(state => state.filter.selectedSort);
+ const searchInput = useAppSelector(state => state.filter.searchInput);
- const regionPokemonList = useAppSelector(
- state => state.pokedex.regionOptions,
- );
+ const regionPokemonList = useAppSelector(state => state.filter.regionOptions);
const { data: fetchedRegionOptions } = useGetRegionOptions();
const { data: fetchedTypeOptions, isLoading: isFetchingTypeOptions } =
diff --git a/src/features/Filters/filterApi.ts b/src/features/Filters/filterApi.ts
new file mode 100644
index 0000000..d06f03d
--- /dev/null
+++ b/src/features/Filters/filterApi.ts
@@ -0,0 +1,24 @@
+import { createApi } from '@reduxjs/toolkit/query/react';
+import { pokeApiBaseQuery } from '../../services/pokeapi/paginationBaseQuery';
+import {
+ RegionListResponseData,
+ TypeListResponseData,
+} from '../Pokedex/types/api';
+
+export const filterApi = createApi({
+ reducerPath: 'filterApi',
+ baseQuery: pokeApiBaseQuery,
+ endpoints: builder => ({
+ getTypeList: builder.query({
+ query: () => ({ url: 'type', fetchAllPages: true }),
+ transformResponse: (response: RegionListResponseData) => {
+ return {
+ ...response,
+ results: [{ name: 'All Types', url: '' }, ...response.results],
+ };
+ },
+ }),
+ }),
+});
+
+export const { useGetTypeListQuery } = filterApi;
diff --git a/src/features/Filters/filterSlice.ts b/src/features/Filters/filterSlice.ts
new file mode 100644
index 0000000..4bbb8c5
--- /dev/null
+++ b/src/features/Filters/filterSlice.ts
@@ -0,0 +1,72 @@
+import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit';
+import { FilterState } from './types/slice';
+import { RegionPokemonRange } from './types/slice';
+import { filterApi } from './filterApi';
+
+filterApi.endpoints.getTypeList.initiate(); // initialize type list fetching
+
+const initialState: FilterState = {
+ regionOptions: [],
+ typeOptions: [],
+ sortOptions: [],
+ selectedRegion: '',
+ selectedType: '',
+ selectedSort: '',
+ searchInput: '',
+};
+
+export const filterSlice: Slice = createSlice({
+ name: 'filter',
+ initialState,
+ reducers: {
+ setSelectedRegion: (state, action: PayloadAction) => {
+ state.selectedRegion = action.payload;
+ },
+ setSelectedType: (state, action: PayloadAction) => {
+ state.selectedType = action.payload;
+ },
+ setSelectedSort: (state, action: PayloadAction) => {
+ state.selectedSort = action.payload;
+ },
+ setRegionOptions: (state, action: PayloadAction) => {
+ state.regionOptions = action.payload;
+ },
+ setTypeOptions: (state, action: PayloadAction) => {
+ state.typeOptions = action.payload;
+ },
+ setSortOptions: (
+ state,
+ action: PayloadAction<{ name: string; value: string }[]>,
+ ) => {
+ state.sortOptions = action.payload;
+ },
+ setSearchInput: (state, action: PayloadAction) => {
+ state.searchInput = action.payload;
+ },
+ },
+ extraReducers: builder => {
+ builder.addMatcher(
+ filterApi.endpoints.getTypeList.matchFulfilled,
+ (state, action) => {
+ if (action.payload && action.payload.results.length > 0) {
+ const regionListResults = action.payload.results;
+ state.typeOptions = regionListResults.map(region => region.name);
+
+ state.selectedType = action.payload.results[0].name;
+ }
+ },
+ );
+ },
+});
+
+export const {
+ setSelectedRegion,
+ setSelectedType,
+ setSelectedSort,
+ setRegionOptions,
+ setTypeOptions,
+ setSortOptions,
+ setSearchInput,
+} = filterSlice.actions;
+
+export default filterSlice.reducer;
diff --git a/src/features/Filters/index.ts b/src/features/Filters/index.ts
new file mode 100644
index 0000000..5b18d95
--- /dev/null
+++ b/src/features/Filters/index.ts
@@ -0,0 +1 @@
+export { default } from './Filters';
diff --git a/src/features/Filters/types/slice.ts b/src/features/Filters/types/slice.ts
new file mode 100644
index 0000000..0d5a602
--- /dev/null
+++ b/src/features/Filters/types/slice.ts
@@ -0,0 +1,15 @@
+export type FilterState = {
+ regionOptions: RegionPokemonRange[];
+ typeOptions: string[];
+ sortOptions: { name: string; value: string }[];
+ selectedRegion: string;
+ selectedType: string;
+ selectedSort: string;
+ searchInput: string;
+};
+
+export type RegionPokemonRange = {
+ region: string;
+ startId: number;
+ endId: number;
+};
diff --git a/src/features/Pokedex/Filters/index.ts b/src/features/Pokedex/Filters/index.ts
deleted file mode 100644
index 3fdb3d0..0000000
--- a/src/features/Pokedex/Filters/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default } from './Filters';
-export { default as useFilterLoaded } from './useFilterLoaded';
diff --git a/src/features/Pokedex/Filters/useFilterLoaded.ts b/src/features/Pokedex/Filters/useFilterLoaded.ts
deleted file mode 100644
index 52beedc..0000000
--- a/src/features/Pokedex/Filters/useFilterLoaded.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useState, useEffect } from 'react';
-import { useGetRegionListQuery, useGetTypeListQuery } from '../pokedexApi';
-
-const useFilterLoaded = () => {
- const { isLoading: isLoadingRegionList } = useGetRegionListQuery();
- const { isLoading: isLoadingTypeList } = useGetTypeListQuery();
- const [isFilterLoaded, setIsFilterLoaded] = useState(false);
-
- useEffect(() => {
- if (!isLoadingRegionList && !isLoadingTypeList) {
- setIsFilterLoaded(true);
- }
- }, [isLoadingRegionList, isLoadingTypeList]);
-
- return isFilterLoaded;
-};
-
-export default useFilterLoaded;
diff --git a/src/features/Pokedex/Pokedex.tsx b/src/features/Pokedex/Pokedex.tsx
index 4868f58..ecd6962 100644
--- a/src/features/Pokedex/Pokedex.tsx
+++ b/src/features/Pokedex/Pokedex.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import PokemonCard, { PokemonCardProps } from 'components/PokemonCard';
-import Filters from './Filters';
+import Filters from 'features/Filters';
import Loading from 'components/Loading';
import { useAppSelector } from 'app/hooks';
@@ -41,13 +41,13 @@ export const searchPokemonCardsByName = (
};
const Pokedex = () => {
+ const selectedType = useAppSelector(state => state.filter.selectedType);
+ const selectedSort = useAppSelector(state => state.filter.selectedSort);
+ const searchInput = useAppSelector(state => state.filter.searchInput);
+
const isLoadingPokemons = useAppSelector(
state => state.pokedex.isLoadingPokemons,
);
- const selectedType = useAppSelector(state => state.pokedex.selectedType);
- const selectedSort = useAppSelector(state => state.pokedex.selectedSort);
- const searchInput = useAppSelector(state => state.pokedex.searchInput);
-
const pokemonList = useAppSelector(state => state.pokedex.pokemonCardList);
const filteredPokemonList = filterPokemonCardsByType(
@@ -65,7 +65,6 @@ const Pokedex = () => {
return (
<>
-
{isLoadingPokemons ? (
) : (
diff --git a/src/features/Pokedex/pokedexApi.ts b/src/features/Pokedex/pokedexApi.ts
deleted file mode 100644
index 6a754ac..0000000
--- a/src/features/Pokedex/pokedexApi.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
-import {
- pokeApiAllPagesCustomBaseQuery,
- pokeApiFullListFetchArgs,
-} from './paginationBaseQuery';
-import {
- AreaResponseData,
- LocationResponseData,
- PokemonListResponseData,
- PokemonResponseData,
- RegionListResponseData,
- RegionResponseData,
- TypeListResponseData,
- TypeResponseData,
- PokemonListItem,
- nameUrlPair,
- PokemonList,
-} from './types/api';
-
-const pokeApiBaseQuery = async (
- args: pokeApiFullListFetchArgs,
- api: any,
- extra: any,
-) => {
- const baseUrl = 'https://pokeapi.co/api/v2/';
-
- if (args.fetchAllPages) {
- return pokeApiAllPagesCustomBaseQuery(args, api, extra, baseUrl);
- } else {
- return fetchBaseQuery({ baseUrl })(args, api, extra);
- }
-};
-
-export const pokedexApi = createApi({
- reducerPath: 'pokedexApi',
- baseQuery: pokeApiBaseQuery,
- endpoints: builder => ({
- getPokemonList: builder.query({
- query: () => ({ url: `pokemon`, fetchAllPages: true }),
- }),
- getRegionList: builder.query({
- query: () => ({ url: 'region', fetchAllPages: true }),
- }),
- getTypeList: builder.query({
- query: () => ({ url: 'type', fetchAllPages: true }),
- transformResponse: (response: RegionListResponseData) => {
- return {
- ...response,
- results: [{ name: 'All Types', url: '' }, ...response.results],
- };
- },
- }),
- getPokemon: builder.query({
- query: IdOrName => ({ url: `pokemon/${IdOrName}` }),
- }),
- getRegion: builder.query({
- query: IdOrName => ({ url: `region/${IdOrName}` }),
- }),
- getType: builder.query({
- query: IdOrName => ({ url: `type/${IdOrName}` }),
- }),
- getLocation: builder.query({
- query: IdOrName => ({ url: `location/${IdOrName}` }),
- }),
- getArea: builder.query({
- query: IdOrName => ({ url: `location-area/${IdOrName}` }),
- }),
- }),
-});
-
-export const {
- useGetPokemonListQuery,
- useGetRegionListQuery,
- useGetTypeListQuery,
- useGetPokemonQuery,
- useGetRegionQuery,
- useGetTypeQuery,
- useGetAreaQuery,
- useGetLocationQuery,
-} = pokedexApi;
diff --git a/src/features/Pokedex/pokedexSlice.ts b/src/features/Pokedex/pokedexSlice.ts
index aa345f3..18b4e29 100644
--- a/src/features/Pokedex/pokedexSlice.ts
+++ b/src/features/Pokedex/pokedexSlice.ts
@@ -1,15 +1,11 @@
-import { createAsyncThunk, createSlice, Dispatch } from '@reduxjs/toolkit';
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import type { Slice, PayloadAction } from '@reduxjs/toolkit';
-import { PokedexState, RegionPokemonRange } from 'features/Pokedex/types/slice';
+import { PokedexState } from 'features/Pokedex/types/slice';
import { getStartAndEndIdsForRegion } from './utils';
import { PokemonResponseData } from './types/api';
-import { pokedexApi } from './pokedexApi';
-import { RootState } from '../../app/store';
-
-pokedexApi.endpoints.getTypeList.initiate(); // initialize type list fetching
-// typesData will be used in Filters.tsx
+import { RootState } from 'app/store';
export const fetchPokemonsInTheRegion = createAsyncThunk<
PokemonResponseData[],
@@ -17,7 +13,7 @@ export const fetchPokemonsInTheRegion = createAsyncThunk<
{ state: RootState }
>('pokedex/setSelectedRegion', async (region: string, thunkAPI) => {
const { dispatch, getState } = thunkAPI;
- const regionOptions = getState().pokedex.regionOptions;
+ const regionOptions = getState().filter.regionOptions;
dispatch(setIsLoadingPokemons(true));
@@ -44,13 +40,6 @@ export const fetchPokemonsInTheRegion = createAsyncThunk<
});
const initialState: PokedexState = {
- regionOptions: [],
- typeOptions: [],
- sortOptions: [],
- selectedRegion: '',
- selectedType: '',
- selectedSort: '',
- searchInput: '',
isLoadingPokemons: true,
pokemonCardList: [],
};
@@ -59,30 +48,6 @@ export const pokedexSlice: Slice = createSlice({
name: 'pokedex',
initialState,
reducers: {
- setSelectedRegion: (state, action: PayloadAction) => {
- state.selectedRegion = action.payload;
- },
- setSelectedType: (state, action: PayloadAction) => {
- state.selectedType = action.payload;
- },
- setSelectedSort: (state, action: PayloadAction) => {
- state.selectedSort = action.payload;
- },
- setRegionOptions: (state, action: PayloadAction) => {
- state.regionOptions = action.payload;
- },
- setTypeOptions: (state, action: PayloadAction) => {
- state.typeOptions = action.payload;
- },
- setSortOptions: (
- state,
- action: PayloadAction<{ name: string; value: string }[]>,
- ) => {
- state.sortOptions = action.payload;
- },
- setSearchInput: (state, action: PayloadAction) => {
- state.searchInput = action.payload;
- },
setIsLoadingPokemons: (state, action: PayloadAction) => {
state.isLoadingPokemons = action.payload;
},
@@ -101,29 +66,9 @@ export const pokedexSlice: Slice = createSlice({
types: pokemon.types.map(type => type.type.name),
}));
});
- builder.addMatcher(
- pokedexApi.endpoints.getTypeList.matchFulfilled,
- (state, action) => {
- if (action.payload && action.payload.results.length > 0) {
- const regionListResults = action.payload.results;
- state.typeOptions = regionListResults.map(region => region.name);
-
- state.selectedType = action.payload.results[0].name;
- }
- },
- );
},
});
-export const {
- setSelectedRegion,
- setSelectedType,
- setSelectedSort,
- setRegionOptions,
- setTypeOptions,
- setSortOptions,
- setSearchInput,
- setIsLoadingPokemons,
-} = pokedexSlice.actions;
+export const { setIsLoadingPokemons } = pokedexSlice.actions;
export default pokedexSlice.reducer;
diff --git a/src/features/Pokedex/types/slice.ts b/src/features/Pokedex/types/slice.ts
index 3f97e49..849d0be 100644
--- a/src/features/Pokedex/types/slice.ts
+++ b/src/features/Pokedex/types/slice.ts
@@ -2,19 +2,6 @@ import { PokemonResponseData } from './api';
import { PokemonCardProps } from 'components/PokemonCard';
export type PokedexState = {
- regionOptions: RegionPokemonRange[];
- typeOptions: string[];
- sortOptions: { name: string; value: string }[];
- selectedRegion: string;
- selectedType: string;
- selectedSort: string;
- searchInput: string;
isLoadingPokemons: boolean;
pokemonCardList: PokemonCardProps[];
};
-
-export type RegionPokemonRange = {
- region: string;
- startId: number;
- endId: number;
-};
diff --git a/src/features/Pokedex/utils.ts b/src/features/Pokedex/utils.ts
index 340eee8..ddb78d2 100644
--- a/src/features/Pokedex/utils.ts
+++ b/src/features/Pokedex/utils.ts
@@ -1,4 +1,4 @@
-import { RegionPokemonRange } from './types/slice';
+import { RegionPokemonRange } from 'features/Filters/types/slice';
export const getStartAndEndIdsForRegion = (
region: string,
diff --git a/src/features/Pokedex/paginationBaseQuery.ts b/src/services/pokeapi/paginationBaseQuery.ts
similarity index 80%
rename from src/features/Pokedex/paginationBaseQuery.ts
rename to src/services/pokeapi/paginationBaseQuery.ts
index 31becee..b32a3c3 100644
--- a/src/features/Pokedex/paginationBaseQuery.ts
+++ b/src/services/pokeapi/paginationBaseQuery.ts
@@ -55,3 +55,17 @@ export const pokeApiAllPagesCustomBaseQuery = async (
}
return result;
};
+
+export const pokeApiBaseQuery = async (
+ args: pokeApiFullListFetchArgs,
+ api: any,
+ extra: any,
+) => {
+ const baseUrl = 'https://pokeapi.co/api/v2/';
+
+ if (args.fetchAllPages) {
+ return pokeApiAllPagesCustomBaseQuery(args, api, extra, baseUrl);
+ } else {
+ return fetchBaseQuery({ baseUrl })(args, api, extra);
+ }
+};