diff --git a/.eslintrc.json b/.eslintrc.json index a4d86d3..8d82eb5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,19 +16,10 @@ }, "ecmaVersion": "latest" }, - "plugins": [ - "react-refresh", - "@typescript-eslint", - "simple-import-sort", - "prettier" - ], + "plugins": ["@typescript-eslint", "simple-import-sort", "prettier"], "rules": { "prettier/prettier": "error", "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "react-refresh/only-export-components": [ - "warn", - { "allowConstantExport": true } - ] + "simple-import-sort/exports": "error" } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd9d55..24b6e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,72 +1,69 @@ # 1.0.0 (2024-07-03) - ### Bug Fixes -* add `textColors` constant to manager colors of `Text` component ([1ce1641](https://github.com/ArtelierMaisa/artelier-maisa/commit/1ce16411952e34ecd34e2d23ce343609c5db115c)) -* add correct `favicon` image ([978ded8](https://github.com/ArtelierMaisa/artelier-maisa/commit/978ded8fec4a8277567ad52178de524c1cff9ac9)) -* add cover to profile image ([54ec48e](https://github.com/ArtelierMaisa/artelier-maisa/commit/54ec48e7aa1c13f6f4f86e059824b199f69f5165)) -* add highlight to tab products when user open platform directly in the `Product` page ([5ae34ab](https://github.com/ArtelierMaisa/artelier-maisa/commit/5ae34ab8ceed56646f410df78ec89718a281b206)) -* add safe `z-index` to overlay in `Modal` component ([903f119](https://github.com/ArtelierMaisa/artelier-maisa/commit/903f1196df4e0f94397a7043ed9178178f2769f3)) -* add scroll Y auto to `Modal` component and custom style ([29587f8](https://github.com/ArtelierMaisa/artelier-maisa/commit/29587f829131444bdd45eeeef4cdcd603ad7280c)) -* adjust in `SendMessageProps` interface when its used by `WhatsAppButton` and `Footer` ([5403d73](https://github.com/ArtelierMaisa/artelier-maisa/commit/5403d73a8dce639620668f57cf7fc3c94a304c4e)) -* bigger adjusts in colors and sizes of `Icon` types component, and size of `Text` component ([9402a49](https://github.com/ArtelierMaisa/artelier-maisa/commit/9402a49b647f8b2154591690b35e0036cfb842a6)) -* improve `Text` component and add responsiveness to `Footer` component ([c0218bc](https://github.com/ArtelierMaisa/artelier-maisa/commit/c0218bc18f87166fe96363692602443925a03787)) -* release 1.0.3 ([48dd83d](https://github.com/ArtelierMaisa/artelier-maisa/commit/48dd83d9ca8f5bee52cb61293c34a8362b8f6233)) -* remove Uri import ([4d933b6](https://github.com/ArtelierMaisa/artelier-maisa/commit/4d933b6917b545125a347be074f63d58c722d39b)) -* resolve problem when products doesn't exists in category ([d2d625e](https://github.com/ArtelierMaisa/artelier-maisa/commit/d2d625e25fc61b01e427c4432cd790e14786f8ca)) -* small adjust in favicon path image ([9da52a1](https://github.com/ArtelierMaisa/artelier-maisa/commit/9da52a185b75191f635188e411842056fd675a4d)) - +- add `textColors` constant to manager colors of `Text` component ([1ce1641](https://github.com/ArtelierMaisa/artelier-maisa/commit/1ce16411952e34ecd34e2d23ce343609c5db115c)) +- add correct `favicon` image ([978ded8](https://github.com/ArtelierMaisa/artelier-maisa/commit/978ded8fec4a8277567ad52178de524c1cff9ac9)) +- add cover to profile image ([54ec48e](https://github.com/ArtelierMaisa/artelier-maisa/commit/54ec48e7aa1c13f6f4f86e059824b199f69f5165)) +- add highlight to tab products when user open platform directly in the `Product` page ([5ae34ab](https://github.com/ArtelierMaisa/artelier-maisa/commit/5ae34ab8ceed56646f410df78ec89718a281b206)) +- add safe `z-index` to overlay in `Modal` component ([903f119](https://github.com/ArtelierMaisa/artelier-maisa/commit/903f1196df4e0f94397a7043ed9178178f2769f3)) +- add scroll Y auto to `Modal` component and custom style ([29587f8](https://github.com/ArtelierMaisa/artelier-maisa/commit/29587f829131444bdd45eeeef4cdcd603ad7280c)) +- adjust in `SendMessageProps` interface when its used by `WhatsAppButton` and `Footer` ([5403d73](https://github.com/ArtelierMaisa/artelier-maisa/commit/5403d73a8dce639620668f57cf7fc3c94a304c4e)) +- bigger adjusts in colors and sizes of `Icon` types component, and size of `Text` component ([9402a49](https://github.com/ArtelierMaisa/artelier-maisa/commit/9402a49b647f8b2154591690b35e0036cfb842a6)) +- improve `Text` component and add responsiveness to `Footer` component ([c0218bc](https://github.com/ArtelierMaisa/artelier-maisa/commit/c0218bc18f87166fe96363692602443925a03787)) +- release 1.0.3 ([48dd83d](https://github.com/ArtelierMaisa/artelier-maisa/commit/48dd83d9ca8f5bee52cb61293c34a8362b8f6233)) +- remove Uri import ([4d933b6](https://github.com/ArtelierMaisa/artelier-maisa/commit/4d933b6917b545125a347be074f63d58c722d39b)) +- resolve problem when products doesn't exists in category ([d2d625e](https://github.com/ArtelierMaisa/artelier-maisa/commit/d2d625e25fc61b01e427c4432cd790e14786f8ca)) +- small adjust in favicon path image ([9da52a1](https://github.com/ArtelierMaisa/artelier-maisa/commit/9da52a185b75191f635188e411842056fd675a4d)) ### Features -* add `.env.example` ([84922f8](https://github.com/ArtelierMaisa/artelier-maisa/commit/84922f84ed80baf438637f4ee5749a906972d32b)) -* add `CarouselButton` and `Carousel` components ([8e2e052](https://github.com/ArtelierMaisa/artelier-maisa/commit/8e2e05253e7e518186454ea823cf749fa1800e33)) -* add `CarouselEvent` component and small style adjust in `ProductCard` component ([c2580c7](https://github.com/ArtelierMaisa/artelier-maisa/commit/c2580c7bdb5aab85d92f2bf74638a11884f86409)) -* add `CarouselImage` component ([fa1a946](https://github.com/ArtelierMaisa/artelier-maisa/commit/fa1a946cb4931745261625b4fd7e6d9fa31c68e1)) -* add `firabase` config ([c6458ea](https://github.com/ArtelierMaisa/artelier-maisa/commit/c6458eac190a47f54abcd09075d93195030815d4)) -* add `flowbite` and `flowbite-react` config ([2eb6203](https://github.com/ArtelierMaisa/artelier-maisa/commit/2eb6203d62d9b850e10abda07b7a73eebba35245)) -* add `Footer` component ([ac105b9](https://github.com/ArtelierMaisa/artelier-maisa/commit/ac105b986893761ace626b363779d35b41a112dc)) -* add `GenericButton` component ([a86f529](https://github.com/ArtelierMaisa/artelier-maisa/commit/a86f5298e2c6653fea84a1db4219f2ca8c8d5b25)) -* add `Header` component ([dd0d4b9](https://github.com/ArtelierMaisa/artelier-maisa/commit/dd0d4b925f78cb447087b372b5a4849fd299e31f)) -* add `inter` font to tailwind ([125f10c](https://github.com/ArtelierMaisa/artelier-maisa/commit/125f10c0f17a90aafa1b323bef74b78146c7a9a4)) -* add `isLoading` property to `GenericButton` component ([a38c559](https://github.com/ArtelierMaisa/artelier-maisa/commit/a38c55945eff5f65b5cb055046a4f3965a39b25b)) -* add `Metric` component and custom `box-shadow` in `tailwindcss` ([f1646e5](https://github.com/ArtelierMaisa/artelier-maisa/commit/f1646e5e1a95b0bee13776a0d49f6898b9b67997)) -* add `Modal` component ([2458bea](https://github.com/ArtelierMaisa/artelier-maisa/commit/2458bea6e85aa731268f1cf6373a98350404ef6e)) -* add `Modal` component and `tailwind-scrollbar` plugin ([5e535b5](https://github.com/ArtelierMaisa/artelier-maisa/commit/5e535b5d07f0b8e90e4070bff2777e7c229ea196)) -* add `NotFound` page ([63523b7](https://github.com/ArtelierMaisa/artelier-maisa/commit/63523b7efc9a155c1315381b1b6d3729e6cb01ef)) -* add `ProductCard` component ([af05290](https://github.com/ArtelierMaisa/artelier-maisa/commit/af05290efabdb78f37506f9212f1bba736d163b2)) -* add `react-scroll` for scroll to correctly section in structure of landing page ([2e4ee45](https://github.com/ArtelierMaisa/artelier-maisa/commit/2e4ee45b20114788f2cb6ddf34fd625ec0d17cde)) -* add `ScrollTopContext` and `useScrollTop` to when we go back from `Products` page ([a14ef70](https://github.com/ArtelierMaisa/artelier-maisa/commit/a14ef700a6ce3f8aafd7b4d62e1a15e7ec62491c)) -* add `SearchInput` component ([a6abe6e](https://github.com/ArtelierMaisa/artelier-maisa/commit/a6abe6ecbc211a0c28a96f26a78076f39c8a6280)) -* add `Spinner` component ([cd57837](https://github.com/ArtelierMaisa/artelier-maisa/commit/cd578373a63b0e71ee9e9e236a782eb3e21e6983)) -* add `tailwindcss ` and setup colors ([afd6e85](https://github.com/ArtelierMaisa/artelier-maisa/commit/afd6e85942776176400409243bee14305eb04a33)) -* add `useUser` hook to handle with user requests ([08c29c9](https://github.com/ArtelierMaisa/artelier-maisa/commit/08c29c9ef3e2fd69f7f2422e33fa6c51c18598fa)) -* add `weights` and `sizes` of `Text` component in `constants` folder ([f83e82c](https://github.com/ArtelierMaisa/artelier-maisa/commit/f83e82cdf2b9ad33bf96c903bf82e3813d88753f)) -* add `WhatsAppButton` component ([c03307a](https://github.com/ArtelierMaisa/artelier-maisa/commit/c03307a46ffeab70b60627d7730f6ad3734ae366)) -* add conditional to show `additional` of the about in `LandingPage` ([309c8e9](https://github.com/ArtelierMaisa/artelier-maisa/commit/309c8e99266014b3f19593d74cfbe7dbcd2b5663)) -* add default phone and email ([e955c99](https://github.com/ArtelierMaisa/artelier-maisa/commit/e955c99fd30b8d6b1189a39882147b359cfd3677)) -* add integration in the `Products` page ([728339e](https://github.com/ArtelierMaisa/artelier-maisa/commit/728339ee590d17c6708887164bf981fea6da0c09)) -* add integration with banners ([2ad30de](https://github.com/ArtelierMaisa/artelier-maisa/commit/2ad30ded2d1d4aae0507673891d5fd3d9dc9ee51)) -* add integration with highlight data ([0e5086f](https://github.com/ArtelierMaisa/artelier-maisa/commit/0e5086f39ef838f6ba820ce01ee092123a5db085)) -* add integration with WhatsApp API ([18303e5](https://github.com/ArtelierMaisa/artelier-maisa/commit/18303e56414cc868d2fa527a11e3fad818a6fc3b)) -* add mail to in envelope icon in the `Footer` component ([99d3e86](https://github.com/ArtelierMaisa/artelier-maisa/commit/99d3e86d5ae4b6e4dc5f74c7e4752d362a2067c9)) -* add products to `LandingPage` ([997699e](https://github.com/ArtelierMaisa/artelier-maisa/commit/997699e59ea4c34f7f8b4ed86e044a1365a6f66a)) -* add project logo ([38fd301](https://github.com/ArtelierMaisa/artelier-maisa/commit/38fd301a299623c89b0ad55843bb2697da1d7895)) -* add routes in `App` ([f0886c0](https://github.com/ArtelierMaisa/artelier-maisa/commit/f0886c00398dd3c2bba1bf5f535be78c19e745ce)) -* add support ts to images ([c0dfd4e](https://github.com/ArtelierMaisa/artelier-maisa/commit/c0dfd4e391f7b39401e3d0420b27e0dee80e06f8)) -* create `envs` file to manager all environment variables ([8f58e8a](https://github.com/ArtelierMaisa/artelier-maisa/commit/8f58e8a82651678c003c49a96b80fd0c1f61f657)) -* create `react` project with `vite` ([597aab4](https://github.com/ArtelierMaisa/artelier-maisa/commit/597aab4465565f84ca35b9a753067d7e484dc515)) -* create `Text` component ([7d9551a](https://github.com/ArtelierMaisa/artelier-maisa/commit/7d9551a1a0f76cd951df7250b44ba3eed14ff2ae)) -* create Icon component ([01df400](https://github.com/ArtelierMaisa/artelier-maisa/commit/01df400fcbdafcf31423c7dc03848a41aae0ce7b)) -* finish `LandingPage` structure and style ([06a1697](https://github.com/ArtelierMaisa/artelier-maisa/commit/06a1697e2e3ed5afdd4398ea16add925d7f7d5a5)) -* finish structure and styles from Products page ([ebdeff4](https://github.com/ArtelierMaisa/artelier-maisa/commit/ebdeff4ae320a11ae5aaab56de030fdc666b2685)) -* integrate `Header` component with `react-router-dom` ([26891c9](https://github.com/ArtelierMaisa/artelier-maisa/commit/26891c911a541381c101332a75c8ed80b5144141)) -* release 1.0.0 ([152a936](https://github.com/ArtelierMaisa/artelier-maisa/commit/152a9361cedba93aac9d738918830cce424fa62e)) -* release 1.0.1 ([4a419f5](https://github.com/ArtelierMaisa/artelier-maisa/commit/4a419f5135d7875f19b7e1ef86a940d339252559)) -* release 1.0.2 ([6415cef](https://github.com/ArtelierMaisa/artelier-maisa/commit/6415cef7178b58e41592d2ac1dfbedbaba60ae01)) - +- add `.env.example` ([84922f8](https://github.com/ArtelierMaisa/artelier-maisa/commit/84922f84ed80baf438637f4ee5749a906972d32b)) +- add `CarouselButton` and `Carousel` components ([8e2e052](https://github.com/ArtelierMaisa/artelier-maisa/commit/8e2e05253e7e518186454ea823cf749fa1800e33)) +- add `CarouselEvent` component and small style adjust in `ProductCard` component ([c2580c7](https://github.com/ArtelierMaisa/artelier-maisa/commit/c2580c7bdb5aab85d92f2bf74638a11884f86409)) +- add `CarouselImage` component ([fa1a946](https://github.com/ArtelierMaisa/artelier-maisa/commit/fa1a946cb4931745261625b4fd7e6d9fa31c68e1)) +- add `firabase` config ([c6458ea](https://github.com/ArtelierMaisa/artelier-maisa/commit/c6458eac190a47f54abcd09075d93195030815d4)) +- add `flowbite` and `flowbite-react` config ([2eb6203](https://github.com/ArtelierMaisa/artelier-maisa/commit/2eb6203d62d9b850e10abda07b7a73eebba35245)) +- add `Footer` component ([ac105b9](https://github.com/ArtelierMaisa/artelier-maisa/commit/ac105b986893761ace626b363779d35b41a112dc)) +- add `GenericButton` component ([a86f529](https://github.com/ArtelierMaisa/artelier-maisa/commit/a86f5298e2c6653fea84a1db4219f2ca8c8d5b25)) +- add `Header` component ([dd0d4b9](https://github.com/ArtelierMaisa/artelier-maisa/commit/dd0d4b925f78cb447087b372b5a4849fd299e31f)) +- add `inter` font to tailwind ([125f10c](https://github.com/ArtelierMaisa/artelier-maisa/commit/125f10c0f17a90aafa1b323bef74b78146c7a9a4)) +- add `isLoading` property to `GenericButton` component ([a38c559](https://github.com/ArtelierMaisa/artelier-maisa/commit/a38c55945eff5f65b5cb055046a4f3965a39b25b)) +- add `Metric` component and custom `box-shadow` in `tailwindcss` ([f1646e5](https://github.com/ArtelierMaisa/artelier-maisa/commit/f1646e5e1a95b0bee13776a0d49f6898b9b67997)) +- add `Modal` component ([2458bea](https://github.com/ArtelierMaisa/artelier-maisa/commit/2458bea6e85aa731268f1cf6373a98350404ef6e)) +- add `Modal` component and `tailwind-scrollbar` plugin ([5e535b5](https://github.com/ArtelierMaisa/artelier-maisa/commit/5e535b5d07f0b8e90e4070bff2777e7c229ea196)) +- add `NotFound` page ([63523b7](https://github.com/ArtelierMaisa/artelier-maisa/commit/63523b7efc9a155c1315381b1b6d3729e6cb01ef)) +- add `ProductCard` component ([af05290](https://github.com/ArtelierMaisa/artelier-maisa/commit/af05290efabdb78f37506f9212f1bba736d163b2)) +- add `react-scroll` for scroll to correctly section in structure of landing page ([2e4ee45](https://github.com/ArtelierMaisa/artelier-maisa/commit/2e4ee45b20114788f2cb6ddf34fd625ec0d17cde)) +- add `ScrollTopContext` and `useScrollTop` to when we go back from `Products` page ([a14ef70](https://github.com/ArtelierMaisa/artelier-maisa/commit/a14ef700a6ce3f8aafd7b4d62e1a15e7ec62491c)) +- add `SearchInput` component ([a6abe6e](https://github.com/ArtelierMaisa/artelier-maisa/commit/a6abe6ecbc211a0c28a96f26a78076f39c8a6280)) +- add `Spinner` component ([cd57837](https://github.com/ArtelierMaisa/artelier-maisa/commit/cd578373a63b0e71ee9e9e236a782eb3e21e6983)) +- add `tailwindcss ` and setup colors ([afd6e85](https://github.com/ArtelierMaisa/artelier-maisa/commit/afd6e85942776176400409243bee14305eb04a33)) +- add `useUser` hook to handle with user requests ([08c29c9](https://github.com/ArtelierMaisa/artelier-maisa/commit/08c29c9ef3e2fd69f7f2422e33fa6c51c18598fa)) +- add `weights` and `sizes` of `Text` component in `constants` folder ([f83e82c](https://github.com/ArtelierMaisa/artelier-maisa/commit/f83e82cdf2b9ad33bf96c903bf82e3813d88753f)) +- add `WhatsAppButton` component ([c03307a](https://github.com/ArtelierMaisa/artelier-maisa/commit/c03307a46ffeab70b60627d7730f6ad3734ae366)) +- add conditional to show `additional` of the about in `LandingPage` ([309c8e9](https://github.com/ArtelierMaisa/artelier-maisa/commit/309c8e99266014b3f19593d74cfbe7dbcd2b5663)) +- add default phone and email ([e955c99](https://github.com/ArtelierMaisa/artelier-maisa/commit/e955c99fd30b8d6b1189a39882147b359cfd3677)) +- add integration in the `Products` page ([728339e](https://github.com/ArtelierMaisa/artelier-maisa/commit/728339ee590d17c6708887164bf981fea6da0c09)) +- add integration with banners ([2ad30de](https://github.com/ArtelierMaisa/artelier-maisa/commit/2ad30ded2d1d4aae0507673891d5fd3d9dc9ee51)) +- add integration with highlight data ([0e5086f](https://github.com/ArtelierMaisa/artelier-maisa/commit/0e5086f39ef838f6ba820ce01ee092123a5db085)) +- add integration with WhatsApp API ([18303e5](https://github.com/ArtelierMaisa/artelier-maisa/commit/18303e56414cc868d2fa527a11e3fad818a6fc3b)) +- add mail to in envelope icon in the `Footer` component ([99d3e86](https://github.com/ArtelierMaisa/artelier-maisa/commit/99d3e86d5ae4b6e4dc5f74c7e4752d362a2067c9)) +- add products to `LandingPage` ([997699e](https://github.com/ArtelierMaisa/artelier-maisa/commit/997699e59ea4c34f7f8b4ed86e044a1365a6f66a)) +- add project logo ([38fd301](https://github.com/ArtelierMaisa/artelier-maisa/commit/38fd301a299623c89b0ad55843bb2697da1d7895)) +- add routes in `App` ([f0886c0](https://github.com/ArtelierMaisa/artelier-maisa/commit/f0886c00398dd3c2bba1bf5f535be78c19e745ce)) +- add support ts to images ([c0dfd4e](https://github.com/ArtelierMaisa/artelier-maisa/commit/c0dfd4e391f7b39401e3d0420b27e0dee80e06f8)) +- create `envs` file to manager all environment variables ([8f58e8a](https://github.com/ArtelierMaisa/artelier-maisa/commit/8f58e8a82651678c003c49a96b80fd0c1f61f657)) +- create `react` project with `vite` ([597aab4](https://github.com/ArtelierMaisa/artelier-maisa/commit/597aab4465565f84ca35b9a753067d7e484dc515)) +- create `Text` component ([7d9551a](https://github.com/ArtelierMaisa/artelier-maisa/commit/7d9551a1a0f76cd951df7250b44ba3eed14ff2ae)) +- create Icon component ([01df400](https://github.com/ArtelierMaisa/artelier-maisa/commit/01df400fcbdafcf31423c7dc03848a41aae0ce7b)) +- finish `LandingPage` structure and style ([06a1697](https://github.com/ArtelierMaisa/artelier-maisa/commit/06a1697e2e3ed5afdd4398ea16add925d7f7d5a5)) +- finish structure and styles from Products page ([ebdeff4](https://github.com/ArtelierMaisa/artelier-maisa/commit/ebdeff4ae320a11ae5aaab56de030fdc666b2685)) +- integrate `Header` component with `react-router-dom` ([26891c9](https://github.com/ArtelierMaisa/artelier-maisa/commit/26891c911a541381c101332a75c8ed80b5144141)) +- release 1.0.0 ([152a936](https://github.com/ArtelierMaisa/artelier-maisa/commit/152a9361cedba93aac9d738918830cce424fa62e)) +- release 1.0.1 ([4a419f5](https://github.com/ArtelierMaisa/artelier-maisa/commit/4a419f5135d7875f19b7e1ef86a940d339252559)) +- release 1.0.2 ([6415cef](https://github.com/ArtelierMaisa/artelier-maisa/commit/6415cef7178b58e41592d2ac1dfbedbaba60ae01)) ### Performance Improvements -* change `Text` to span when it's a text inside text ([10969e1](https://github.com/ArtelierMaisa/artelier-maisa/commit/10969e1681bbdd92e50eba35fc209420392fb58c)) +- change `Text` to span when it's a text inside text ([10969e1](https://github.com/ArtelierMaisa/artelier-maisa/commit/10969e1681bbdd92e50eba35fc209420392fb58c)) diff --git a/index.html b/index.html index b6c1b0b..4001b85 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ + @@ -13,6 +14,45 @@ rel="stylesheet" /> + + + + + + + + + + + + + + + + + + + + + + + + Artelier Maisa diff --git a/package-lock.json b/package-lock.json index 41bea3d..1afb084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,11 @@ "firebase": "^10.11.1", "flowbite": "^2.3.0", "flowbite-react": "^0.9.0", + "i18next": "^23.11.5", + "i18next-browser-languagedetector": "^8.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^14.1.2", "react-modal": "^3.16.1", "react-router-dom": "^6.23.1", "react-scroll": "^1.9.0", @@ -38,7 +41,6 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-simple-import-sort": "^12.1.0", "git-commit-msg-linter": "^5.0.7", "husky": "^9.0.11", @@ -53,7 +55,7 @@ "vite": "^5.2.0" }, "engines": { - "node": ">= 20.11.x" + "node": ">= 20.x" } }, "node_modules/@alloc/quick-lru": { @@ -4777,15 +4779,6 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz", - "integrity": "sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==", - "dev": true, - "peerDependencies": { - "eslint": ">=7" - } - }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5878,6 +5871,14 @@ "node": "14 || >=16.14" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -5933,6 +5934,36 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18next": { + "version": "23.11.5", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz", + "integrity": "sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz", + "integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -10959,6 +10990,27 @@ "react": "^18.3.1" } }, + "node_modules/react-i18next": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.2.tgz", + "integrity": "sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-icons": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz", @@ -12820,6 +12872,14 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/package.json b/package.json index beb520b..b6a98c3 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,11 @@ "firebase": "^10.11.1", "flowbite": "^2.3.0", "flowbite-react": "^0.9.0", + "i18next": "^23.11.5", + "i18next-browser-languagedetector": "^8.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^14.1.2", "react-modal": "^3.16.1", "react-router-dom": "^6.23.1", "react-scroll": "^1.9.0", @@ -42,7 +45,6 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-simple-import-sort": "^12.1.0", "git-commit-msg-linter": "^5.0.7", "husky": "^9.0.11", diff --git a/src/@types/components/CarouselImage.ts b/src/@types/components/CarouselImage.ts index f5aa9eb..f96c2e4 100644 --- a/src/@types/components/CarouselImage.ts +++ b/src/@types/components/CarouselImage.ts @@ -1,4 +1,4 @@ export interface CarouselImageProps { - id: string; uri: string; + name: string; } diff --git a/src/@types/components/Modal.ts b/src/@types/components/Modal.ts index 78c9db5..eccd981 100644 --- a/src/@types/components/Modal.ts +++ b/src/@types/components/Modal.ts @@ -1,6 +1,7 @@ export interface ProductImageProps { id: string; uri: string; + name: string; } export interface ProductProps { diff --git a/src/@types/components/ProductCard.ts b/src/@types/components/ProductCard.ts index ef5babc..7351322 100644 --- a/src/@types/components/ProductCard.ts +++ b/src/@types/components/ProductCard.ts @@ -1,5 +1,4 @@ export interface ProductCardProps { - id: string; name: string; description: string; price: string; diff --git a/src/@types/components/Translator.ts b/src/@types/components/Translator.ts new file mode 100644 index 0000000..7022ff9 --- /dev/null +++ b/src/@types/components/Translator.ts @@ -0,0 +1,5 @@ +export type LanguageType = 'pt-BR' | 'en-US'; + +export interface TranslatorProps { + path: string; +} diff --git a/src/@types/components/index.ts b/src/@types/components/index.ts index 9322e74..cde66ce 100644 --- a/src/@types/components/index.ts +++ b/src/@types/components/index.ts @@ -10,4 +10,5 @@ export * from './ProductCard'; export * from './SearchInput'; export * from './Spinner'; export * from './Text'; +export * from './Translator'; export * from './WhatsAppButton'; diff --git a/src/@types/utils/whatsapp.ts b/src/@types/utils/whatsapp.ts index 9712e32..53fef69 100644 --- a/src/@types/utils/whatsapp.ts +++ b/src/@types/utils/whatsapp.ts @@ -1,6 +1,4 @@ -import { WhatsAppButtonProps } from '../components'; - -export type SendMessageType = 'footer' | 'whatsapp-button'; -export interface SendMessageProps extends Partial { - type?: SendMessageType; +export interface BuildWhatsAppUrlProps { + message: string; + phone?: string; } diff --git a/src/App.tsx b/src/App.tsx index 53ced20..d5b29d3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import './styles/global.scss'; import { Flowbite } from 'flowbite-react'; +import { Toaster } from 'sonner'; import { UserProvider } from './contexts'; import { Router } from './routes'; @@ -9,6 +10,14 @@ import { flowbiteTheme } from './styles'; function App() { return ( + + diff --git a/src/assets/images/brazil.jpg b/src/assets/images/brazil.jpg new file mode 100644 index 0000000..839df41 Binary files /dev/null and b/src/assets/images/brazil.jpg differ diff --git a/src/assets/images/united-states.png b/src/assets/images/united-states.png new file mode 100644 index 0000000..36d775a Binary files /dev/null and b/src/assets/images/united-states.png differ diff --git a/src/components/Carousel/index.tsx b/src/components/Carousel/index.tsx index 63c1c19..160b062 100644 --- a/src/components/Carousel/index.tsx +++ b/src/components/Carousel/index.tsx @@ -1,10 +1,11 @@ import { Carousel as FlowbiteCarousel } from 'flowbite-react'; +import { memo } from 'react'; import { CarouselProps } from '../../@types'; import { carouselHeights } from '../../constants'; -import { CarouselButton } from '../CarouselButton'; +import { CarouselButton } from '../'; -export function Carousel(props: CarouselProps) { +function Carousel(props: CarouselProps) { const { children, type = 'banner', @@ -18,17 +19,30 @@ export function Carousel(props: CarouselProps) { const isBanner = type === 'banner'; const slideInterval = isBanner ? 3000 : 5000; + const rightControl: React.JSX.Element = !hasChildren ? ( + + ) : ( + + ); + const leftControl: React.JSX.Element = !hasChildren ? ( + + ) : ( + + ); + return (
} - leftControl={} + rightControl={rightControl} + leftControl={leftControl} > {children}
); } + +export default memo(Carousel); diff --git a/src/components/CarouselButton/index.tsx b/src/components/CarouselButton/index.tsx index 73f8f7d..e846252 100644 --- a/src/components/CarouselButton/index.tsx +++ b/src/components/CarouselButton/index.tsx @@ -1,11 +1,13 @@ +import { memo } from 'react'; + import { CarouselButtonProps, CarouselButtonType, IconProps, } from '../../@types'; -import { Icon } from '../Icon'; +import { Icon, Translator } from '../'; -export function CarouselButton(props: CarouselButtonProps) { +function CarouselButton(props: CarouselButtonProps) { const { type } = props; const isNext = type === 'next'; @@ -25,8 +27,14 @@ export function CarouselButton(props: CarouselButtonProps) { {icons[type]} - {isNext ? 'Próximo' : 'Anterior'} + + + ); } + +export default memo(CarouselButton); diff --git a/src/components/CarouselEvent/index.tsx b/src/components/CarouselEvent/index.tsx index ee9ef26..2e53fd0 100644 --- a/src/components/CarouselEvent/index.tsx +++ b/src/components/CarouselEvent/index.tsx @@ -1,23 +1,26 @@ import { Card } from 'flowbite-react'; +import React, { memo } from 'react'; import { CarouselEventProps } from '../../@types'; -import { Text } from '../Text'; +import { Text } from '../'; -export function CarouselEvent(props: CarouselEventProps) { +function CarouselEvent(props: CarouselEventProps) { const { description, image, title } = props; + const renderImage: React.JSX.Element = ( + {title} + ); + return (
renderImage} horizontal - renderImage={() => ( - {`Image - )} >
@@ -36,3 +39,5 @@ export function CarouselEvent(props: CarouselEventProps) {
); } + +export default memo(CarouselEvent); diff --git a/src/components/CarouselImage/index.tsx b/src/components/CarouselImage/index.tsx index 814f4ba..9773951 100644 --- a/src/components/CarouselImage/index.tsx +++ b/src/components/CarouselImage/index.tsx @@ -1,14 +1,17 @@ +import { memo } from 'react'; + import { CarouselImageProps } from '../../@types'; -export function CarouselImage(props: CarouselImageProps) { - const { id, uri } = props; +function CarouselImage(props: CarouselImageProps) { + const { name, uri } = props; return ( Imagem do Carousel ); } + +export default memo(CarouselImage); diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index dc8a47a..8f39ce7 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,12 +1,16 @@ import { Footer as FlowbiteFooter } from 'flowbite-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import packageJson from '../../../package.json'; import { IconProps } from '../../@types'; import { DEFAULT_EMAIL } from '../../config'; -import { sendMessage } from '../../utils'; -import { Icon, Text } from '../'; +import { buildWhatsAppUrl } from '../../utils'; +import { Icon, Text, Translator } from '../'; + +function Footer() { + const { t } = useTranslation(); -export function Footer() { const version = packageJson.version; const currentYear = new Date().getFullYear(); @@ -16,34 +20,39 @@ export function Footer() { }; function onSendWhatsAppMessage(): void { - window.open(sendMessage({ type: 'footer' })); + window.open(buildWhatsAppUrl({ message: t('footer.whatsAppMessage') })); } return (
- Todos os Direitos Reservados. + + + - Versão {version}. + + {version}. +
- Projeto de extensão desenvolvido na{' '} + {' '} - Univali + @@ -55,6 +64,7 @@ export function Footer() { className='rounded focus:outline-none focus:ring focus:ring-facebook focus:border-facebook' href='https://www.facebook.com/artelier.maisa/' target='_blank' + title={t('footer.facebook')} > @@ -75,6 +86,7 @@ export function Footer() { type='button' className='rounded focus:outline-none focus:ring focus:ring-whatsapp focus:border-whatring-whatsapp' onClick={onSendWhatsAppMessage} + title={t('footer.whatsapp')} > ); } + +export default memo(Footer); diff --git a/src/components/GenericButton/index.tsx b/src/components/GenericButton/index.tsx index 2badeeb..8375146 100644 --- a/src/components/GenericButton/index.tsx +++ b/src/components/GenericButton/index.tsx @@ -1,9 +1,10 @@ +import { memo } from 'react'; + import { Colors, GenericButtonProps, SpinnerColor } from '../../@types'; import { genericButtonHeights } from '../../constants'; -import { Spinner } from '../Spinner'; -import { Text } from '../Text'; +import { Spinner, Text } from '../'; -export function GenericButton(props: GenericButtonProps) { +function GenericButton(props: GenericButtonProps) { const { title, variant = 'primary', @@ -67,3 +68,5 @@ export function GenericButton(props: GenericButtonProps) { ); } + +export default memo(GenericButton); diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index d598223..b359d4e 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,14 +1,21 @@ import { Navbar } from 'flowbite-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { Link } from 'react-scroll'; -import { TextProps } from '../../@types'; -import { PRIMARY_LOGO } from '../../config'; +import { LanguageType, TextProps } from '../../@types'; +import { BRAZIL, PRIMARY_LOGO, UNITED_STATES } from '../../config'; import { useScrollTop, useUser } from '../../hooks'; -import { Text } from '../Text'; +import { Text, Translator } from '../'; -export function Header() { +function Header() { const { highlights } = useUser(); const { handleTo, to } = useScrollTop(); + const { i18n, t } = useTranslation(); + + async function handleChangeLanguage(language: LanguageType): Promise { + await i18n.changeLanguage(language); + } const commonTextProps: Omit = { type: 'semibold', @@ -26,7 +33,7 @@ export function Header() { Artelier Maisa Logo @@ -36,7 +43,7 @@ export function Header() { color={isProducts ? 'white' : 'background-color'} {...commonTextProps} > - Produtos + @@ -53,7 +60,7 @@ export function Header() { color={isAbout ? 'white' : 'background-color'} {...commonTextProps} > - Sobre a Maisa + @@ -70,20 +77,49 @@ export function Header() { color={isEvents ? 'white' : 'background-color'} {...commonTextProps} > - Divulgações + )} - handleTo(null)} - > - - Artelier by Maisa - - +
+
+ + + +
+ + handleTo(null)}> + + + + +
); } + +export default memo(Header); diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index e5c58ff..1802d96 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -12,11 +12,12 @@ import { WhatsappLogo, X, } from '@phosphor-icons/react'; +import { memo } from 'react'; import { IconProps } from '../../@types'; -import { iconColors, iconSizes } from '../../constants/components'; +import { iconColors, iconSizes } from '../../constants'; -export function Icon(props: IconProps) { +function Icon(props: IconProps) { const { variant, color = 'background-color', @@ -49,3 +50,5 @@ export function Icon(props: IconProps) { return icons[variant]; } + +export default memo(Icon); diff --git a/src/components/Metric/index.tsx b/src/components/Metric/index.tsx index d6ab10f..bc30ddc 100644 --- a/src/components/Metric/index.tsx +++ b/src/components/Metric/index.tsx @@ -1,10 +1,20 @@ +import React, { memo } from 'react'; + import { MetricProps } from '../../@types'; -import { metricsVariants } from '../../constants'; -import { Text } from '../Text'; +import { Text, Translator } from '../'; -export function Metric(props: MetricProps) { +function Metric(props: MetricProps) { const { value, variant = 'material' } = props; + const metricsVariants: Record< + Required['variant'], + React.JSX.Element + > = { + material: , + size: , + weight: , + }; + return (
@@ -33,3 +43,5 @@ export function Metric(props: MetricProps) {
); } + +export default memo(Metric); diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 8ae706a..b43a451 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,3 +1,4 @@ +import { memo } from 'react'; import ReactModal from 'react-modal'; import { ModalProps } from '../../@types'; @@ -10,7 +11,7 @@ import { WhatsAppButton, } from '../'; -export function Modal(props: ModalProps) { +function Modal(props: ModalProps) { const { isOpen, product, onClose } = props; const hasMetrics = !!product.size || !!product.weight || !!product.material; @@ -36,7 +37,7 @@ export function Modal(props: ModalProps) { {product?.images && product.images.map(image => ( - + ))} @@ -70,3 +71,5 @@ export function Modal(props: ModalProps) { ); } + +export default memo(Modal); diff --git a/src/components/ProductCard/index.tsx b/src/components/ProductCard/index.tsx index a593e08..7fe56b1 100644 --- a/src/components/ProductCard/index.tsx +++ b/src/components/ProductCard/index.tsx @@ -1,23 +1,27 @@ import { Card } from 'flowbite-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { ProductCardProps } from '../../@types'; -import { GenericButton } from '../GenericButton'; -import { Text } from '../Text'; +import { GenericButton, Text, Translator } from '../'; -export function ProductCard(props: ProductCardProps) { - const { id, name, description, price, image, onSeeMore } = props; +function ProductCard(props: ProductCardProps) { + const { name, description, price, image, onSeeMore } = props; + + const { t } = useTranslation(); + + const renderImage: React.JSX.Element = ( + {name} + ); return ( ( - {`Image - )} + renderImage={() => renderImage} > {name} @@ -28,10 +32,14 @@ export function ProductCard(props: ProductCardProps) {
- + - Por{' '} + {' '} {price} @@ -40,3 +48,5 @@ export function ProductCard(props: ProductCardProps) { ); } + +export default memo(ProductCard); diff --git a/src/components/SearchInput/index.tsx b/src/components/SearchInput/index.tsx index cad2317..07aab6d 100644 --- a/src/components/SearchInput/index.tsx +++ b/src/components/SearchInput/index.tsx @@ -1,21 +1,31 @@ -import { useState } from 'react'; +import { FormEvent, memo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { SearchInputCategoryProps, SearchInputProps } from '../../@types'; -import { GenericButton, Icon, Text } from '../'; +import { GenericButton, Icon, Text, Translator } from '../'; -export function SearchInput(props: SearchInputProps) { +function SearchInput(props: SearchInputProps) { const { categories, searchValue, onChange, onSelect, onSearch } = props; const [showDropdown, setShowDropdown] = useState(false); const [categorySelected, setCategorySelected] = useState(null); + const { t } = useTranslation(); + function onSelectCategory(category: SearchInputCategoryProps | null): void { setCategorySelected(category); + if (onSelect) onSelect(category); setShowDropdown(false); } + function onSubmit(event: FormEvent): void { + event.preventDefault(); + + if (onSearch) onSearch(); + } + const renderCategoryDropdown: React.JSX.Element = (
    @@ -37,7 +47,9 @@ export function SearchInput(props: SearchInputProps) { className='inline-flex w-full justify-center sm:justify-start cursor-pointer px-4 py-2 hover:transition-opacity hover:duration-300 hover:opacity-80' onClick={() => onSelectCategory(null)} > - Sem Categoria + + +
@@ -46,7 +58,10 @@ export function SearchInput(props: SearchInputProps) { return (
-
+
@@ -89,15 +109,25 @@ export function SearchInput(props: SearchInputProps) {
setShowDropdown(!showDropdown)} isHugWidth /> {showDropdown && renderCategoryDropdown} - +
); } + +export default memo(SearchInput); diff --git a/src/components/Spinner/index.tsx b/src/components/Spinner/index.tsx index 0de81af..9abac96 100644 --- a/src/components/Spinner/index.tsx +++ b/src/components/Spinner/index.tsx @@ -1,16 +1,22 @@ import { Spinner as FlowbiteSpinner } from 'flowbite-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { SpinnerProps } from '../../@types'; import { spinnerSizes } from '../../constants'; -export function Spinner(props: SpinnerProps) { +function Spinner(props: SpinnerProps) { const { size = 'medium', color = 'primary' } = props; + const { t } = useTranslation(); + return ( ); } + +export default memo(Spinner); diff --git a/src/components/Text/index.tsx b/src/components/Text/index.tsx index 126f9d8..6e46e63 100644 --- a/src/components/Text/index.tsx +++ b/src/components/Text/index.tsx @@ -1,3 +1,5 @@ +import { memo } from 'react'; + import { TextProps } from '../../@types'; import { textColors, @@ -7,7 +9,7 @@ import { textWeights, } from '../../constants'; -export function Text(props: TextProps) { +function Text(props: TextProps) { const { children, type = 'regular', @@ -35,3 +37,5 @@ export function Text(props: TextProps) {

); } + +export default memo(Text); diff --git a/src/components/Translator/index.tsx b/src/components/Translator/index.tsx new file mode 100644 index 0000000..f74c08b --- /dev/null +++ b/src/components/Translator/index.tsx @@ -0,0 +1,14 @@ +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { TranslatorProps } from '../../@types'; + +function Translator(props: TranslatorProps) { + const { t } = useTranslation(); + + const { path } = props; + + return t(path); +} + +export default memo(Translator); diff --git a/src/components/WhatsAppButton/index.tsx b/src/components/WhatsAppButton/index.tsx index f48a0bc..0fe1f6b 100644 --- a/src/components/WhatsAppButton/index.tsx +++ b/src/components/WhatsAppButton/index.tsx @@ -1,13 +1,23 @@ +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + import { WhatsAppButtonProps } from '../../@types'; import { DEFAULT_PHONE } from '../../config'; -import { sendMessage } from '../../utils'; -import { Icon, Text } from '../'; +import { buildWhatsAppUrl } from '../../utils'; +import { Icon, Text, Translator } from '../'; -export function WhatsAppButton(props: WhatsAppButtonProps) { +function WhatsAppButton(props: WhatsAppButtonProps) { const { product, phone = DEFAULT_PHONE } = props; + const { t } = useTranslation(); + function onSendWhatsAppMessage(): void { - window.open(sendMessage({ phone, product })); + window.open( + buildWhatsAppUrl({ + phone, + message: `${t('whatsAppButton.firstWhatsAppMessage')}${product}${t('whatsAppButton.secondWhatsAppMessage')}`, + }), + ); } return ( @@ -22,10 +32,12 @@ export function WhatsAppButton(props: WhatsAppButtonProps) { className='text-base md:text-xl' toCenter > - Entrar em Contanto via WhatsApp +
); } + +export default memo(WhatsAppButton); diff --git a/src/components/index.ts b/src/components/index.ts index 8be13f4..e51bc5f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,15 +1,35 @@ -export * from './Carousel'; -export * from './CarouselButton'; -export * from './CarouselEvent'; -export * from './CarouselImage'; -export * from './Footer'; -export * from './GenericButton'; -export * from './Header'; -export * from './Icon'; -export * from './Metric'; -export * from './Modal'; -export * from './ProductCard'; -export * from './SearchInput'; -export * from './Spinner'; -export * from './Text'; -export * from './WhatsAppButton'; +import Carousel from './Carousel'; +import CarouselButton from './CarouselButton'; +import CarouselEvent from './CarouselEvent'; +import CarouselImage from './CarouselImage'; +import Footer from './Footer'; +import GenericButton from './GenericButton'; +import Header from './Header'; +import Icon from './Icon'; +import Metric from './Metric'; +import Modal from './Modal'; +import ProductCard from './ProductCard'; +import SearchInput from './SearchInput'; +import Spinner from './Spinner'; +import Text from './Text'; +import Translator from './Translator'; +import WhatsAppButton from './WhatsAppButton'; + +export { + Carousel, + CarouselButton, + CarouselEvent, + CarouselImage, + Footer, + GenericButton, + Header, + Icon, + Metric, + Modal, + ProductCard, + SearchInput, + Spinner, + Text, + Translator, + WhatsAppButton, +}; diff --git a/src/config/images.ts b/src/config/images.ts index d4d20c1..79f1032 100644 --- a/src/config/images.ts +++ b/src/config/images.ts @@ -1,4 +1,6 @@ +import BRAZIL from './../assets/images/brazil.jpg'; import PRIMARY_LOGO from './../assets/images/primary-logo.jpg'; import SECONDARY_LOGO from './../assets/images/secondary-logo.jpg'; +import UNITED_STATES from './../assets/images/united-states.png'; -export { PRIMARY_LOGO, SECONDARY_LOGO }; +export { BRAZIL, PRIMARY_LOGO, SECONDARY_LOGO, UNITED_STATES }; diff --git a/src/constants/components/Metric.ts b/src/constants/components/Metric.ts deleted file mode 100644 index cd8f336..0000000 --- a/src/constants/components/Metric.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MetricProps } from '../../@types'; - -export const metricsVariants: Record['variant'], string> = - { - material: 'Material', - size: 'Tamanho', - weight: 'Peso', - }; diff --git a/src/constants/components/index.ts b/src/constants/components/index.ts index ef47a6d..68b3224 100644 --- a/src/constants/components/index.ts +++ b/src/constants/components/index.ts @@ -1,6 +1,5 @@ export * from './Carousel'; export * from './GenericButton'; export * from './Icon'; -export * from './Metric'; export * from './Spinner'; export * from './Text'; diff --git a/src/contexts/UserContext.tsx b/src/contexts/UserContext.tsx index a421146..5e7fcc1 100644 --- a/src/contexts/UserContext.tsx +++ b/src/contexts/UserContext.tsx @@ -1,11 +1,5 @@ -import { get, onValue, ref, remove } from 'firebase/database'; -import { - createContext, - PropsWithChildren, - useCallback, - useEffect, - useState, -} from 'react'; +import { onValue, ref, remove } from 'firebase/database'; +import { createContext, PropsWithChildren, useEffect, useState } from 'react'; import { toast } from 'sonner'; import { @@ -16,14 +10,13 @@ import { Product, UserContextProps, } from '../@types'; +import { Translator } from '../components'; import { categoryMapper } from '../helpers'; import { database } from '../services'; import { mapper } from '../utils'; -// TODO: Create type to UserContext in @types/contexts. export const UserContext = createContext({} as UserContextProps); -// TODO: Create the data user logic. Add all requests and states this context. export function UserProvider({ children }: Required) { const [isLoaded, setIsLoaded] = useState(false); const [highlights, setHighlights] = useState([]); @@ -37,85 +30,36 @@ export function UserProvider({ children }: Required) { return currentTime > highlight.removedAt; } - function handleGenericErrorToast(): void { - toast.error( - 'Falha ao buscar suas informações! Algo deu errado durante a busca de informações. Por favor, tente novamente. Se o problema persistir, entre em contato com o suporte técnico.', - { duration: 7500 }, - ); + function sortProducts(product: Product, productCompare: Product): number { + return productCompare.createdAt - product.createdAt; } - const handleGetAbout = useCallback(async () => { - const aboutRef = ref(database, 'about'); - const aboutSnapshot = await get(aboutRef); - - if (!aboutSnapshot.exists()) return handleGenericErrorToast(); - - const aboutFirebase = mapper(aboutSnapshot)[0]; - - if (!aboutFirebase) return handleGenericErrorToast(); - - setAbout(aboutFirebase); - }, []); - - const handleGetHighlights = useCallback(async () => { - const highlightsRef = ref(database, 'highlights'); - const highlightsSnapshot = await get(highlightsRef); - - if (!highlightsSnapshot.exists()) return setHighlights([]); - - const highlightsFirebase = mapper(highlightsSnapshot); - - if (!highlightsFirebase) return handleGenericErrorToast(); - - // TODO -> REMOVE HIGHLIGHTS IMAGES FROM FIREBASE STORAGE - highlightsFirebase.forEach(async highlight => { - if (isGreaterThanPeriodRemove(highlight)) { - const highlightRef = ref(database, `highlights/${highlight.id}`); - remove(highlightRef); - } + function handleGenericErrorToast(): void { + toast.error(, { + duration: 7500, }); - - const highlightsInPeriod = highlightsFirebase.filter( - highlight => !isGreaterThanPeriodRemove(highlight), - ); - - setHighlights(highlightsInPeriod); - }, []); - - const handleGetBanners = useCallback(async () => { - const bannersRef = ref(database, 'banners'); - const bannersSnapshot = await get(bannersRef); - - if (!bannersSnapshot.exists()) return handleGenericErrorToast(); - - const bannersFirebase = mapper(bannersSnapshot); - - if (!bannersFirebase) return handleGenericErrorToast(); - - setBanners(bannersFirebase); - }, []); - - function sortProducts(product: Product, productCompare: Product): number { - return productCompare.createdAt - product.createdAt; } - const fetchFirebase = useCallback(async () => { - await handleGetBanners(); - await handleGetHighlights(); - await handleGetAbout(); + useEffect(() => { + const bannersRef = ref(database, 'banners'); + const categoriesRef = ref(database, 'categories'); + const highlightsRef = ref(database, 'highlights'); + const aboutRef = ref(database, 'about'); - setIsLoaded(true); - }, [handleGetAbout, handleGetHighlights, handleGetBanners]); + const unsubscribeBanners = onValue(bannersRef, bannersSnapshot => { + if (!bannersSnapshot.exists()) { + setBanners([]); + return handleGenericErrorToast(); + } - useEffect(() => { - fetchFirebase(); - }, [fetchFirebase]); + const bannersFirebase = mapper(bannersSnapshot); + if (!bannersFirebase) return handleGenericErrorToast(); - useEffect(() => { - const categoriesRef = ref(database, 'categories'); + setBanners(bannersFirebase); + }); - const unsubscribe = onValue(categoriesRef, categoriesSnapshot => { - if (!categoriesSnapshot.val()) { + const unsubscribeCategories = onValue(categoriesRef, categoriesSnapshot => { + if (!categoriesSnapshot.exists()) { setProducts([]); setCategories([]); return; @@ -134,11 +78,53 @@ export function UserProvider({ children }: Required) { setCategories(categoriesMapped); }); + const unsubscribeHighlights = onValue(highlightsRef, highlightsSnapshot => { + if (!highlightsSnapshot.exists()) { + setHighlights([]); + return handleGenericErrorToast(); + } + + const highlightsFirebase = mapper(highlightsSnapshot); + if (!highlightsFirebase) return handleGenericErrorToast(); + + highlightsFirebase.forEach(async highlight => { + if (isGreaterThanPeriodRemove(highlight)) { + const highlightRef = ref(database, `highlights/${highlight.id}`); + remove(highlightRef); + } + }); + + const highlightsInPeriod = highlightsFirebase.filter( + highlight => !isGreaterThanPeriodRemove(highlight), + ); + + setHighlights(highlightsInPeriod); + }); + + const unsubscribeAbout = onValue(aboutRef, aboutSnapshot => { + if (!aboutSnapshot.exists()) return handleGenericErrorToast(); + + const aboutFirebase = mapper(aboutSnapshot)[0]; + if (!aboutFirebase) { + setAbout({} as About); + return handleGenericErrorToast(); + } + + setAbout(aboutFirebase); + }); + () => { - unsubscribe(); + unsubscribeBanners(); + unsubscribeCategories(); + unsubscribeHighlights(); + unsubscribeAbout(); }; }, []); + useEffect(() => { + if (banners.length > 0 && about?.id) setIsLoaded(true); + }, [banners, about]); + return ( ( - {} as Product, - ); + const [productSelected, setProductSelected] = useState(null); const { about, highlights, banners, products, isLoaded } = useUser(); + const { t } = useTranslation(); const navigate = useNavigate(); - function navigateToProductPage(): void { + function navigateToProductsPage(): void { navigate('/products'); } @@ -33,6 +34,9 @@ export function LandingPage() { const firstThreeProducts = [firstProduct, secondProduct, threeProduct].filter( product => product, ); + const productUsedByModal: ProductProps = productSelected + ? { ...productSelected, title: productSelected.name } + : ({} as ProductProps); return ( <> @@ -46,8 +50,8 @@ export function LandingPage() { {banners.map(banner => ( ))} @@ -59,7 +63,6 @@ export function LandingPage() { {firstThreeProducts.map(product => ( )} @@ -95,14 +98,14 @@ export function LandingPage() {
- Sobre a Maisa +
{`Foto @@ -129,7 +132,7 @@ export function LandingPage() { ) : (
- Preparando algo especial para você... + @@ -140,9 +143,9 @@ export function LandingPage() { setProductSelected({} as Product)} - product={{ ...productSelected, title: productSelected.name }} + isOpen={!!productSelected} + onClose={() => setProductSelected(null)} + product={productUsedByModal} /> ); diff --git a/src/pages/NotFound/index.tsx b/src/pages/NotFound/index.tsx index 54c7489..de2a761 100644 --- a/src/pages/NotFound/index.tsx +++ b/src/pages/NotFound/index.tsx @@ -1,37 +1,44 @@ +import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; -import { GenericButton, Text } from '../../components'; +import { GenericButton, Text, Translator } from '../../components'; import { PRIMARY_LOGO } from '../../config'; export function NotFound() { const navigate = useNavigate(); + const { t } = useTranslation(); - function goToLandingPage(): void { + function navigateToLandingPage(): void { navigate('/'); } return (
- Não se preocupe, temos muitos produtos incríveis esperando por você! - Volte para a página inicial ou use a barra de busca para encontrar o - que procura. + - +
); diff --git a/src/pages/Products/index.tsx b/src/pages/Products/index.tsx index 938213d..cda76d0 100644 --- a/src/pages/Products/index.tsx +++ b/src/pages/Products/index.tsx @@ -1,6 +1,11 @@ import { useCallback, useEffect, useState } from 'react'; -import { Category, Product, SearchInputCategoryProps } from '../../@types'; +import { + Category, + Product, + ProductProps, + SearchInputCategoryProps, +} from '../../@types'; import { Footer, Header, @@ -10,6 +15,7 @@ import { SearchInput, Spinner, Text, + Translator, } from '../../components'; import { useUser } from '../../hooks'; @@ -18,9 +24,7 @@ export function Products() { const [searchValue, setSearchValue] = useState(''); const [products, setProducts] = useState(productsFirebase); - const [productSelected, setProductSelected] = useState( - {} as Product, - ); + const [productSelected, setProductSelected] = useState(null); const [categorySelected, setCategorySelected] = useState( null, ); @@ -34,10 +38,10 @@ export function Products() { const productsSelected = searchValue.trim() ? allProductsOfTheCategory.filter(product => - searchValue + product.name .trim() .toLowerCase() - .includes(product.name.trim().toLowerCase()), + .includes(searchValue.trim().toLowerCase()), ) : allProductsOfTheCategory; @@ -48,13 +52,15 @@ export function Products() { category: SearchInputCategoryProps | null, ): void { if (!category) return setCategorySelected(null); - setCategorySelected(categories.find(({ id }) => id === category.id)!); + + const foundCategory = categories.find(({ id }) => id === category.id); + if (!foundCategory) return setCategorySelected(null); + setCategorySelected(foundCategory); } - useEffect(() => { - if (productsFirebase.length) setProducts(productsFirebase); - else setProducts([]); - }, [productsFirebase]); + const productUsedByModal: ProductProps = productSelected + ? { ...productSelected, title: productSelected.name } + : ({} as ProductProps); useEffect(() => { if (!searchValue) handleSearchProducts(); @@ -82,7 +88,6 @@ export function Products() { {products.map(product => ( - Não há produtos disponíveis + @@ -104,7 +109,7 @@ export function Products() { ) : (
- Preparando nossos lindos produtos para você... + @@ -115,9 +120,9 @@ export function Products() { setProductSelected({} as Product)} - product={{ ...productSelected, title: productSelected.name }} + isOpen={!!productSelected} + onClose={() => setProductSelected(null)} + product={productUsedByModal} /> ); diff --git a/src/utils/whatsapp.ts b/src/utils/whatsapp.ts index f8505bf..999dbff 100644 --- a/src/utils/whatsapp.ts +++ b/src/utils/whatsapp.ts @@ -1,16 +1,8 @@ -import { SendMessageProps } from '../@types'; +import { BuildWhatsAppUrlProps } from '../@types'; import { DEFAULT_PHONE } from '../config'; -export function sendMessage(props: SendMessageProps) { - const { type = 'whatsapp-button', phone = DEFAULT_PHONE, product } = props; +export function buildWhatsAppUrl(props: BuildWhatsAppUrlProps) { + const { message, phone = DEFAULT_PHONE } = props; - const productName = product ? `"${product}" ` : ''; - - const messages: Record['type'], string> = { - footer: - 'Olá, estava olhando o seu site e resolvi entrar em contato. Tenho interesse em seus produtos!', - 'whatsapp-button': `Olá, estou interresado(a) no produto ${productName}que achei no seu site, gostaria de conversar sobre ele.`, - }; - - return `https://wa.me/${phone}?text=${encodeURIComponent(messages[type])}`; + return `https://wa.me/${phone}?text=${encodeURIComponent(message)}`; }