Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .stylelintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module.exports = {
extends: "@mate-academy/stylelint-config",
rules: {}
rules: {
"scss/at-mixin-pattern": null,
}
};
122 changes: 116 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
{
"name": "react_phone-catalog",
"homepage": "react_phone-catalog",
"version": "0.1.0",
"keywords": [],
"author": "Mate Academy",
"license": "GPL-3.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.5.2",
"@reduxjs/toolkit": "^2.11.2",
"bulma": "^1.0.1",
"classnames": "^2.5.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.2.0",
"react-router-dom": "^6.25.1",
"react-transition-group": "^4.4.5"
},
"devDependencies": {
"@cypress/react18": "^2.0.1",
"@mate-academy/scripts": "^1.8.5",
"@mate-academy/scripts": "^2.1.3",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/node": "^20.14.10",
Expand Down Expand Up @@ -55,7 +56,7 @@
"format": "prettier --write './src/**/*.{ts,tsx}'",
"lint": "npm run style-format && npm run format && npm run lint-js && npm run lint-css",
"update": "mate-scripts update",
"postinstall": "npm run update && cypress verify",
"postinstall": "npm run update",
"predeploy": "npm run build",
"deploy": "mate-scripts deploy"
},
Expand Down
13 changes: 12 additions & 1 deletion src/App.scss
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
// not empty
// not empty
.App {
min-height: 100vh;
display: flex;
flex-direction: column;

}

.main {
flex: 1;
padding-inline: 32px;
}
27 changes: 26 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
import { Route, Routes } from 'react-router-dom';
import './App.scss';
import { Footer } from './components/Footer';
import { Header } from './components/Header';
import { HomePage } from './modules/HomePage';
import { ProductsPage } from './modules/ProductsPage';
import { ProductDetailsPage } from './modules/ProductDetailsPage';
import { FavoritesPage } from './modules/FavoritesPage';
import { CartPage } from './modules/CartPage';
import { NotFoundPage } from './modules/NotFoundPage';

export const App = () => (
<div className="App">
<h1>Product Catalog</h1>
<Header />
<div className="main">
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/phones" element={<ProductsPage category="phones" />} />
<Route path="/tablets" element={<ProductsPage category="tablets" />} />
<Route
path="/accessories"
element={<ProductsPage category="accessories" />}
/>
<Route path="/:category/:productId" element={<ProductDetailsPage />} />
<Route path="/favorites" element={<FavoritesPage />} />
<Route path="/cart" element={<CartPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
<Footer />
</div>
);
8 changes: 8 additions & 0 deletions src/api/phone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PhoneType } from '../features/types/phoneType';
import { getData } from '../features/utils/client';

export async function getPhones(): Promise<PhoneType[]> {
const phones = await getData<PhoneType[]>('/api/phones.json');

return phones;
}
35 changes: 35 additions & 0 deletions src/api/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ProductDetails } from '../features/types/productDetailsType';
import { Product, ProductCategory } from '../features/types/productType';
import { getData } from '../features/utils/client';

const DETAILS_FILE_BY_CATEGORY: Record<ProductCategory, string> = {
phones: '/api/phones.json',
tablets: '/api/tablets.json',
accessories: '/api/accessories.json',
};

export function getProducts(): Promise<Product[]> {
return getData<Product[]>('/api/products.json');
}

export async function getProductDetails(
category: ProductCategory,
productId: string,
): Promise<ProductDetails> {
const url = DETAILS_FILE_BY_CATEGORY[category];

const res = await fetch(url);

if (!res.ok) {
throw new Error('Failed to load details');
}

const all: ProductDetails[] = await res.json();
const found = all.find(p => p.id === productId);

if (!found) {
throw new Error('Product not found');
}

return found;
}
Empty file added src/app/hooks.ts
Empty file.
26 changes: 26 additions & 0 deletions src/app/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { configureStore, Middleware } from '@reduxjs/toolkit';
import favoritesReducer from '../features/slices/favorites/favoritesSlice';
import cartListReducer from '../features/slices/cartSlice/cartSlice';

const localStorageMiddleware: Middleware = store => next => action => {
const result = next(action);

const state = store.getState();

localStorage.setItem('favorites', JSON.stringify(state.favorites.items));
localStorage.setItem('cartList', JSON.stringify(state.cartList.items));

return result;
};

export const store = configureStore({
reducer: {
favorites: favoritesReducer,
cartList: cartListReducer,
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(localStorageMiddleware),
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
3 changes: 3 additions & 0 deletions src/assets/icons/Favourites.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/icons/Home.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading