Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update latest prod state with [sprint-4] #373

Merged
merged 37 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3ce3d1d
fix(RSS-ECOMM-5_01): breadcrumbs text (#336)
Kleostro Jun 4, 2024
159cc37
fix(RSS-ECOMM-5_02): footer subscribe form (#337)
Kleostro Jun 4, 2024
c011816
feat(RSS-ECOMM-4_16): display promo on main page (#338)
Kleostro Jun 4, 2024
f31a378
fix(RSS-ECOMM-5_03): display promo slider (#340)
Kleostro Jun 4, 2024
5ae7cde
feat(RSS-ECOMM-5_04): formatted time function (#341)
Kleostro Jun 4, 2024
dd833f3
feat(RSS-ECOMM-5_06): implement cooperation page (#342)
Kleostro Jun 5, 2024
02caf88
feat(RSS_ECOMM-5_99): blog carts (#339)
YulikK Jun 5, 2024
ee9e6e5
feat(RSS-ECOMM-5_07): add details to user messages (#343)
stardustmeg Jun 5, 2024
2b656e4
feat(RSS-ECOMM-5_08): update post description (#344)
stardustmeg Jun 5, 2024
e049828
refactor(RSS-ECOMM-5_09): separate wishlist button into component (#345)
Kleostro Jun 6, 2024
7ae8343
feat(RSS-ECOMM-5_10): pagination in catalog bottom (#346)
Kleostro Jun 6, 2024
6917c29
feat(RSS-ECOMM-5_12): implement wishlist page (#347)
Kleostro Jun 7, 2024
1a4961c
refactor(RSS-ECOMM-5_13): wishlist (#348)
Kleostro Jun 7, 2024
19314f0
refactor(RSS-ECOMM-5_14): wishlist and catalog pages (#349)
Kleostro Jun 7, 2024
8ec81b2
feat(RSS-ECOMM-5_98)/coupon product (#350)
YulikK Jun 7, 2024
67dc9ad
feat(RSS-ECOMM-5_11): translate breadcrumbs (#351)
stardustmeg Jun 8, 2024
0bbd5e5
refactor(RSS-ECOMM-5_15): breadcrumbs rendering (#352)
Kleostro Jun 8, 2024
8ef455c
fix(RSS-ECOMM-5_97): shopList error (#354)
YulikK Jun 8, 2024
7fca25a
refactor(RSS-ECOMM-5_16): link slider and url (#353)
Kleostro Jun 8, 2024
4221d50
refactor(RSS-ECOMM-5_17): display product slider on product page and…
Kleostro Jun 9, 2024
b543802
feat(RSS-ECOMM-5_96): delete coupons (#357)
YulikK Jun 9, 2024
fd6c1b5
refactor(RSS-ECOMM-5_18): router (#358)
Kleostro Jun 10, 2024
da951e9
feat(RSS-ECOMM-5_96): update requests (#355)
YulikK Jun 10, 2024
50f5b60
feat(RSS-ECOMM-5_95): promo code banner (#359)
YulikK Jun 10, 2024
f5bc7a5
refactor(RSS-ECOMM-5_19): move cart and wishlist to top header (#360)
stardustmeg Jun 11, 2024
05cede8
feat(RSS-ECOMM-5_94): cart styles (#362)
YulikK Jun 12, 2024
8567884
refactor(RSS-ECOMM-5_20): refactor main codebase (#361)
stardustmeg Jun 12, 2024
01bf90e
feat(RSS-ECOMM-5_93): birthday check (#363)
YulikK Jun 12, 2024
899be3b
feat(RSS-ECOMM-5_93): link cart (#364)
YulikK Jun 12, 2024
2ffdcec
refactor(RSS-ECOMM-5_21): implement separate page for addresses (#365)
stardustmeg Jun 13, 2024
d0ed6ac
refactor(RSS-ECOMM-5_22): update Not Found page (#366)
stardustmeg Jun 13, 2024
36d21ee
feat(RSS-ECOMM-5_19): implement about us page (#367)
Kleostro Jun 14, 2024
6ec1242
feat(RSS-ECOMM-5_20): rss logo (#369)
Kleostro Jun 15, 2024
30f066e
feat(RSS-ECOMM-5_90: test for api and components (#370)
YulikK Jun 17, 2024
08a772d
refactor(RSS-ECOMM-5_23): remove unused elements (#368)
stardustmeg Jun 17, 2024
a9bab4d
feat(RSS-ECOMM-5_24): add mentor's photo (#371)
stardustmeg Jun 17, 2024
e8c239b
fix(RSS-ECOMM-5_25): modal close (#372)
Kleostro Jun 17, 2024
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
3 changes: 1 addition & 2 deletions .validate-branch-namerc.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module.exports = {
pattern:
/^sprint-4|^(feat|fix|hotfix|chore|refactor|revert|docs|style|test|)\(RSS-ECOMM-\d_\d{2}\)\/[a-z]+[a-zA-Z0-9]*$/,
pattern: /^(feat|fix|hotfix|chore|refactor|revert|docs|style|test|)\(RSS-ECOMM-\d_\d{2}\)\/[a-z]+[a-zA-Z0-9]*$/,
errorMsg: 'Please use correct branch name',
};

Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
"typescript": "^5.4.5",
"validate-branch-name": "^1.3.0",
"vite": "^5.2.11",
"vite-plugin-sass": "^0.1.0",
"vitest": "^1.6.0"
"vite-plugin-sass": "^0.1.0"
},
"dependencies": {
"@commercetools/api-request-builder": "^6.0.0",
Expand All @@ -66,23 +65,27 @@
"@commercetools/sdk-middleware-http": "^7.0.4",
"@types/hammerjs": "^2.0.45",
"@types/js-cookie": "^3.0.6",
"@types/sinon": "^17.0.3",
"@types/uuid": "^9.0.8",
"autoprefixer": "^10.4.19",
"hammerjs": "^2.0.8",
"isomorphic-fetch": "^3.0.0",
"js-cookie": "^3.0.5",
"materialize-css": "^1.0.0-rc.2",
"modern-normalize": "^2.0.0",
"msw": "^2.3.1",
"nouislider": "^15.7.1",
"plop": "^4.0.1",
"postcode-validator": "^3.8.20",
"sharp": "^0.33.3",
"sinon": "^18.0.0",
"swiper": "^11.1.3",
"uuid": "^9.0.1",
"vite-plugin-checker": "^0.6.4",
"vite-plugin-image-optimizer": "^1.1.7",
"vite-plugin-svg-spriter": "^1.0.0",
"vite-tsconfig-paths": "^4.3.2"
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0"
},
"homepage": "https://github.com/stardustmeg/ecommerce-application#readme",
"license": "ISC"
Expand Down
2 changes: 1 addition & 1 deletion postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
export default {
plugins: {
autoprefixer: {},
},
Expand Down
Binary file added public/img/png/expand-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/kleostroAvatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/mikaleinikAvatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/stardustmegAvatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/yulikKAvatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/webp/promo-slider-1_2.webp
Binary file not shown.
Binary file added public/img/webp/promo-slider-2_1.webp
Binary file not shown.
Binary file added public/img/webp/promo-slider-2_2.webp
Binary file not shown.
Binary file added public/img/webp/promo-slider-3_1.webp
Binary file not shown.
Binary file added public/img/webp/promo-slider-3_2.webp
Binary file not shown.
31 changes: 23 additions & 8 deletions src/app/App/model/AppModel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint-disable max-lines-per-function */
import type { Page, PageParams, PagesType } from '@/shared/types/page.ts';
import type { Page, PagesType } from '@/shared/types/page.ts';

import RouterModel from '@/app/Router/model/RouterModel.ts';
import modal from '@/shared/Modal/model/ModalModel.ts';
import ScrollToTopModel from '@/shared/ScrollToTop/model/ScrollToTopModel.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { showErrorMessage } from '@/shared/utils/userMessage.ts';
import FooterModel from '@/widgets/Footer/model/FooterModel.ts';
import HeaderModel from '@/widgets/Header/model/HeaderModel.ts';

Expand All @@ -14,8 +15,8 @@ class AppModel {
private appView: AppView = new AppView();

constructor() {
this.initialize().catch(() => {
throw new Error('AppModel initialization error');
this.initialize().catch((error) => {
showErrorMessage(error);
});
}

Expand All @@ -26,8 +27,8 @@ class AppModel {
return new AboutUsPageModel(this.appView.getHTML());
},
[PAGE_ID.BLOG]: async (): Promise<Page> => {
const { default: PostListModel } = await import('@/pages/Blog/PostList/model/PostListModel.ts');
return new PostListModel(this.appView.getHTML());
const { default: BlogPageModel } = await import('@/pages/BlogPage/model/BlogPageModel.ts');
return new BlogPageModel(this.appView.getHTML());
},
[PAGE_ID.CART_PAGE]: async (): Promise<Page> => {
const { default: CartPageModel } = await import('@/pages/CartPage/model/CartPageModel.ts');
Expand All @@ -37,6 +38,10 @@ class AppModel {
const { default: CatalogPageModel } = await import('@/pages/CatalogPage/model/CatalogPageModel.ts');
return new CatalogPageModel(this.appView.getHTML());
},
[PAGE_ID.COOPERATION_PAGE]: async (): Promise<Page> => {
const { default: CooperationPageModel } = await import('@/pages/CooperationPage/model/CooperationPageModel.ts');
return new CooperationPageModel(this.appView.getHTML());
},
[PAGE_ID.DEFAULT_PAGE]: async (): Promise<Page> => {
const { default: MainPageModel } = await import('@/pages/MainPage/model/MainPageModel.ts');
return new MainPageModel(this.appView.getHTML());
Expand All @@ -53,23 +58,33 @@ class AppModel {
const { default: NotFoundPageModel } = await import('@/pages/NotFoundPage/model/NotFoundPageModel.ts');
return new NotFoundPageModel(this.appView.getHTML());
},
[PAGE_ID.PRODUCT_PAGE]: async (params: PageParams): Promise<Page> => {
[PAGE_ID.PRODUCT_PAGE]: async (): Promise<Page> => {
const { default: ProductPageModel } = await import('@/pages/ProductPage/model/ProductPageModel.ts');
return new ProductPageModel(this.appView.getHTML(), params);
return new ProductPageModel(this.appView.getHTML());
},
[PAGE_ID.REGISTRATION_PAGE]: async (): Promise<Page> => {
const { default: RegistrationPageModel } = await import(
'@/pages/RegistrationPage/model/RegistrationPageModel.ts'
);
return new RegistrationPageModel(this.appView.getHTML());
},
[PAGE_ID.USER_ADDRESSES_PAGE]: async (): Promise<Page> => {
const { default: UserAddressesPageModel } = await import(
'@/pages/UserAddressesPage/model/UserAddressesPageModel.ts'
);
return new UserAddressesPageModel(this.appView.getHTML());
},
[PAGE_ID.USER_PROFILE_PAGE]: async (): Promise<Page> => {
const { default: UserProfilePageModel } = await import('@/pages/UserProfilePage/model/UserProfilePageModel.ts');
return new UserProfilePageModel(this.appView.getHTML());
},
[PAGE_ID.WISHLIST_PAGE]: async (): Promise<Page> => {
const { default: WishlistPageModel } = await import('@/pages/WishlistPage/model/WishlistPageModel.ts');
return new WishlistPageModel(this.appView.getHTML());
},
};

const routes = new Map<string, (params: PageParams) => Promise<Page>>();
const routes = new Map<string, () => Promise<Page>>();
Object.entries(routesMap).forEach(([key, value]) => routes.set(key, value));

return Promise.resolve(routes);
Expand Down
4 changes: 4 additions & 0 deletions src/app/App/tests/App.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import AppModel from '../model/AppModel.ts';

const app = new AppModel();

/**
* @vitest-environment jsdom
*/

describe('Checking AppModel class', () => {
it('application successfully created', () => {
expect(app.start()).toBe(true);
Expand Down
6 changes: 5 additions & 1 deletion src/app/App/view/appView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
flex-direction: column;
justify-content: center;
margin: 0 auto;
padding: var(--medium-offset) 0;
padding: calc(var(--medium-offset) * 2) 0;
width: 100%;
min-height: calc(100vh - calc(var(--extra-small-offset) * 7.1));

@media (max-width: 768px) {
padding: calc(var(--medium-offset) * 2.65) 0;
}
}
9 changes: 9 additions & 0 deletions src/app/Router/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const remove = (url: URL, key: string | string[]): void => {
if (Array.isArray(key)) {
key.forEach((k) => url.searchParams.delete(k));
} else {
url.searchParams.delete(key);
}
};
export const append = (url: URL, key: string, value: string): void => url.searchParams.append(key, value);
export const set = (url: URL, key: string, value: string): void => url.searchParams.set(key, value);
72 changes: 35 additions & 37 deletions src/app/Router/model/RouterModel.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { PageParams, PagesType } from '@/shared/types/page';
import type { PagesType } from '@/shared/types/page';

import getStore from '@/shared/Store/Store.ts';
import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { isValidPath, isValidState } from '@/shared/types/validation/paths.ts';
import setPageTitle from '@/shared/utils/setPageTitle.ts';
import showErrorMessage from '@/shared/utils/userMessage.ts';
import { showErrorMessage } from '@/shared/utils/userMessage.ts';

const DEFAULT_SEGMENT = import.meta.env.VITE_APP_DEFAULT_SEGMENT;
const NEXT_SEGMENT = import.meta.env.VITE_APP_NEXT_SEGMENT;
Expand Down Expand Up @@ -45,13 +45,13 @@ class RouterModel {
return;
}

this.handleRequest(currentPage, currentPath);
this.handleRequest(currentPage, currentPath).catch(showErrorMessage);
});
}

public static appendSearchParams(key: string, value: string): void {
public static changeSearchParams(callback: (url: URL) => void): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.append(key, value);
callback(url);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}
Expand All @@ -62,58 +62,56 @@ class RouterModel {
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}

public static deleteSearchParams(key: string): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.delete(key);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
public static getCurrentPage(): string {
return window.location.pathname.slice(PATH_SEGMENTS_TO_KEEP).split(DEFAULT_SEGMENT)[NEXT_SEGMENT];
}

public static getInstance(): RouterModel {
return RouterModel.router;
}

public static getSearchParams(): URLSearchParams {
return new URL(decodeURIComponent(window.location.href)).searchParams;
public static getPageID(): null | string {
return (
(window.location.pathname + window.location.search)
.slice(NEXT_SEGMENT)
.split(DEFAULT_SEGMENT)
.join(DEFAULT_SEGMENT)
.split(DEFAULT_SEGMENT)
[NEXT_SEGMENT]?.split(SEARCH_SEGMENT)[PATH_SEGMENTS_TO_KEEP] || null
);
}

public static setSearchParams(key: string, value: string): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.delete(key);
url.searchParams.set(key, value);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
public static getSavedPath(): string {
const path =
window.location.pathname.slice(NEXT_SEGMENT).split(DEFAULT_SEGMENT).join(DEFAULT_SEGMENT) || PAGE_ID.DEFAULT_PAGE;
return path + window.location.search;
}

public static getSearchParams(): URLSearchParams {
return new URL(decodeURIComponent(window.location.href)).searchParams;
}

private async checkPageAndParams(
currentPage: string,
path: string,
): Promise<{ hasRoute: boolean; params: PageParams } | null> {
private async checkPageAndParams(currentPage: string, path: string): Promise<boolean | null> {
const hasRoute = this.routes.has(currentPage);
const id = path.split(DEFAULT_SEGMENT)[NEXT_SEGMENT]?.split(SEARCH_SEGMENT)[PATH_SEGMENTS_TO_KEEP] || null;

setPageTitle(currentPage, hasRoute);
observeStore(selectCurrentLanguage, () => this.checkPageAndParams(currentPage, path));

if (!hasRoute) {
await this.routes.get(PAGE_ID.NOT_FOUND_PAGE)?.({});
await this.routes.get(PAGE_ID.NOT_FOUND_PAGE)?.();
return null;
}

return {
hasRoute,
params: { [currentPage.slice(PATH_SEGMENTS_TO_KEEP)]: { id } },
};
return hasRoute;
}

private handleRequest(currentPage: string, path: string): void {
this.checkPageAndParams(currentPage, path)
.then((check) => {
if (check) {
this.routes.get(currentPage)?.(check.params).catch(showErrorMessage);
}
})
.catch(showErrorMessage);
private async handleRequest(currentPage: string, path: string): Promise<void> {
try {
await this.checkPageAndParams(currentPage, path);
this.routes.get(currentPage)?.().catch(showErrorMessage);
} catch (error) {
showErrorMessage(error);
}
}

public navigateTo(path: string): void {
Expand All @@ -122,7 +120,7 @@ class RouterModel {
PAGE_ID.DEFAULT_PAGE;

if (currentPage !== getStore().getState().currentPage || currentPage === PAGE_ID.DEFAULT_PAGE) {
this.handleRequest(currentPage, path);
this.handleRequest(currentPage, path).catch(showErrorMessage);
}
history.pushState({ path }, '', `/${path}`);
}
Expand Down
7 changes: 6 additions & 1 deletion src/app/styles/common.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
* {
box-sizing: border-box;

&::selection {
background-color: var(--steam-green-1000);
}
}

html,
Expand All @@ -17,7 +21,7 @@ body {
}

html {
font-size: clamp(12px, 1vw, 50px);
font-size: clamp(12px, 1.1vw, 50px);
}

body {
Expand All @@ -44,6 +48,7 @@ img {
max-width: 100%;
}

/* stylelint-disable-next-line selector-class-pattern */
.stop-scroll {
overflow-y: hidden;
}
40 changes: 40 additions & 0 deletions src/app/styles/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,46 @@ $one: var(--one);
$two: var(--two);
$active-color: var(--steam-green-800);

@mixin switchLabel {
position: relative;
display: inline-block;
width: var(--large-offset);
height: calc(calc(var(--small-offset) / 1.5) + var(--extra-small-offset));
cursor: pointer;
}

@mixin switchCheckbox {
width: 0;
height: 0;
opacity: 1;
}

@mixin switchLabelSpan($label-span-color: var(--noble-gray-tr-800), $label-span-before-color: var(--steam-green-800)) {
position: absolute;
border-radius: calc(var(--large-br) * 2);
background-color: $label-span-color;
transition: 0.3s cubic-bezier(0.8, 0.5, 0.2, 1.4);
cursor: pointer;
pointer-events: none;
inset: 0;

&::before {
content: '';
position: absolute;
left: calc(var(--one) * 5);
top: 50%;
bottom: 0;
z-index: 2;
border-radius: var(--large-br);
width: calc(var(--small-offset) / 1.5);
height: calc(var(--small-offset) / 1.5);
box-shadow: var(--mellow-shadow-600);
background-color: $label-span-before-color;
transform: translateY(-50%);
transition: 0.3s cubic-bezier(0.8, 0.5, 0.2, 1.4);
}
}

@mixin link($padding: 0 calc(var(--extra-small-offset) / 2), $color: var(--noble-gray-800)) {
position: relative;
display: flex;
Expand Down
Loading
Loading