diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx
index 36664f1..7ddc1dc 100644
--- a/src/components/app/app.tsx
+++ b/src/components/app/app.tsx
@@ -26,7 +26,7 @@ export default function App() {
const authorizationStatus = useAppSelector(getAuthorizationStatus);
const isLoadingOffers = useAppSelector(getIsOffersLoadingStatus);
const dispatch = useAppDispatch();
- const favoritesOffers = useAppSelector(getFavoritesOffers);
+ const favoriteOffers = useAppSelector(getFavoritesOffers);
const isFavoritesLoaded = useAppSelector(getIsFavoritesLoaded);
useEffect(() => {
@@ -46,7 +46,7 @@ export default function App() {
return ;
}
- const favoritePlacesCount = favoritesOffers.length;
+ const favoritePlacesCount = favoriteOffers.length;
return (
diff --git a/src/components/favorites/favorites-list.tsx b/src/components/favorites/favorites-list.tsx
index 562ac14..91100d9 100644
--- a/src/components/favorites/favorites-list.tsx
+++ b/src/components/favorites/favorites-list.tsx
@@ -4,10 +4,10 @@ import { OfferForCardType } from '../../types/offer';
import {FavoritesListItem} from './favorites-list-item';
export type FavoritesListProps = {
- offersCard: OfferForCardType[];
+ offerCards: OfferForCardType[];
};
-export default function FavoritesList({offersCard}: FavoritesListProps): JSX.Element {
+export default function FavoritesList({offerCards}: FavoritesListProps): JSX.Element {
const dispatch = useAppDispatch();
const handleFavoriteClick = (data: {id: string; status: boolean}) => {
@@ -15,16 +15,16 @@ export default function FavoritesList({offersCard}: FavoritesListProps): JSX.Ele
};
const currentCitys = new Set(
- offersCard
- .map((offer) => (offer.isFavorite === true ? offer.city.name : ''))
+ offerCards
+ .map((offer) => (offer.city.name))
.filter(Boolean),
);
return (
{[...currentCitys].map((city) => {
- const currentOffersOfCity = offersCard.filter(
- (offer) => offer.isFavorite === true && offer.city.name === city,
+ const currentOffersOfCity = offerCards.filter(
+ (offer) => offer.city.name === city,
);
return (
(
- );
-}
+ ),
+);
+
+Footer.displayName = 'Footer';
diff --git a/src/components/layout-tools.tsx b/src/components/layout-tools.tsx
index 096fd99..3bb3757 100644
--- a/src/components/layout-tools.tsx
+++ b/src/components/layout-tools.tsx
@@ -1,5 +1,5 @@
import { Outlet } from 'react-router-dom';
-import Footer from './footer';
+import {Footer} from './footer';
export default function LayoutTools() {
return (
diff --git a/src/components/main/card.tsx b/src/components/main/card.tsx
index e130236..6771571 100644
--- a/src/components/main/card.tsx
+++ b/src/components/main/card.tsx
@@ -5,6 +5,7 @@ import { getWidthForRating } from '../../utils';
import { useAppSelector } from '../../hooks';
import { getAuthorizationStatus } from '../../store/user-process/selectors';
import { memo } from 'react';
+import { getIsFavoritesLoading } from '../../store/offers/selectors';
export type CardProps = {
offer: OfferForCardType;
@@ -18,8 +19,16 @@ export const Card = memo(({ offer, onClick, onHover, className }: CardProps): JS
const {id, title, isFavorite, isPremium, type } = offer;
const auth = useAppSelector(getAuthorizationStatus);
const navigate = useNavigate();
+ const isFavoritesLoading = useAppSelector(getIsFavoritesLoading);
const handleFavoriteClick = () => {
+ if(isFavoritesLoading) {
+ return;
+ }
+ if(auth === AuthorizationStatus.Unknown) {
+ return;
+
+ }
if(auth === AuthorizationStatus.Auth) {
onClick({id, status: !isFavorite});
} else {
diff --git a/src/pages/favorites/favorite-section.tsx b/src/pages/favorites/favorite-section.tsx
index f7fc3aa..eab9a00 100644
--- a/src/pages/favorites/favorite-section.tsx
+++ b/src/pages/favorites/favorite-section.tsx
@@ -5,12 +5,12 @@ import Favorites from './favorites';
import FavoritesEmpty from './favorites-empty';
export const FavoriteSection = memo(() => {
- const favoritesOffers = useAppSelector(getFavoritesOffers);
+ const favoriteOffers = useAppSelector(getFavoritesOffers);
- const isFavoriteOffers: boolean = favoritesOffers.length > 0;
+ const isFavoriteOffers: boolean = favoriteOffers.length > 0;
return (
<>
- {isFavoriteOffers && }
+ {isFavoriteOffers && }
{!isFavoriteOffers && }
>
);
diff --git a/src/pages/favorites/favorites.tsx b/src/pages/favorites/favorites.tsx
index d50ea8f..4b445b5 100644
--- a/src/pages/favorites/favorites.tsx
+++ b/src/pages/favorites/favorites.tsx
@@ -2,16 +2,16 @@ import FavoritesList from '../../components/favorites/favorites-list';
import { OfferForCardType } from '../../types/offer';
export type FavoritesProps = {
- offersCard: OfferForCardType[];
+ offerCards: OfferForCardType[];
};
-export default function Favorites({offersCard}: FavoritesProps): JSX.Element {
+export default function Favorites({offerCards}: FavoritesProps): JSX.Element {
return (
diff --git a/src/pages/login.tsx b/src/pages/login.tsx
index d4953b5..e90352c 100644
--- a/src/pages/login.tsx
+++ b/src/pages/login.tsx
@@ -8,6 +8,7 @@ import { getAuthorizationStatus } from '../store/user-process/selectors';
import { AppRoute, AuthorizationStatus } from '../const';
export default function Login(): JSX.Element {
+ const pattern = '^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$';
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
@@ -15,14 +16,16 @@ export default function Login(): JSX.Element {
const city = getRandomCity();
const authStatus = useAppSelector(getAuthorizationStatus);
const navigate = useNavigate();
+ const [isFormDisabled, setIsFormDisabled] = useState(false);
const handleSubmit = (evt: FormEvent) => {
evt.preventDefault();
-
+ setIsFormDisabled(true);
dispatch(loginUser({
login: email,
password:password
}));
+ setIsFormDisabled(false);
};
useEffect(() => {
@@ -74,6 +77,7 @@ export default function Login(): JSX.Element {
placeholder="Email"
required
value={email}
+ disabled={isFormDisabled}
onChange={(evt) => setEmail(evt.target.value)}
/>
@@ -86,12 +90,15 @@ export default function Login(): JSX.Element {
placeholder="Password"
required
value={password}
+ disabled={isFormDisabled}
+ pattern={pattern}
onChange={(evt) => setPassword(evt.target.value)}
/>
diff --git a/src/store/api-actions.ts b/src/store/api-actions.ts
index b964c61..0eb6c7d 100644
--- a/src/store/api-actions.ts
+++ b/src/store/api-actions.ts
@@ -51,7 +51,7 @@ OfferFullType,
extra: AxiosInstance;
}
>('offer/toggleOffer', async ({id, status}, {extra: api}) => {
- const { data } = await api.post(`${APIRoute.Favorites}/${id}/${status ? 1 : 0}`, status);
+ const { data } = await api.post(`${APIRoute.Favorites}/${id}/${status ? 1 : 0}`);
return data;
});
diff --git a/src/store/offers/offers-process.ts b/src/store/offers/offers-process.ts
index c03126f..aeeca4c 100644
--- a/src/store/offers/offers-process.ts
+++ b/src/store/offers/offers-process.ts
@@ -11,6 +11,7 @@ export const initialState: OffersProcessType = {
isOffersLoadingStatus: false,
hasError: false,
isFavoritesLoading: false,
+ isFavoritesLoaded: false,
};
export const offersProcess = createSlice({
@@ -32,6 +33,8 @@ export const offersProcess = createSlice({
clearFavoriteOffers:(state) => {
state.favoritesOffers = [];
state.isFavoritesLoading = false;
+ state.isFavoritesLoaded = false;
+
}
},
extraReducers(builder) {
@@ -49,32 +52,37 @@ export const offersProcess = createSlice({
state.hasError = true;
})
.addCase(toggleFavoriteOffer.fulfilled, (state, action) => {
- const updateOffer = action.payload;
- const currentOffer = state.offers.find((offer) => offer.id === updateOffer.id);
+ const updatedOffer = action.payload;
+ const currentOffer = state.offers.find((offer) => offer.id === updatedOffer.id);
if(currentOffer) {
- currentOffer.isFavorite = updateOffer.isFavorite;
+ currentOffer.isFavorite = updatedOffer.isFavorite;
- if(updateOffer.isFavorite) {
- const checkOffer = state.favoritesOffers.some((offer) => offer.id === updateOffer.id);
+ if(updatedOffer.isFavorite) {
+ const checkOffer = state.favoritesOffers.some((offer) => offer.id === updatedOffer.id);
if(!checkOffer) {
state.favoritesOffers.push(currentOffer);
}
- }else {
- state.favoritesOffers = state.favoritesOffers.filter((offer) => offer.id !== updateOffer.id);
}
+ } else {
+ state.favoritesOffers = state.favoritesOffers.filter((offer) => offer.id !== updatedOffer.id);
}
})
.addCase(fetchFavoriteOfferActions.fulfilled, (state, action) => {
state.favoritesOffers = action.payload;
- state.isFavoritesLoading = true;
+ state.isFavoritesLoading = false;
+ state.isFavoritesLoaded = true;
state.offers.forEach((offer) => {
offer.isFavorite = state.favoritesOffers.some((favorite) => favorite.id === offer.id);
});
})
+ .addCase(fetchFavoriteOfferActions.pending, (state) => {
+ state.isFavoritesLoading = true;
+ })
.addCase(fetchFavoriteOfferActions.rejected, (state) => {
state.isFavoritesLoading = false;
+ state.isFavoritesLoaded = false;
});
},
});
diff --git a/src/store/offers/selectors.ts b/src/store/offers/selectors.ts
index 7b3dfce..be7154a 100644
--- a/src/store/offers/selectors.ts
+++ b/src/store/offers/selectors.ts
@@ -8,4 +8,6 @@ export const getActiveSortType = (state: State): SortType => state[NameSpace.Off
export const getFavoritesOffers = (state: State): OfferForCardType[] => state[NameSpace.Offers].favoritesOffers;
export const getIsOffersLoadingStatus = (state: State): boolean => state[NameSpace.Offers].isOffersLoadingStatus;
export const getOffersError = (state: State): boolean => state[NameSpace.Offers].hasError;
-export const getIsFavoritesLoaded = (state: State): boolean => state[NameSpace.Offers].isFavoritesLoading;
+export const getIsFavoritesLoading = (state: State): boolean => state[NameSpace.Offers].isFavoritesLoading;
+export const getIsFavoritesLoaded = (state: State): boolean => state[NameSpace.Offers].isFavoritesLoaded;
+
diff --git a/src/types/state.ts b/src/types/state.ts
index b6780aa..f441082 100644
--- a/src/types/state.ts
+++ b/src/types/state.ts
@@ -18,6 +18,7 @@ export type OffersProcessType = {
isOffersLoadingStatus: boolean;
hasError: boolean;
isFavoritesLoading: boolean;
+ isFavoritesLoaded: boolean;
}
export type OfferProcessType = {