From ea96a6977cae114a647ff1d6aa407ddad704a995 Mon Sep 17 00:00:00 2001 From: LanselonX <101521725+LanselonX@users.noreply.github.com> Date: Wed, 25 Oct 2023 05:36:16 +0300 Subject: [PATCH] feature: reusable modals (#121) * feat: added initial info-modal * fix: cleanup & useless styles * fix: fixed some styles, returned action modal * fix: changed the approach to using styles * fix: flex issue * fix: test version teameights-types * fix: types * fix: added type * fix: lib issues, reusability * fix: added new modal window * fix: badges and modals * fix: added logic for mobile user * fix: added new phone action modal * fix: added new phone action modal * fix: code issues * fix: changed folder location * fix: some styles * fix: added storybook for modal * fix: added storybook for modal * Revert "fix: added storybook for modal" This reverts commit c5e7773c4e5cf6d25ef83ddb866da5fa20a6f4d6. * fix: added for test new function * fix: added new modal for a test * fix: changed mock data * fix: changed mock data * fix: fixed new error * fix: previous issues and code * fix: namings * fix: namings * fix: added new modal that fixes freezes * feat: added image loader, fix issues with modals * feat: added flags & fixed issues --------- Co-authored-by: Nikita Mashchenko --- client/next.config.js | 18 + client/package.json | 5 +- client/src/app/page.tsx | 122 ++- .../assets/icons/arrows/arrow-right.tsx | 15 + .../src/shared/assets/icons/arrows/index.ts | 1 + .../shared/assets/icons/chat-circle-dots.tsx | 16 + client/src/shared/assets/icons/crown.tsx | 17 - .../shared/assets/icons/crowns/crown20.tsx | 31 + .../shared/assets/icons/crowns/crown28.tsx | 31 + .../shared/assets/icons/crowns/crown40.tsx | 24 + .../src/shared/assets/icons/crowns/index.ts | 3 + client/src/shared/assets/icons/index.ts | 6 +- client/src/shared/assets/icons/search.tsx | 1 + client/src/shared/assets/icons/user-plus.tsx | 39 + client/src/shared/constant/countries.ts | 998 ++++++++++++++++++ client/src/shared/constant/country-flags.ts | 256 +++++ client/src/shared/constant/index.tsx | 5 +- client/src/shared/fixtures/team.ts | 16 +- client/src/shared/fixtures/user.ts | 26 +- .../lib/utils/capitalize/capitalize.test.ts | 48 + .../shared/lib/utils/capitalize/capitalize.ts | 8 + .../shared/lib/utils/get-age/get-age.test.ts | 29 + .../src/shared/lib/utils/get-age/get-age.ts | 20 + .../get-country-flag/get-country-flag.test.ts | 23 + .../get-country-flag/get-country-flag.tsx | 11 + client/src/shared/lib/utils/index.ts | 3 + .../badge/badge-framework/badge-framework.tsx | 1 - .../badge/badge-language/badge-language.tsx | 2 +- client/src/shared/ui/button/button.tsx | 3 + client/src/shared/ui/drawer/drawer.tsx | 13 +- client/src/shared/ui/flex/flex.module.scss | 13 - client/src/shared/ui/flex/flex.tsx | 21 +- .../ui/image-loader/image-loader.module.scss | 14 + .../ui/image-loader/image-loader.stories.tsx | 58 + .../shared/ui/image-loader/image-loader.tsx | 77 ++ client/src/shared/ui/image-loader/index.ts | 1 + .../image-loader/ui/skeleton-loader/index.ts | 1 + .../skeleton-loader.module.scss | 3 + .../ui/skeleton-loader/skeleton-loader.tsx | 33 + client/src/shared/ui/index.ts | 2 +- client/src/shared/ui/modal/modal.module.scss | 60 +- client/src/shared/ui/modal/modal.tsx | 33 +- client/src/shared/ui/skeleton/index.ts | 1 - .../shared/ui/skeleton/skeleton.module.scss | 12 +- client/src/shared/ui/skeleton/skeleton.tsx | 4 - client/src/widgets/index.ts | 1 + .../modals/action-modal/action-modal.tsx | 24 + .../action-modal/desktop/desktop.module.scss | 4 + .../action-modal/desktop/desktop.stories.tsx | 44 + .../modals/action-modal/desktop/desktop.tsx | 22 + .../modals/action-modal/desktop/index.ts | 1 + .../interfaces/action-modal-interface.ts | 6 + .../modals/action-modal/interfaces/index.ts | 1 + .../modals/action-modal/phone/index.ts | 1 + .../action-modal/phone/phone.module.scss | 18 + .../action-modal/phone/phone.stories.tsx | 43 + .../modals/action-modal/phone/phone.tsx | 38 + client/src/widgets/modals/index.ts | 3 + .../team/desktop/desktop.module.scss | 3 + .../team/desktop/desktop.stories.tsx | 43 + .../info-modal/team/desktop/desktop.tsx | 121 +++ .../widgets/modals/info-modal/team/index.ts | 1 + .../info-modal/team/interfaces/index.ts | 1 + .../interfaces/info-modal-team-interface.ts | 9 + .../info-modal/team/phone/phone.module.scss | 8 + .../info-modal/team/phone/phone.stories.tsx | 43 + .../modals/info-modal/team/phone/phone.tsx | 150 +++ .../widgets/modals/info-modal/team/team.tsx | 31 + .../user/desktop/desktop.module.scss | 6 + .../user/desktop/desktop.stories.tsx | 32 + .../info-modal/user/desktop/desktop.tsx | 106 ++ .../widgets/modals/info-modal/user/index.ts | 1 + .../info-modal/user/interfaces/index.ts | 1 + .../interfaces/info-modal-user-interface.ts | 7 + .../info-modal/user/phone/phone.module.scss | 10 + .../info-modal/user/phone/phone.stories.tsx | 32 + .../modals/info-modal/user/phone/phone.tsx | 109 ++ .../widgets/modals/info-modal/user/user.tsx | 20 + client/yarn.lock | 103 +- 79 files changed, 2929 insertions(+), 238 deletions(-) create mode 100644 client/src/shared/assets/icons/arrows/arrow-right.tsx create mode 100644 client/src/shared/assets/icons/chat-circle-dots.tsx delete mode 100644 client/src/shared/assets/icons/crown.tsx create mode 100644 client/src/shared/assets/icons/crowns/crown20.tsx create mode 100644 client/src/shared/assets/icons/crowns/crown28.tsx create mode 100644 client/src/shared/assets/icons/crowns/crown40.tsx create mode 100644 client/src/shared/assets/icons/crowns/index.ts create mode 100644 client/src/shared/assets/icons/user-plus.tsx create mode 100644 client/src/shared/constant/countries.ts create mode 100644 client/src/shared/constant/country-flags.ts create mode 100644 client/src/shared/lib/utils/capitalize/capitalize.test.ts create mode 100644 client/src/shared/lib/utils/capitalize/capitalize.ts create mode 100644 client/src/shared/lib/utils/get-age/get-age.test.ts create mode 100644 client/src/shared/lib/utils/get-age/get-age.ts create mode 100644 client/src/shared/lib/utils/get-country-flag/get-country-flag.test.ts create mode 100644 client/src/shared/lib/utils/get-country-flag/get-country-flag.tsx create mode 100644 client/src/shared/ui/image-loader/image-loader.module.scss create mode 100644 client/src/shared/ui/image-loader/image-loader.stories.tsx create mode 100644 client/src/shared/ui/image-loader/image-loader.tsx create mode 100644 client/src/shared/ui/image-loader/index.ts create mode 100644 client/src/shared/ui/image-loader/ui/skeleton-loader/index.ts create mode 100644 client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.module.scss create mode 100644 client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.tsx delete mode 100644 client/src/shared/ui/skeleton/index.ts create mode 100644 client/src/widgets/index.ts create mode 100644 client/src/widgets/modals/action-modal/action-modal.tsx create mode 100644 client/src/widgets/modals/action-modal/desktop/desktop.module.scss create mode 100644 client/src/widgets/modals/action-modal/desktop/desktop.stories.tsx create mode 100644 client/src/widgets/modals/action-modal/desktop/desktop.tsx create mode 100644 client/src/widgets/modals/action-modal/desktop/index.ts create mode 100644 client/src/widgets/modals/action-modal/interfaces/action-modal-interface.ts create mode 100644 client/src/widgets/modals/action-modal/interfaces/index.ts create mode 100644 client/src/widgets/modals/action-modal/phone/index.ts create mode 100644 client/src/widgets/modals/action-modal/phone/phone.module.scss create mode 100644 client/src/widgets/modals/action-modal/phone/phone.stories.tsx create mode 100644 client/src/widgets/modals/action-modal/phone/phone.tsx create mode 100644 client/src/widgets/modals/index.ts create mode 100644 client/src/widgets/modals/info-modal/team/desktop/desktop.module.scss create mode 100644 client/src/widgets/modals/info-modal/team/desktop/desktop.stories.tsx create mode 100644 client/src/widgets/modals/info-modal/team/desktop/desktop.tsx create mode 100644 client/src/widgets/modals/info-modal/team/index.ts create mode 100644 client/src/widgets/modals/info-modal/team/interfaces/index.ts create mode 100644 client/src/widgets/modals/info-modal/team/interfaces/info-modal-team-interface.ts create mode 100644 client/src/widgets/modals/info-modal/team/phone/phone.module.scss create mode 100644 client/src/widgets/modals/info-modal/team/phone/phone.stories.tsx create mode 100644 client/src/widgets/modals/info-modal/team/phone/phone.tsx create mode 100644 client/src/widgets/modals/info-modal/team/team.tsx create mode 100644 client/src/widgets/modals/info-modal/user/desktop/desktop.module.scss create mode 100644 client/src/widgets/modals/info-modal/user/desktop/desktop.stories.tsx create mode 100644 client/src/widgets/modals/info-modal/user/desktop/desktop.tsx create mode 100644 client/src/widgets/modals/info-modal/user/index.ts create mode 100644 client/src/widgets/modals/info-modal/user/interfaces/index.ts create mode 100644 client/src/widgets/modals/info-modal/user/interfaces/info-modal-user-interface.ts create mode 100644 client/src/widgets/modals/info-modal/user/phone/phone.module.scss create mode 100644 client/src/widgets/modals/info-modal/user/phone/phone.stories.tsx create mode 100644 client/src/widgets/modals/info-modal/user/phone/phone.tsx create mode 100644 client/src/widgets/modals/info-modal/user/user.tsx diff --git a/client/next.config.js b/client/next.config.js index c38878afa..e8d2b2b16 100644 --- a/client/next.config.js +++ b/client/next.config.js @@ -2,6 +2,24 @@ const path = require('path'); module.exports = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 's3.amazonaws.com', + port: '', + pathname: '/my-bucket/**', + }, + { + protocol: 'https', + hostname: 'picsum.photos', + }, + { + protocol: 'https', + hostname: 'flagcdn.com', + }, + ], + }, sassOptions: { includePaths: [path.join(__dirname, 'styles')], }, diff --git a/client/package.json b/client/package.json index 5bb26d981..371636132 100644 --- a/client/package.json +++ b/client/package.json @@ -25,7 +25,7 @@ "@react-oauth/google": "^0.11.1", "@storybook/addon-styling": "^1.3.6", "@tanstack/react-query": "beta", - "@teameights/types": "^1.1.14", + "@teameights/types": "^1.1.17", "@types/lodash.debounce": "^4.0.7", "@types/node": "20.4.8", "@types/react": "18.2.18", @@ -38,11 +38,12 @@ "lodash.debounce": "^4.0.8", "next": "13.4.12", "react": "18.2.0", + "react-content-loader": "^6.2.1", "react-dom": "18.2.0", "react-hook-form": "^7.45.4", - "react-modal": "^3.16.1", "react-modern-drawer": "^1.2.2", "react-particles": "^2.12.2", + "react-responsive-modal": "^6.4.2", "react-select": "^5.7.4", "react-tooltip": "^5.21.3", "sass": "^1.64.2", diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx index f78e6a781..61bcba71e 100644 --- a/client/src/app/page.tsx +++ b/client/src/app/page.tsx @@ -1,18 +1,43 @@ 'use client'; - -import { Flex, Typography, Skeleton, Button, Drawer } from '@/shared/ui'; -import { useGetScreenWidth } from '@/shared/lib'; -import { Crown } from '@/shared/assets'; -import { IUserRequest } from '@teameights/types'; -import { NewtonsCradle, RaceBy, Ring } from '@uiball/loaders'; -import { toast } from 'sonner'; +import { Typography, Button } from '@/shared/ui'; import { useState } from 'react'; -import { SelectAutocomplete } from '@/shared/ui/select/ui/select-autocomplete/select-autocomplete'; +import { ActionModal } from '@/widgets/modals'; +import { userResponseFixture } from '@/shared/fixtures/user'; +import { teamFixture } from '@/shared/fixtures/team'; +import { UserInfoModal } from '@/widgets/modals/info-modal/user'; +import { TeamInfoModal } from '@/widgets/modals/info-modal/team/team'; export default function Home() { - const width = useGetScreenWidth(); - const user: IUserRequest = { username: 'nmashchenko' }; - const [open, setOpen] = useState(false); + const [isOpenFirstModal, setIsOpenFirstModal] = useState(false); + const [isOpenThirdModal, setIsOpenThirdModal] = useState(false); + const [openModal, setOpenModal] = useState(false); + + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + + const openFirstModal = () => { + setIsOpenFirstModal(true); + }; + + const closeFirstModal = () => { + setIsOpenFirstModal(false); + }; + + const openThirdModal = () => { + setIsOpenThirdModal(true); + }; + + const closeThirdModal = () => { + setIsOpenThirdModal(false); + }; + + const handleJoin = () => { + console.log('Join button clicked'); + }; return ( <> @@ -20,47 +45,54 @@ export default function Home() { We are working hard to deliver teameights on NextJS/TS soon! -
The screen width is: {width}
- - Hello, {user.username}! + Hello, {userResponseFixture.username}! Get to login - - - - - - - - - - - - - - - - +
+ + + + + +
- - setOpen(false)}> - - +
+ + +
- +
+ + +
); } diff --git a/client/src/shared/assets/icons/arrows/arrow-right.tsx b/client/src/shared/assets/icons/arrows/arrow-right.tsx new file mode 100644 index 000000000..b549b4e87 --- /dev/null +++ b/client/src/shared/assets/icons/arrows/arrow-right.tsx @@ -0,0 +1,15 @@ +import { FC, SVGProps } from 'react'; +export const ArrowRight: FC> = props => { + return ( + + + + ); +}; diff --git a/client/src/shared/assets/icons/arrows/index.ts b/client/src/shared/assets/icons/arrows/index.ts index 981a25a06..477242686 100644 --- a/client/src/shared/assets/icons/arrows/index.ts +++ b/client/src/shared/assets/icons/arrows/index.ts @@ -1 +1,2 @@ export { ArrowLeft } from './arrow-left'; +export { ArrowRight } from './arrow-right'; diff --git a/client/src/shared/assets/icons/chat-circle-dots.tsx b/client/src/shared/assets/icons/chat-circle-dots.tsx new file mode 100644 index 000000000..d49632ca2 --- /dev/null +++ b/client/src/shared/assets/icons/chat-circle-dots.tsx @@ -0,0 +1,16 @@ +import { FC, SVGProps } from 'react'; + +export const ChatCircleDots: FC> = props => { + return ( + + + + ); +}; diff --git a/client/src/shared/assets/icons/crown.tsx b/client/src/shared/assets/icons/crown.tsx deleted file mode 100644 index f8f563b8b..000000000 --- a/client/src/shared/assets/icons/crown.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FC, SVGProps } from 'react'; -export const Crown: FC> = props => { - return ( - - - - - ); -}; diff --git a/client/src/shared/assets/icons/crowns/crown20.tsx b/client/src/shared/assets/icons/crowns/crown20.tsx new file mode 100644 index 000000000..3192df5c2 --- /dev/null +++ b/client/src/shared/assets/icons/crowns/crown20.tsx @@ -0,0 +1,31 @@ +import { FC, SVGProps } from 'react'; +export const Crown20: FC> = props => { + return ( + + + + + + + + + + + + ); +}; diff --git a/client/src/shared/assets/icons/crowns/crown28.tsx b/client/src/shared/assets/icons/crowns/crown28.tsx new file mode 100644 index 000000000..4872da281 --- /dev/null +++ b/client/src/shared/assets/icons/crowns/crown28.tsx @@ -0,0 +1,31 @@ +import { FC, SVGProps } from 'react'; +export const Crown28: FC> = props => { + return ( + + + + + + + + + + + + ); +}; diff --git a/client/src/shared/assets/icons/crowns/crown40.tsx b/client/src/shared/assets/icons/crowns/crown40.tsx new file mode 100644 index 000000000..7e06ede04 --- /dev/null +++ b/client/src/shared/assets/icons/crowns/crown40.tsx @@ -0,0 +1,24 @@ +import { FC, SVGProps } from 'react'; +export const Crown40: FC> = props => { + return ( + + + + + ); +}; diff --git a/client/src/shared/assets/icons/crowns/index.ts b/client/src/shared/assets/icons/crowns/index.ts new file mode 100644 index 000000000..898ea89cc --- /dev/null +++ b/client/src/shared/assets/icons/crowns/index.ts @@ -0,0 +1,3 @@ +export { Crown40 } from './crown40'; +export { Crown28 } from './crown28'; +export { Crown20 } from './crown20'; diff --git a/client/src/shared/assets/icons/index.ts b/client/src/shared/assets/icons/index.ts index 6a07d9613..cad7568d7 100644 --- a/client/src/shared/assets/icons/index.ts +++ b/client/src/shared/assets/icons/index.ts @@ -2,15 +2,17 @@ export { Check } from './check'; export { Cross } from './cross'; export { Eye } from './eye'; export { EyeClosed } from './eye-closed'; +export { Crown20, Crown28, Crown40 } from './crowns'; export { Link } from './link'; export { Plus } from './plus'; export { Question } from './question'; export { WarningCircle } from './warning-circle'; export { X } from './x'; export { Cookie } from './cookie'; -export { Crown } from './crown'; export { Search } from './search'; -export { ArrowLeft } from './arrows'; +export { UserPlus } from './user-plus'; +export { ChatCircleDots } from './chat-circle-dots'; +export { ArrowLeft, ArrowRight } from './arrows'; export { CaretUp, CaretDown } from './caret'; export { LogoBig, LogoSmall } from './logo'; export { Google, Github } from './socials'; diff --git a/client/src/shared/assets/icons/search.tsx b/client/src/shared/assets/icons/search.tsx index 478fdb3c1..636bfde22 100644 --- a/client/src/shared/assets/icons/search.tsx +++ b/client/src/shared/assets/icons/search.tsx @@ -1,4 +1,5 @@ import { FC, SVGProps } from 'react'; + export const Search: FC> = props => { return ( > = props => { + return ( + + + + + + + ); +}; diff --git a/client/src/shared/constant/countries.ts b/client/src/shared/constant/countries.ts new file mode 100644 index 000000000..d1a8b7a75 --- /dev/null +++ b/client/src/shared/constant/countries.ts @@ -0,0 +1,998 @@ +export const countries = [ + { + label: 'Afghanistan', + value: 'afghanistan', + }, + { + label: 'Aland Islands', + value: 'alandislands', + }, + { + label: 'Albania', + value: 'albania', + }, + { + label: 'Algeria', + value: 'algeria', + }, + { + label: 'American Samoa', + value: 'americansamoa', + }, + { + label: 'Andorra', + value: 'andorra', + }, + { + label: 'Angola', + value: 'angola', + }, + { + label: 'Anguilla', + value: 'anguilla', + }, + { + label: 'Antarctica', + value: 'antarctica', + }, + { + label: 'Antigua and Barbuda', + value: 'antiguaandbarbuda', + }, + { + label: 'Argentina', + value: 'argentina', + }, + { + label: 'Armenia', + value: 'armenia', + }, + { + label: 'Aruba', + value: 'aruba', + }, + { + label: 'Australia', + value: 'australia', + }, + { + label: 'Austria', + value: 'austria', + }, + { + label: 'Azerbaijan', + value: 'azerbaijan', + }, + { + label: 'Bahamas', + value: 'bahamas', + }, + { + label: 'Bahrain', + value: 'bahrain', + }, + { + label: 'Bangladesh', + value: 'bangladesh', + }, + { + label: 'Barbados', + value: 'barbados', + }, + { + label: 'Belarus', + value: 'belarus', + }, + { + label: 'Belgium', + value: 'belgium', + }, + { + label: 'Belize', + value: 'belize', + }, + { + label: 'Benin', + value: 'benin', + }, + { + label: 'Bermuda', + value: 'bermuda', + }, + { + label: 'Bhutan', + value: 'bhutan', + }, + { + label: 'Bolivia', + value: 'bolivia', + }, + { + label: 'Bosnia and Herzegovina', + value: 'bosniaandherzegovina', + }, + { + label: 'Botswana', + value: 'botswana', + }, + { + label: 'Bouvet Island', + value: 'bouvetisland', + }, + { + label: 'Brazil', + value: 'brazil', + }, + { + label: 'British Indian Ocean Territory', + value: 'britishindianoceanterritory', + }, + { + label: 'British Virgin Islands', + value: 'britishvirginislands', + }, + { + label: 'Brunei', + value: 'brunei', + }, + { + label: 'Bulgaria', + value: 'bulgaria', + }, + { + label: 'Burkina Faso', + value: 'burkinafaso', + }, + { + label: 'Burundi', + value: 'burundi', + }, + { + label: 'Cambodia', + value: 'cambodia', + }, + { + label: 'Cameroon', + value: 'cameroon', + }, + { + label: 'Canada', + value: 'canada', + }, + { + label: 'Cape Verde', + value: 'capeverde', + }, + { + label: 'Caribbean Netherlands', + value: 'caribbeannetherlands', + }, + { + label: 'Cayman Islands', + value: 'caymanislands', + }, + { + label: 'Central African Republic', + value: 'centralafricanrepublic', + }, + { + label: 'Chad', + value: 'chad', + }, + { + label: 'Chile', + value: 'chile', + }, + { + label: 'China', + value: 'china', + }, + { + label: 'Christmas Island', + value: 'christmasisland', + }, + { + label: 'Cocos (Keeling) Islands', + value: 'cocos(keeling)islands', + }, + { + label: 'Colombia', + value: 'colombia', + }, + { + label: 'Comoros', + value: 'comoros', + }, + { + label: 'Cook Islands', + value: 'cookislands', + }, + { + label: 'Costa Rica', + value: 'costarica', + }, + { + label: 'Croatia', + value: 'croatia', + }, + { + label: 'Cuba', + value: 'cuba', + }, + { + label: 'Curacao', + value: 'curacao', + }, + { + label: 'Cyprus', + value: 'cyprus', + }, + { + label: 'Czechia', + value: 'czechia', + }, + { + label: 'DR Congo', + value: 'drcongo', + }, + { + label: 'Denmark', + value: 'denmark', + }, + { + label: 'Djibouti', + value: 'djibouti', + }, + { + label: 'Dominica', + value: 'dominica', + }, + { + label: 'Dominican Republic', + value: 'dominicanrepublic', + }, + { + label: 'Ecuador', + value: 'ecuador', + }, + { + label: 'Egypt', + value: 'egypt', + }, + { + label: 'El Salvador', + value: 'elsalvador', + }, + { + label: 'Equatorial Guinea', + value: 'equatorialguinea', + }, + { + label: 'Eritrea', + value: 'eritrea', + }, + { + label: 'Estonia', + value: 'estonia', + }, + { + label: 'Eswatini', + value: 'eswatini', + }, + { + label: 'Ethiopia', + value: 'ethiopia', + }, + { + label: 'Falkland Islands', + value: 'falklandislands', + }, + { + label: 'Faroe Islands', + value: 'faroeislands', + }, + { + label: 'Fiji', + value: 'fiji', + }, + { + label: 'Finland', + value: 'finland', + }, + { + label: 'France', + value: 'france', + }, + { + label: 'French Guiana', + value: 'frenchguiana', + }, + { + label: 'French Polynesia', + value: 'frenchpolynesia', + }, + { + label: 'French Southern and Antarctic Lands', + value: 'frenchsouthernandantarcticlands', + }, + { + label: 'Gabon', + value: 'gabon', + }, + { + label: 'Gambia', + value: 'gambia', + }, + { + label: 'Georgia', + value: 'georgia', + }, + { + label: 'Germany', + value: 'germany', + }, + { + label: 'Ghana', + value: 'ghana', + }, + { + label: 'Gibraltar', + value: 'gibraltar', + }, + { + label: 'Greece', + value: 'greece', + }, + { + label: 'Greenland', + value: 'greenland', + }, + { + label: 'Grenada', + value: 'grenada', + }, + { + label: 'Guadeloupe', + value: 'guadeloupe', + }, + { + label: 'Guam', + value: 'guam', + }, + { + label: 'Guatemala', + value: 'guatemala', + }, + { + label: 'Guernsey', + value: 'guernsey', + }, + { + label: 'Guinea', + value: 'guinea', + }, + { + label: 'Guinea-Bissau', + value: 'guinea-bissau', + }, + { + label: 'Guyana', + value: 'guyana', + }, + { + label: 'Haiti', + value: 'haiti', + }, + { + label: 'Heard Island and McDonald Islands', + value: 'heardislandandmcdonaldislands', + }, + { + label: 'Honduras', + value: 'honduras', + }, + { + label: 'Hong Kong', + value: 'hongkong', + }, + { + label: 'Hungary', + value: 'hungary', + }, + { + label: 'Iceland', + value: 'iceland', + }, + { + label: 'India', + value: 'india', + }, + { + label: 'Indonesia', + value: 'indonesia', + }, + { + label: 'Iran', + value: 'iran', + }, + { + label: 'Iraq', + value: 'iraq', + }, + { + label: 'Ireland', + value: 'ireland', + }, + { + label: 'Isle of Man', + value: 'isleofman', + }, + { + label: 'Israel', + value: 'israel', + }, + { + label: 'Italy', + value: 'italy', + }, + { + label: 'Ivory Coast', + value: 'ivorycoast', + }, + { + label: 'Jamaica', + value: 'jamaica', + }, + { + label: 'Japan', + value: 'japan', + }, + { + label: 'Jersey', + value: 'jersey', + }, + { + label: 'Jordan', + value: 'jordan', + }, + { + label: 'Kazakhstan', + value: 'kazakhstan', + }, + { + label: 'Kenya', + value: 'kenya', + }, + { + label: 'Kiribati', + value: 'kiribati', + }, + { + label: 'Kosovo', + value: 'kosovo', + }, + { + label: 'Kuwait', + value: 'kuwait', + }, + { + label: 'Kyrgyzstan', + value: 'kyrgyzstan', + }, + { + label: 'Laos', + value: 'laos', + }, + { + label: 'Latvia', + value: 'latvia', + }, + { + label: 'Lebanon', + value: 'lebanon', + }, + { + label: 'Lesotho', + value: 'lesotho', + }, + { + label: 'Liberia', + value: 'liberia', + }, + { + label: 'Libya', + value: 'libya', + }, + { + label: 'Liechtenstein', + value: 'liechtenstein', + }, + { + label: 'Lithuania', + value: 'lithuania', + }, + { + label: 'Luxembourg', + value: 'luxembourg', + }, + { + label: 'Macau', + value: 'macau', + }, + { + label: 'Madagascar', + value: 'madagascar', + }, + { + label: 'Malawi', + value: 'malawi', + }, + { + label: 'Malaysia', + value: 'malaysia', + }, + { + label: 'Maldives', + value: 'maldives', + }, + { + label: 'Mali', + value: 'mali', + }, + { + label: 'Malta', + value: 'malta', + }, + { + label: 'Marshall Islands', + value: 'marshallislands', + }, + { + label: 'Martinique', + value: 'martinique', + }, + { + label: 'Mauritania', + value: 'mauritania', + }, + { + label: 'Mauritius', + value: 'mauritius', + }, + { + label: 'Mayotte', + value: 'mayotte', + }, + { + label: 'Mexico', + value: 'mexico', + }, + { + label: 'Micronesia', + value: 'micronesia', + }, + { + label: 'Moldova', + value: 'moldova', + }, + { + label: 'Monaco', + value: 'monaco', + }, + { + label: 'Mongolia', + value: 'mongolia', + }, + { + label: 'Montenegro', + value: 'montenegro', + }, + { + label: 'Montserrat', + value: 'montserrat', + }, + { + label: 'Morocco', + value: 'morocco', + }, + { + label: 'Mozambique', + value: 'mozambique', + }, + { + label: 'Myanmar', + value: 'myanmar', + }, + { + label: 'Namibia', + value: 'namibia', + }, + { + label: 'Nauru', + value: 'nauru', + }, + { + label: 'Nepal', + value: 'nepal', + }, + { + label: 'Netherlands', + value: 'netherlands', + }, + { + label: 'New Caledonia', + value: 'newcaledonia', + }, + { + label: 'New Zealand', + value: 'newzealand', + }, + { + label: 'Nicaragua', + value: 'nicaragua', + }, + { + label: 'Niger', + value: 'niger', + }, + { + label: 'Nigeria', + value: 'nigeria', + }, + { + label: 'Niue', + value: 'niue', + }, + { + label: 'Norfolk Island', + value: 'norfolkisland', + }, + { + label: 'North Korea', + value: 'northkorea', + }, + { + label: 'North Macedonia', + value: 'northmacedonia', + }, + { + label: 'Northern Mariana Islands', + value: 'northernmarianaislands', + }, + { + label: 'Norway', + value: 'norway', + }, + { + label: 'Oman', + value: 'oman', + }, + { + label: 'Pakistan', + value: 'pakistan', + }, + { + label: 'Palau', + value: 'palau', + }, + { + label: 'Palestine', + value: 'palestine', + }, + { + label: 'Panama', + value: 'panama', + }, + { + label: 'Papua New Guinea', + value: 'papuanewguinea', + }, + { + label: 'Paraguay', + value: 'paraguay', + }, + { + label: 'Peru', + value: 'peru', + }, + { + label: 'Philippines', + value: 'philippines', + }, + { + label: 'Pitcairn Islands', + value: 'pitcairnislands', + }, + { + label: 'Poland', + value: 'poland', + }, + { + label: 'Portugal', + value: 'portugal', + }, + { + label: 'Puerto Rico', + value: 'puertorico', + }, + { + label: 'Qatar', + value: 'qatar', + }, + { + label: 'Republic of the Congo', + value: 'republicofthecongo', + }, + { + label: 'Reunion', + value: 'reunion', + }, + { + label: 'Romania', + value: 'romania', + }, + { + label: 'Russia', + value: 'russia', + }, + { + label: 'Rwanda', + value: 'rwanda', + }, + { + label: 'Saint Barthelemy', + value: 'saintbarthelemy', + }, + { + label: 'Saint Helena, Ascension and Tristan da Cunha', + value: 'sainthelena,ascensionandtristandacunha', + }, + { + label: 'Saint Kitts and Nevis', + value: 'saintkittsandnevis', + }, + { + label: 'Saint Lucia', + value: 'saintlucia', + }, + { + label: 'Saint Martin', + value: 'saintmartin', + }, + { + label: 'Saint Pierre and Miquelon', + value: 'saintpierreandmiquelon', + }, + { + label: 'Saint Vincent and the Grenadines', + value: 'saintvincentandthegrenadines', + }, + { + label: 'Samoa', + value: 'samoa', + }, + { + label: 'San Marino', + value: 'sanmarino', + }, + { + label: 'Saudi Arabia', + value: 'saudiarabia', + }, + { + label: 'Senegal', + value: 'senegal', + }, + { + label: 'Serbia', + value: 'serbia', + }, + { + label: 'Seychelles', + value: 'seychelles', + }, + { + label: 'Sierra Leone', + value: 'sierraleone', + }, + { + label: 'Singapore', + value: 'singapore', + }, + { + label: 'Sint Maarten', + value: 'sintmaarten', + }, + { + label: 'Slovakia', + value: 'slovakia', + }, + { + label: 'Slovenia', + value: 'slovenia', + }, + { + label: 'Solomon Islands', + value: 'solomonislands', + }, + { + label: 'Somalia', + value: 'somalia', + }, + { + label: 'South Africa', + value: 'southafrica', + }, + { + label: 'South Georgia', + value: 'southgeorgia', + }, + { + label: 'South Korea', + value: 'southkorea', + }, + { + label: 'South Sudan', + value: 'southsudan', + }, + { + label: 'Spain', + value: 'spain', + }, + { + label: 'Sri Lanka', + value: 'srilanka', + }, + { + label: 'Sudan', + value: 'sudan', + }, + { + label: 'Suriname', + value: 'suriname', + }, + { + label: 'Svalbard and Jan Mayen', + value: 'svalbardandjanmayen', + }, + { + label: 'Sweden', + value: 'sweden', + }, + { + label: 'Switzerland', + value: 'switzerland', + }, + { + label: 'Syria', + value: 'syria', + }, + { + label: 'Taiwan', + value: 'taiwan', + }, + { + label: 'Tajikistan', + value: 'tajikistan', + }, + { + label: 'Tanzania', + value: 'tanzania', + }, + { + label: 'Thailand', + value: 'thailand', + }, + { + label: 'Timor-Leste', + value: 'timor-leste', + }, + { + label: 'Togo', + value: 'togo', + }, + { + label: 'Tokelau', + value: 'tokelau', + }, + { + label: 'Tonga', + value: 'tonga', + }, + { + label: 'Trinidad and Tobago', + value: 'trinidadandtobago', + }, + { + label: 'Tunisia', + value: 'tunisia', + }, + { + label: 'Turkey', + value: 'turkey', + }, + { + label: 'Turkmenistan', + value: 'turkmenistan', + }, + { + label: 'Turks and Caicos Islands', + value: 'turksandcaicosislands', + }, + { + label: 'Tuvalu', + value: 'tuvalu', + }, + { + label: 'Uganda', + value: 'uganda', + }, + { + label: 'Ukraine', + value: 'ukraine', + }, + { + label: 'United Arab Emirates', + value: 'unitedarabemirates', + }, + { + label: 'United Kingdom', + value: 'unitedkingdom', + }, + { + label: 'United States', + value: 'unitedstates', + }, + { + label: 'United States Minor Outlying Islands', + value: 'unitedstatesminoroutlyingislands', + }, + { + label: 'United States Virgin Islands', + value: 'unitedstatesvirginislands', + }, + { + label: 'Uruguay', + value: 'uruguay', + }, + { + label: 'Uzbekistan', + value: 'uzbekistan', + }, + { + label: 'Vanuatu', + value: 'vanuatu', + }, + { + label: 'Vatican City', + value: 'vaticancity', + }, + { + label: 'Venezuela', + value: 'venezuela', + }, + { + label: 'Vietnam', + value: 'vietnam', + }, + { + label: 'Wallis and Futuna', + value: 'wallisandfutuna', + }, + { + label: 'Western Sahara', + value: 'westernsahara', + }, + { + label: 'Yemen', + value: 'yemen', + }, + { + label: 'Zambia', + value: 'zambia', + }, + { + label: 'Zimbabwe', + value: 'zimbabwe', + }, +]; diff --git a/client/src/shared/constant/country-flags.ts b/client/src/shared/constant/country-flags.ts new file mode 100644 index 000000000..d414e8c5e --- /dev/null +++ b/client/src/shared/constant/country-flags.ts @@ -0,0 +1,256 @@ +interface CountryFlags { + [key: string]: string; +} + +export const countryFlags: CountryFlags = { + Afghanistan: + 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_the_Taliban.svg/320px-Flag_of_the_Taliban.svg.png', + 'Aland Islands': 'https://flagcdn.com/w320/ax.png', + Albania: 'https://flagcdn.com/w320/al.png', + Algeria: 'https://flagcdn.com/w320/dz.png', + 'American Samoa': 'https://flagcdn.com/w320/as.png', + Andorra: 'https://flagcdn.com/w320/ad.png', + Angola: 'https://flagcdn.com/w320/ao.png', + Anguilla: 'https://flagcdn.com/w320/ai.png', + Antarctica: 'https://flagcdn.com/w320/aq.png', + 'Antigua and Barbuda': 'https://flagcdn.com/w320/ag.png', + Argentina: 'https://flagcdn.com/w320/ar.png', + Armenia: 'https://flagcdn.com/w320/am.png', + Aruba: 'https://flagcdn.com/w320/aw.png', + Australia: 'https://flagcdn.com/w320/au.png', + Austria: 'https://flagcdn.com/w320/at.png', + Azerbaijan: 'https://flagcdn.com/w320/az.png', + Bahamas: 'https://flagcdn.com/w320/bs.png', + Bahrain: 'https://flagcdn.com/w320/bh.png', + Bangladesh: 'https://flagcdn.com/w320/bd.png', + Barbados: 'https://flagcdn.com/w320/bb.png', + Belarus: 'https://flagcdn.com/w320/by.png', + Belgium: 'https://flagcdn.com/w320/be.png', + Belize: 'https://flagcdn.com/w320/bz.png', + Benin: 'https://flagcdn.com/w320/bj.png', + Bermuda: 'https://flagcdn.com/w320/bm.png', + Bhutan: 'https://flagcdn.com/w320/bt.png', + Bolivia: 'https://flagcdn.com/w320/bo.png', + 'Bosnia and Herzegovina': 'https://flagcdn.com/w320/ba.png', + Botswana: 'https://flagcdn.com/w320/bw.png', + 'Bouvet Island': 'https://flagcdn.com/w320/bv.png', + Brazil: 'https://flagcdn.com/w320/br.png', + 'British Indian Ocean Territory': 'https://flagcdn.com/w320/io.png', + 'British Virgin Islands': 'https://flagcdn.com/w320/vg.png', + Brunei: 'https://flagcdn.com/w320/bn.png', + Bulgaria: 'https://flagcdn.com/w320/bg.png', + 'Burkina Faso': 'https://flagcdn.com/w320/bf.png', + Burundi: 'https://flagcdn.com/w320/bi.png', + Cambodia: 'https://flagcdn.com/w320/kh.png', + Cameroon: 'https://flagcdn.com/w320/cm.png', + Canada: 'https://flagcdn.com/w320/ca.png', + 'Cape Verde': 'https://flagcdn.com/w320/cv.png', + 'Caribbean Netherlands': 'https://flagcdn.com/w320/bq.png', + 'Cayman Islands': 'https://flagcdn.com/w320/ky.png', + 'Central African Republic': 'https://flagcdn.com/w320/cf.png', + Chad: 'https://flagcdn.com/w320/td.png', + Chile: 'https://flagcdn.com/w320/cl.png', + China: 'https://flagcdn.com/w320/cn.png', + 'Christmas Island': 'https://flagcdn.com/w320/cx.png', + 'Cocos (Keeling) Islands': 'https://flagcdn.com/w320/cc.png', + Colombia: 'https://flagcdn.com/w320/co.png', + Comoros: 'https://flagcdn.com/w320/km.png', + 'Cook Islands': 'https://flagcdn.com/w320/ck.png', + 'Costa Rica': 'https://flagcdn.com/w320/cr.png', + Croatia: 'https://flagcdn.com/w320/hr.png', + Cuba: 'https://flagcdn.com/w320/cu.png', + Curacao: 'https://flagcdn.com/w320/cw.png', + Cyprus: 'https://flagcdn.com/w320/cy.png', + Czechia: 'https://flagcdn.com/w320/cz.png', + 'DR Congo': 'https://flagcdn.com/w320/cd.png', + Denmark: 'https://flagcdn.com/w320/dk.png', + Djibouti: 'https://flagcdn.com/w320/dj.png', + Dominica: 'https://flagcdn.com/w320/dm.png', + 'Dominican Republic': 'https://flagcdn.com/w320/do.png', + Ecuador: 'https://flagcdn.com/w320/ec.png', + Egypt: 'https://flagcdn.com/w320/eg.png', + 'El Salvador': 'https://flagcdn.com/w320/sv.png', + 'Equatorial Guinea': 'https://flagcdn.com/w320/gq.png', + Eritrea: 'https://flagcdn.com/w320/er.png', + Estonia: 'https://flagcdn.com/w320/ee.png', + Eswatini: 'https://flagcdn.com/w320/sz.png', + Ethiopia: 'https://flagcdn.com/w320/et.png', + 'Falkland Islands': 'https://flagcdn.com/w320/fk.png', + 'Faroe Islands': 'https://flagcdn.com/w320/fo.png', + Fiji: 'https://flagcdn.com/w320/fj.png', + Finland: 'https://flagcdn.com/w320/fi.png', + France: 'https://flagcdn.com/w320/fr.png', + 'French Guiana': 'https://flagcdn.com/w320/gf.png', + 'French Polynesia': 'https://flagcdn.com/w320/pf.png', + 'French Southern and Antarctic Lands': 'https://flagcdn.com/w320/tf.png', + Gabon: 'https://flagcdn.com/w320/ga.png', + Gambia: 'https://flagcdn.com/w320/gm.png', + Georgia: 'https://flagcdn.com/w320/ge.png', + Germany: 'https://flagcdn.com/w320/de.png', + Ghana: 'https://flagcdn.com/w320/gh.png', + Gibraltar: 'https://flagcdn.com/w320/gi.png', + Greece: 'https://flagcdn.com/w320/gr.png', + Greenland: 'https://flagcdn.com/w320/gl.png', + Grenada: 'https://flagcdn.com/w320/gd.png', + Guadeloupe: 'https://flagcdn.com/w320/gp.png', + Guam: 'https://flagcdn.com/w320/gu.png', + Guatemala: 'https://flagcdn.com/w320/gt.png', + Guernsey: 'https://flagcdn.com/w320/gg.png', + Guinea: 'https://flagcdn.com/w320/gn.png', + 'Guinea-Bissau': 'https://flagcdn.com/w320/gw.png', + Guyana: 'https://flagcdn.com/w320/gy.png', + Haiti: 'https://flagcdn.com/w320/ht.png', + 'Heard Island and McDonald Islands': 'https://flagcdn.com/w320/hm.png', + Honduras: 'https://flagcdn.com/w320/hn.png', + 'Hong Kong': 'https://flagcdn.com/w320/hk.png', + Hungary: 'https://flagcdn.com/w320/hu.png', + Iceland: 'https://flagcdn.com/w320/is.png', + India: 'https://flagcdn.com/w320/in.png', + Indonesia: 'https://flagcdn.com/w320/id.png', + Iran: 'https://flagcdn.com/w320/ir.png', + Iraq: 'https://flagcdn.com/w320/iq.png', + Ireland: 'https://flagcdn.com/w320/ie.png', + 'Isle of Man': 'https://flagcdn.com/w320/im.png', + Israel: 'https://flagcdn.com/w320/il.png', + Italy: 'https://flagcdn.com/w320/it.png', + 'Ivory Coast': 'https://flagcdn.com/w320/ci.png', + Jamaica: 'https://flagcdn.com/w320/jm.png', + Japan: 'https://flagcdn.com/w320/jp.png', + Jersey: 'https://flagcdn.com/w320/je.png', + Jordan: 'https://flagcdn.com/w320/jo.png', + Kazakhstan: 'https://flagcdn.com/w320/kz.png', + Kenya: 'https://flagcdn.com/w320/ke.png', + Kiribati: 'https://flagcdn.com/w320/ki.png', + Kosovo: 'https://flagcdn.com/w320/xk.png', + Kuwait: 'https://flagcdn.com/w320/kw.png', + Kyrgyzstan: 'https://flagcdn.com/w320/kg.png', + Laos: 'https://flagcdn.com/w320/la.png', + Latvia: 'https://flagcdn.com/w320/lv.png', + Lebanon: 'https://flagcdn.com/w320/lb.png', + Lesotho: 'https://flagcdn.com/w320/ls.png', + Liberia: 'https://flagcdn.com/w320/lr.png', + Libya: 'https://flagcdn.com/w320/ly.png', + Liechtenstein: 'https://flagcdn.com/w320/li.png', + Lithuania: 'https://flagcdn.com/w320/lt.png', + Luxembourg: 'https://flagcdn.com/w320/lu.png', + Macau: 'https://flagcdn.com/w320/mo.png', + Madagascar: 'https://flagcdn.com/w320/mg.png', + Malawi: 'https://flagcdn.com/w320/mw.png', + Malaysia: 'https://flagcdn.com/w320/my.png', + Maldives: 'https://flagcdn.com/w320/mv.png', + Mali: 'https://flagcdn.com/w320/ml.png', + Malta: 'https://flagcdn.com/w320/mt.png', + 'Marshall Islands': 'https://flagcdn.com/w320/mh.png', + Martinique: 'https://flagcdn.com/w320/mq.png', + Mauritania: 'https://flagcdn.com/w320/mr.png', + Mauritius: 'https://flagcdn.com/w320/mu.png', + Mayotte: 'https://flagcdn.com/w320/yt.png', + Mexico: 'https://flagcdn.com/w320/mx.png', + Micronesia: 'https://flagcdn.com/w320/fm.png', + Moldova: 'https://flagcdn.com/w320/md.png', + Monaco: 'https://flagcdn.com/w320/mc.png', + Mongolia: 'https://flagcdn.com/w320/mn.png', + Montenegro: 'https://flagcdn.com/w320/me.png', + Montserrat: 'https://flagcdn.com/w320/ms.png', + Morocco: 'https://flagcdn.com/w320/ma.png', + Mozambique: 'https://flagcdn.com/w320/mz.png', + Myanmar: 'https://flagcdn.com/w320/mm.png', + Namibia: 'https://flagcdn.com/w320/na.png', + Nauru: 'https://flagcdn.com/w320/nr.png', + Nepal: 'https://flagcdn.com/w320/np.png', + Netherlands: 'https://flagcdn.com/w320/nl.png', + 'New Caledonia': 'https://flagcdn.com/w320/nc.png', + 'New Zealand': 'https://flagcdn.com/w320/nz.png', + Nicaragua: 'https://flagcdn.com/w320/ni.png', + Niger: 'https://flagcdn.com/w320/ne.png', + Nigeria: 'https://flagcdn.com/w320/ng.png', + Niue: 'https://flagcdn.com/w320/nu.png', + 'Norfolk Island': 'https://flagcdn.com/w320/nf.png', + 'North Korea': 'https://flagcdn.com/w320/kp.png', + 'North Macedonia': 'https://flagcdn.com/w320/mk.png', + 'Northern Mariana Islands': 'https://flagcdn.com/w320/mp.png', + Norway: 'https://flagcdn.com/w320/no.png', + Oman: 'https://flagcdn.com/w320/om.png', + Pakistan: 'https://flagcdn.com/w320/pk.png', + Palau: 'https://flagcdn.com/w320/pw.png', + Palestine: 'https://flagcdn.com/w320/ps.png', + Panama: 'https://flagcdn.com/w320/pa.png', + 'Papua New Guinea': 'https://flagcdn.com/w320/pg.png', + Paraguay: 'https://flagcdn.com/w320/py.png', + Peru: 'https://flagcdn.com/w320/pe.png', + Philippines: 'https://flagcdn.com/w320/ph.png', + 'Pitcairn Islands': 'https://flagcdn.com/w320/pn.png', + Poland: 'https://flagcdn.com/w320/pl.png', + Portugal: 'https://flagcdn.com/w320/pt.png', + 'Puerto Rico': 'https://flagcdn.com/w320/pr.png', + Qatar: 'https://flagcdn.com/w320/qa.png', + 'Republic of the Congo': 'https://flagcdn.com/w320/cg.png', + Reunion: 'https://flagcdn.com/w320/re.png', + Romania: 'https://flagcdn.com/w320/ro.png', + Russia: 'https://upload.wikimedia.org/wikipedia/commons/6/6f/White-blue-white_flag.svg', + Rwanda: 'https://flagcdn.com/w320/rw.png', + 'Saint Barthelemy': 'https://flagcdn.com/w320/bl.png', + 'Saint Helena, Ascension and Tristan da Cunha': 'https://flagcdn.com/w320/sh.png', + 'Saint Kitts and Nevis': 'https://flagcdn.com/w320/kn.png', + 'Saint Lucia': 'https://flagcdn.com/w320/lc.png', + 'Saint Martin': 'https://flagcdn.com/w320/mf.png', + 'Saint Pierre and Miquelon': 'https://flagcdn.com/w320/pm.png', + 'Saint Vincent and the Grenadines': 'https://flagcdn.com/w320/vc.png', + Samoa: 'https://flagcdn.com/w320/ws.png', + 'San Marino': 'https://flagcdn.com/w320/sm.png', + 'Saudi Arabia': 'https://flagcdn.com/w320/sa.png', + Senegal: 'https://flagcdn.com/w320/sn.png', + Serbia: 'https://flagcdn.com/w320/rs.png', + Seychelles: 'https://flagcdn.com/w320/sc.png', + 'Sierra Leone': 'https://flagcdn.com/w320/sl.png', + Singapore: 'https://flagcdn.com/w320/sg.png', + 'Sint Maarten': 'https://flagcdn.com/w320/sx.png', + Slovakia: 'https://flagcdn.com/w320/sk.png', + Slovenia: 'https://flagcdn.com/w320/si.png', + 'Solomon Islands': 'https://flagcdn.com/w320/sb.png', + Somalia: 'https://flagcdn.com/w320/so.png', + 'South Africa': 'https://flagcdn.com/w320/za.png', + 'South Georgia': 'https://flagcdn.com/w320/gs.png', + 'South Korea': 'https://flagcdn.com/w320/kr.png', + 'South Sudan': 'https://flagcdn.com/w320/ss.png', + Spain: 'https://flagcdn.com/w320/es.png', + 'Sri Lanka': 'https://flagcdn.com/w320/lk.png', + Sudan: 'https://flagcdn.com/w320/sd.png', + Suriname: 'https://flagcdn.com/w320/sr.png', + 'Svalbard and Jan Mayen': 'https://flagcdn.com/w320/sj.png', + Sweden: 'https://flagcdn.com/w320/se.png', + Switzerland: 'https://flagcdn.com/w320/ch.png', + Syria: 'https://flagcdn.com/w320/sy.png', + Taiwan: 'https://flagcdn.com/w320/tw.png', + Tajikistan: 'https://flagcdn.com/w320/tj.png', + Tanzania: 'https://flagcdn.com/w320/tz.png', + Thailand: 'https://flagcdn.com/w320/th.png', + 'Timor-Leste': 'https://flagcdn.com/w320/tl.png', + Togo: 'https://flagcdn.com/w320/tg.png', + Tokelau: 'https://flagcdn.com/w320/tk.png', + Tonga: 'https://flagcdn.com/w320/to.png', + 'Trinidad and Tobago': 'https://flagcdn.com/w320/tt.png', + Tunisia: 'https://flagcdn.com/w320/tn.png', + Turkey: 'https://flagcdn.com/w320/tr.png', + Turkmenistan: 'https://flagcdn.com/w320/tm.png', + 'Turks and Caicos Islands': 'https://flagcdn.com/w320/tc.png', + Tuvalu: 'https://flagcdn.com/w320/tv.png', + Uganda: 'https://flagcdn.com/w320/ug.png', + Ukraine: 'https://flagcdn.com/w320/ua.png', + 'United Arab Emirates': 'https://flagcdn.com/w320/ae.png', + 'United Kingdom': 'https://flagcdn.com/w320/gb.png', + 'United States': 'https://flagcdn.com/w320/us.png', + 'United States Minor Outlying Islands': 'https://flagcdn.com/w320/um.png', + 'United States Virgin Islands': 'https://flagcdn.com/w320/vi.png', + Uruguay: 'https://flagcdn.com/w320/uy.png', + Uzbekistan: 'https://flagcdn.com/w320/uz.png', + Vanuatu: 'https://flagcdn.com/w320/vu.png', + 'Vatican City': 'https://flagcdn.com/w320/va.png', + Venezuela: 'https://flagcdn.com/w320/ve.png', + Vietnam: 'https://flagcdn.com/w320/vn.png', + 'Wallis and Futuna': 'https://flagcdn.com/w320/wf.png', + 'Western Sahara': 'https://flagcdn.com/w320/eh.png', + Yemen: 'https://flagcdn.com/w320/ye.png', + Zambia: 'https://flagcdn.com/w320/zm.png', + Zimbabwe: 'https://flagcdn.com/w320/zw.png', +}; diff --git a/client/src/shared/constant/index.tsx b/client/src/shared/constant/index.tsx index 87c1c211a..0026d265b 100644 --- a/client/src/shared/constant/index.tsx +++ b/client/src/shared/constant/index.tsx @@ -1,4 +1,5 @@ export { concentrations } from './concentrations'; -export { frameworkColors } from './framework-colors'; -export { frameworkTextColors } from './framework-colors'; +export { frameworkColors, frameworkTextColors } from './framework-colors'; export { programmingLanguageOptions, languageOptions } from './programming-languages'; +export { countries } from './countries'; +export { countryFlags } from './country-flags'; diff --git a/client/src/shared/fixtures/team.ts b/client/src/shared/fixtures/team.ts index 2a4293dc5..51733f6d1 100644 --- a/client/src/shared/fixtures/team.ts +++ b/client/src/shared/fixtures/team.ts @@ -1,22 +1,26 @@ import { ITeam } from '@teameights/types'; -import userResponseFixture, { generateRandomUserResponseFixture } from './user'; +import { userResponseFixture, generateRandomUserResponseFixture } from './user'; +import { faker } from '@faker-js/faker'; const users = generateRandomUserResponseFixture(7); export const teamFixture: ITeam = { - id: '1', + id: 1, name: 'Sample Team', description: 'This is a sample team', leader: userResponseFixture, members: users, - country: 'United States', + country: 'Russia', tag: 'SampleTag', type: 'open', wins: 10, points: 100, - image: 'https://picsum.photos/3000/3000', + photo: { + id: faker.number.int(), + path: `https://picsum.photos/${Math.floor(Math.random() * 1001) + 3000}/${ + Math.floor(Math.random() * 1001) + 3000 + }`, + }, createdAt: new Date('2023-10-19T12:00:00Z'), updatedAt: new Date('2023-10-19T12:30:00Z'), deletedAt: new Date('2023-10-19T13:00:00Z'), }; - -export default teamFixture; diff --git a/client/src/shared/fixtures/user.ts b/client/src/shared/fixtures/user.ts index 51a19af82..30dc1c58f 100644 --- a/client/src/shared/fixtures/user.ts +++ b/client/src/shared/fixtures/user.ts @@ -1,6 +1,10 @@ import { IUserResponse } from '@teameights/types'; import { faker } from '@faker-js/faker'; +const getRandomNumberBetween = (min: number, max: number) => { + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + export const generateRandomUserResponseFixture = (amount: number): IUserResponse[] => { const users = []; faker.seed(123); // You can use a specific seed for consistent random data @@ -11,7 +15,10 @@ export const generateRandomUserResponseFixture = (amount: number): IUserResponse fullName: faker.person.fullName(), photo: { id: faker.number.int(), - path: 'https://picsum.photos/3000/3000', + path: `https://picsum.photos/${getRandomNumberBetween(1000, 1500)}/${getRandomNumberBetween( + 1000, + 1500 + )}`, }, role: { id: faker.number.int(), name: 'USER' }, status: { id: faker.number.int(), name: 'Active' }, @@ -73,19 +80,22 @@ export const userResponseFixture: IUserResponse = { username: 'sample_username', fullName: 'John Doe', photo: { - id: 1, - path: 'https://picsum.photos/3000/3000', - }, // Replace with actual photo data if available + id: faker.number.int(), + path: `https://picsum.photos/${getRandomNumberBetween(1000, 1500)}/${getRandomNumberBetween( + 1000, + 1500 + )}`, + }, role: { id: 1, name: 'Sample Role' }, // Replace with actual role data status: { id: 1, name: 'Active' }, // Replace with actual status data isLeader: true, country: 'United States', dateOfBirth: new Date('1990-01-15'), - concentration: 'Computer Science', + concentration: 'Backend Developer', description: 'Sample user description', experience: '2 years', - programmingLanguages: ['JavaScript', 'Python'], - frameworks: ['React', 'Node.js'], + frameworks: ['NodeJS', 'React', 'jQuery'], + programmingLanguages: ['JS', 'Swift', 'Dart', 'Scala', 'Ruby'], universities: [ { id: 1, @@ -125,5 +135,3 @@ export const userResponseFixture: IUserResponse = { updatedAt: new Date('2022-02-15'), // Replace with actual update date deletedAt: null, }; - -export default userResponseFixture; diff --git a/client/src/shared/lib/utils/capitalize/capitalize.test.ts b/client/src/shared/lib/utils/capitalize/capitalize.test.ts new file mode 100644 index 000000000..b17d65138 --- /dev/null +++ b/client/src/shared/lib/utils/capitalize/capitalize.test.ts @@ -0,0 +1,48 @@ +import { capitalize } from './capitalize'; // Replace 'your-file' with the actual file path + +describe('capitalizeFirstLetter', () => { + it('should capitalize the first letter of a lowercase string', () => { + const input = 'hello, world!'; + const expected = 'Hello, world!'; + const result = capitalize(input); + expect(result).toBe(expected); + }); + + it('should not modify an empty string', () => { + const input = ''; + const result = capitalize(input); + expect(result).toBe(''); + }); + + it('should not modify a string with an already capitalized first letter', () => { + const input = 'Hello, world!'; + const result = capitalize(input); + expect(result).toBe('Hello, world!'); + }); + + it('should capitalize the first letter of a single lowercase character', () => { + const input = 'a'; + const result = capitalize(input); + expect(result).toBe('A'); + }); + + it('should capitalize the first letter of a string with leading spaces', () => { + const input = ' hello, world!'; + const expected = 'Hello, world!'; + const result = capitalize(input); + expect(result).toBe(expected); + }); + + it('should capitalize the first letter of a string with trailing spaces', () => { + const input = 'hello, world! '; + const expected = 'Hello, world!'; + const result = capitalize(input); + expect(result).toBe(expected); + }); + + it('should handle strings with only spaces', () => { + const input = ' '; + const result = capitalize(input); + expect(result).toBe(' '); + }); +}); diff --git a/client/src/shared/lib/utils/capitalize/capitalize.ts b/client/src/shared/lib/utils/capitalize/capitalize.ts new file mode 100644 index 000000000..d799ce157 --- /dev/null +++ b/client/src/shared/lib/utils/capitalize/capitalize.ts @@ -0,0 +1,8 @@ +export const capitalize = (input: string): string => { + const trimmedInput = input.trim(); + + if (!trimmedInput.length) { + return input; + } + return trimmedInput.charAt(0).toUpperCase() + trimmedInput.slice(1); +}; diff --git a/client/src/shared/lib/utils/get-age/get-age.test.ts b/client/src/shared/lib/utils/get-age/get-age.test.ts new file mode 100644 index 000000000..1fce6b1e4 --- /dev/null +++ b/client/src/shared/lib/utils/get-age/get-age.test.ts @@ -0,0 +1,29 @@ +import { calculateAge } from './get-age'; + +describe('calculateAge', () => { + it('should return the correct age before the birthday', () => { + expect(calculateAge('2000-12-31')).toBe(22); + }); + + it('should return the correct age after the birthday', () => { + expect(calculateAge('2000-01-01')).toBe(23); + }); + + it('should return the correct age on the birthday', () => { + const currentDate = new Date(); + const birthDate = new Date( + currentDate.getFullYear() - 20, + currentDate.getMonth(), + currentDate.getDate() + ); + expect(calculateAge(birthDate)).toBe(20); + }); + + it('should handle Date objects', () => { + expect(calculateAge(new Date('1990-05-17'))).toBe(33); + }); + + it('should handle string dates', () => { + expect(calculateAge('1990-05-17')).toBe(33); + }); +}); diff --git a/client/src/shared/lib/utils/get-age/get-age.ts b/client/src/shared/lib/utils/get-age/get-age.ts new file mode 100644 index 000000000..d6c0ff34f --- /dev/null +++ b/client/src/shared/lib/utils/get-age/get-age.ts @@ -0,0 +1,20 @@ +export const calculateAge = (birthDate: string | Date): number => { + const birthDateObj = typeof birthDate === 'string' ? new Date(birthDate) : birthDate; + const currentDate = new Date(); + + const currentYear = currentDate.getFullYear(); + const birthYear = birthDateObj.getFullYear(); + + let age = currentYear - birthYear; + + const isBirthdayPassed = + currentDate.getMonth() > birthDateObj.getMonth() || + (currentDate.getMonth() === birthDateObj.getMonth() && + currentDate.getDate() >= birthDateObj.getDate()); + + if (!isBirthdayPassed) { + age--; + } + + return age; +}; diff --git a/client/src/shared/lib/utils/get-country-flag/get-country-flag.test.ts b/client/src/shared/lib/utils/get-country-flag/get-country-flag.test.ts new file mode 100644 index 000000000..a0845af73 --- /dev/null +++ b/client/src/shared/lib/utils/get-country-flag/get-country-flag.test.ts @@ -0,0 +1,23 @@ +import { getCountryFlag } from './get-country-flag'; + +describe('getCountryFlag', () => { + it('should get Ukrainian flag', () => { + expect(getCountryFlag('Ukraine')).toBe('https://flagcdn.com/w320/ua.png'); + }); + + it('should get Turks and Caicos Islands flag', () => { + expect(getCountryFlag('Turks and Caicos Islands')).toBe('https://flagcdn.com/w320/tc.png'); + }); + + it('should get Turks and Aland Islands flag', () => { + expect(getCountryFlag('Aland Islands')).toBe('https://flagcdn.com/w320/ax.png'); + }); + + it('should get nothing', () => { + expect(getCountryFlag('')).toBe(''); + }); + + it('should get nothing for random words', () => { + expect(getCountryFlag('adfasreavxgeag')).toBe(''); + }); +}); diff --git a/client/src/shared/lib/utils/get-country-flag/get-country-flag.tsx b/client/src/shared/lib/utils/get-country-flag/get-country-flag.tsx new file mode 100644 index 000000000..f436351df --- /dev/null +++ b/client/src/shared/lib/utils/get-country-flag/get-country-flag.tsx @@ -0,0 +1,11 @@ +import { countryFlags } from '@/shared/constant'; + +export const getCountryFlag = (countryName: string): string => { + const normalizedCountryName = countryName.trim(); + + if (normalizedCountryName in countryFlags) { + return countryFlags[normalizedCountryName]; + } + + return ''; +}; diff --git a/client/src/shared/lib/utils/index.ts b/client/src/shared/lib/utils/index.ts index 2ab742f3c..a51420031 100644 --- a/client/src/shared/lib/utils/index.ts +++ b/client/src/shared/lib/utils/index.ts @@ -1 +1,4 @@ export { getElapsedTime } from './get-elapsed-time/get-elapsed-time'; +export { getCountryFlag } from './get-country-flag/get-country-flag'; +export { calculateAge } from './get-age/get-age'; +export { capitalize } from './capitalize/capitalize'; diff --git a/client/src/shared/ui/badge/badge-framework/badge-framework.tsx b/client/src/shared/ui/badge/badge-framework/badge-framework.tsx index e2308bf94..147b7ee9f 100644 --- a/client/src/shared/ui/badge/badge-framework/badge-framework.tsx +++ b/client/src/shared/ui/badge/badge-framework/badge-framework.tsx @@ -12,7 +12,6 @@ import styles from './badge-framework.module.scss'; * Props: * * @prop {string} data - The name of the framework to display and style the badge. Make sure the name is available in 'frameworkColors' and 'frameworkTextColors'. - * @prop {number} key - The unique key for the badge, especially useful if rendering multiple badges in a list. * @prop {string} [className] - Additional Css classes to apply to the badge for custom styling. * @prop {string} [maxWidth='100%'] - Custom maximum width for the badge. Must be passed with a valid Css unit (e.g. '50px', '100%'). Default is '100%'. * diff --git a/client/src/shared/ui/badge/badge-language/badge-language.tsx b/client/src/shared/ui/badge/badge-language/badge-language.tsx index 4db294123..b1260e7df 100644 --- a/client/src/shared/ui/badge/badge-language/badge-language.tsx +++ b/client/src/shared/ui/badge/badge-language/badge-language.tsx @@ -12,7 +12,7 @@ import styles from './badge-language.module.scss'; * Props: * * @prop {string} data - The key corresponding to the language name in 'languageOptions'. - * @prop {number} key - The unique key for the badge, especially useful if rendering multiple badges in a list or collection. + * @prop {number} uniqueKey - The unique key for the badge, especially useful if rendering multiple badges in a list or collection. * @prop {string} [className] - Additional Css classes to apply to the badge for custom styling. * @prop {string} [maxWidth='100%'] - Custom maximum width for the badge. Must be passed with a valid Css unit (e.g. '50px', '100%'). Default is '100%'. * diff --git a/client/src/shared/ui/button/button.tsx b/client/src/shared/ui/button/button.tsx index 7939893ad..7bfd7fce0 100644 --- a/client/src/shared/ui/button/button.tsx +++ b/client/src/shared/ui/button/button.tsx @@ -48,6 +48,7 @@ interface ButtonProps extends ButtonHTMLAttributes { isDisabled?: boolean; width?: string; color?: Colors; + padding?: string; } export const Button: FC = props => { @@ -60,6 +61,7 @@ export const Button: FC = props => { isDisabled = false, width, color = 'white', + padding, ...rest } = props; @@ -68,6 +70,7 @@ export const Button: FC = props => { disabled={isDisabled} style={{ width: width ? `${width}` : undefined, + padding: padding ? `${padding}` : undefined, }} className={clsx( styles.container, diff --git a/client/src/shared/ui/drawer/drawer.tsx b/client/src/shared/ui/drawer/drawer.tsx index d3f7ddee8..5498eb5f5 100644 --- a/client/src/shared/ui/drawer/drawer.tsx +++ b/client/src/shared/ui/drawer/drawer.tsx @@ -40,10 +40,10 @@ interface DrawerProps { open: boolean; onClose: () => void; isFullHeight?: boolean; - // direction: "left" | "right" | "top" | "bottom"; + direction?: 'left' | 'right' | 'top' | 'bottom'; } export const Drawer: FC> = props => { - const { open, onClose, children, isFullHeight } = props; + const { open, onClose, isFullHeight, direction = 'bottom', children } = props; const style = { borderRadius: isFullHeight ? '0' : '15px 15px 0 0', @@ -53,7 +53,14 @@ export const Drawer: FC> = props => { } as React.CSSProperties; return ( - + {children} ); diff --git a/client/src/shared/ui/flex/flex.module.scss b/client/src/shared/ui/flex/flex.module.scss index e020802a0..d1a5032ce 100644 --- a/client/src/shared/ui/flex/flex.module.scss +++ b/client/src/shared/ui/flex/flex.module.scss @@ -1,16 +1,3 @@ .flexWrapper { display: flex; - /* align-items: var(--align, normal); - justify-content: var(--justify, normal); - gap: var(--gap, 0); - margin: var(--margin, 0); - padding: var(--padding, 0); - flex-direction: var(--direction, row); - max-height: var(--maxHeight, none); - position: var(--position, static); - width: var(--width, 100%); - max-width: var(--maxWidth, none); - height: var(--height, auto); - flex-wrap: nowrap; - flex-shrink: unset; */ } diff --git a/client/src/shared/ui/flex/flex.tsx b/client/src/shared/ui/flex/flex.tsx index 7e8178ffa..a2cd03053 100644 --- a/client/src/shared/ui/flex/flex.tsx +++ b/client/src/shared/ui/flex/flex.tsx @@ -47,13 +47,11 @@ export interface FlexProps { /** * [props.height] - The height property defines the height of the component. Default is 'auto'. */ - height?: string; - + wrap?: string; /** - * [props.flexWrap] - The flex-wrap property controls whether the flex container is single-line or multi-line, and the direction of the cross-axis, which determines the direction new lines are stacked in. Default is 'nowrap' - + * [props.wrap] - The wrap property defines the wrap of the component. Default is 'none'. */ - wrap?: string; + height?: string; /** * [props.flexShrink] - Sets the flex shrink factor. Negative numbers are invalid. Default is 'nowrap' @@ -62,15 +60,14 @@ export interface FlexProps { shrink?: string | number; /** - * [props.className] - adds additional classes to base - + * props.children - The content to be laid out. */ - className?: string; + children: React.ReactNode; /** - * props.children - The content to be laid out. + * props.className - Additional classnames you want to add to flex if needed */ - children: React.ReactNode; + className?: string; } /** @@ -112,8 +109,8 @@ export const Flex: React.FC = props => { position, width, maxWidth, - height, wrap, + height, shrink, children, className, @@ -130,8 +127,8 @@ export const Flex: React.FC = props => { position: position, width: width, maxWidth: maxWidth, - height: height, flexWrap: wrap, + height: height, flexShrink: shrink, } as React.CSSProperties; diff --git a/client/src/shared/ui/image-loader/image-loader.module.scss b/client/src/shared/ui/image-loader/image-loader.module.scss new file mode 100644 index 000000000..4863c1bbd --- /dev/null +++ b/client/src/shared/ui/image-loader/image-loader.module.scss @@ -0,0 +1,14 @@ +.crown_container { + position: absolute; + transform: rotate(30deg) translateX(-15%) translateY(-255%); + z-index: 1000; + + svg { + width: 100%; + height: 100%; + } +} + +.relative { + position: relative; +} diff --git a/client/src/shared/ui/image-loader/image-loader.stories.tsx b/client/src/shared/ui/image-loader/image-loader.stories.tsx new file mode 100644 index 000000000..21bd2981c --- /dev/null +++ b/client/src/shared/ui/image-loader/image-loader.stories.tsx @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ImageLoader } from './image-loader'; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: 'shared/ImageLoader', + component: ImageLoader, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const ImageLoader_Heavy: Story = { + render: () => ( +
+ +
+ ), +}; + +export const ImageLoader_Light: Story = { + render: () => ( +
+ +
+ ), +}; + +export const ImageLoader_SuperHeavy: Story = { + render: () => ( +
+ +
+ ), +}; diff --git a/client/src/shared/ui/image-loader/image-loader.tsx b/client/src/shared/ui/image-loader/image-loader.tsx new file mode 100644 index 000000000..c4b918575 --- /dev/null +++ b/client/src/shared/ui/image-loader/image-loader.tsx @@ -0,0 +1,77 @@ +import { Crown20, Crown28, Crown40 } from '@/shared/assets'; +import { CSSProperties, FC, SyntheticEvent, useState } from 'react'; +import Image, { ImageProps } from 'next/image'; +import { SkeletonLoader } from './ui/skeleton-loader'; +import styles from './image-loader.module.scss'; + +interface ImageLoaderProps extends ImageProps { + fallback?: string; + borderRadius?: string; + crownSize?: 20 | 28 | 40; + debug?: boolean; +} +export const ImageLoader: FC = props => { + const { + crownSize, + borderRadius = '0', + width, + height, + src, + fallback = 'https://upload.wikimedia.org/wikipedia/commons/3/3f/Placeholder_view_vector.svg', + debug, + sizes = '100%', + alt, + ...rest + } = props; + const [loading, setLoading] = useState(true); + const [onErrorSrc, setOnErrorSrc] = useState(undefined); + + function handleOnError(e: SyntheticEvent): void { + console.log('error occurred'); + if (e?.currentTarget?.src !== fallback) { + setOnErrorSrc(fallback); + } + } + + const style = { + borderRadius: borderRadius, + objectFit: 'cover', + userSelect: 'none', + imageRendering: 'pixelated', + } as CSSProperties; + + const imageStyle = { + width: width, + height: height, + } as CSSProperties; + + return ( +
+ {loading && ( + + )} +
+
+ !debug && setLoading(false)} + onError={e => handleOnError(e)} + style={style} + fill + sizes={sizes} + alt={alt} + placeholder='empty' + /> +
+ {crownSize && ( +
+ {crownSize === 20 && } + {crownSize === 28 && } + {crownSize === 40 && } +
+ )} +
+
+ ); +}; diff --git a/client/src/shared/ui/image-loader/index.ts b/client/src/shared/ui/image-loader/index.ts new file mode 100644 index 000000000..346525ea1 --- /dev/null +++ b/client/src/shared/ui/image-loader/index.ts @@ -0,0 +1 @@ +export { ImageLoader } from './image-loader'; diff --git a/client/src/shared/ui/image-loader/ui/skeleton-loader/index.ts b/client/src/shared/ui/image-loader/ui/skeleton-loader/index.ts new file mode 100644 index 000000000..08c8e8816 --- /dev/null +++ b/client/src/shared/ui/image-loader/ui/skeleton-loader/index.ts @@ -0,0 +1 @@ +export { SkeletonLoader } from './skeleton-loader'; diff --git a/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.module.scss b/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.module.scss new file mode 100644 index 000000000..2fe4cdf09 --- /dev/null +++ b/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.module.scss @@ -0,0 +1,3 @@ +.absolute { + position: absolute; +} diff --git a/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.tsx b/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.tsx new file mode 100644 index 000000000..06b4b5ca1 --- /dev/null +++ b/client/src/shared/ui/image-loader/ui/skeleton-loader/skeleton-loader.tsx @@ -0,0 +1,33 @@ +import ContentLoader from 'react-content-loader'; +import { ImageProps } from 'next/image'; +import { CSSProperties, FC } from 'react'; +import styles from './skeleton-loader.module.scss'; + +interface SkeletonLoaderProps extends Pick { + borderRadius: string; + debug?: boolean; +} +export const SkeletonLoader: FC = props => { + const { height, width, borderRadius, debug } = props; + + const style = { + borderRadius: borderRadius, + zIndex: debug ? 99 : 'auto', + } as CSSProperties; + + return ( + + + + ); +}; diff --git a/client/src/shared/ui/index.ts b/client/src/shared/ui/index.ts index e66d46416..acf7b86b0 100644 --- a/client/src/shared/ui/index.ts +++ b/client/src/shared/ui/index.ts @@ -9,6 +9,6 @@ export * from './textarea'; export * from './typography'; export * from './portal'; export * from './flex'; -export * from './skeleton'; export * from './icon-wrapper'; export * from './drawer'; +export * from './image-loader'; diff --git a/client/src/shared/ui/modal/modal.module.scss b/client/src/shared/ui/modal/modal.module.scss index 164c27ebd..2f7bfda21 100644 --- a/client/src/shared/ui/modal/modal.module.scss +++ b/client/src/shared/ui/modal/modal.module.scss @@ -1,50 +1,9 @@ -.modalOverlay { - z-index: 99; - position: fixed; - top: 0; - left: 0; - display: flex; - width: 100vw; - height: 100vh; - backdrop-filter: blur(8px); - background-color: rgba(0, 0, 0, 0.5); - opacity: 0; - transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; -} - -.modalOverlay__afterOpen { - opacity: 1; -} - -.modalOverlay__beforeClose { - opacity: 0; -} - -.modalBody { - position: absolute; - left: 50%; - bottom: 0; - width: 100%; +.customModal { + background: var(--cards-color); padding: 32px; border-radius: 15px; - background: var(--base-cards-color, #1a1c22); - transition: all 225ms cubic-bezier(0, 0, 0.2, 1) 0ms; - transform: translate(-50%, 100%); -} - -.modalBody__afterOpen { - bottom: 50%; - transform: translate(-50%, 50%); -} - -.modalBody__beforeClose { - bottom: 0; - transform: translate(-50%, 100%); -} - -.modalBody, -.modalBody__afterOpen, -.modalBody__beforeClose { + margin: 0; + width: 100%; &.size_l { max-width: 570px; } @@ -61,8 +20,11 @@ .modalContent { width: 100%; height: 100%; - max-height: 80vh; - overflow-y: auto; +} + +.customOverlay { + background: rgba(0, 0, 0, 0.50); + backdrop-filter: blur(5px); } .closeButton { @@ -74,7 +36,7 @@ padding: 16px; cursor: pointer; - &:hover path { - stroke: #d42522; + &:hover { + fill: red; } } diff --git a/client/src/shared/ui/modal/modal.tsx b/client/src/shared/ui/modal/modal.tsx index a74c82aff..2d9c9829a 100644 --- a/client/src/shared/ui/modal/modal.tsx +++ b/client/src/shared/ui/modal/modal.tsx @@ -1,11 +1,10 @@ 'use client'; -import classNames from 'clsx'; +import clsx from 'clsx'; import { FC, PropsWithChildren } from 'react'; -import ReactModal from 'react-modal'; - +import 'react-responsive-modal/styles.css'; +import { Modal as ResponsiveModal } from 'react-responsive-modal'; import styles from './modal.module.scss'; - import { Cross } from '@/shared/assets'; /** @@ -44,31 +43,27 @@ import { Cross } from '@/shared/assets'; interface ModalProps { isOpen: boolean; - onClose?: () => void; + onClose: () => void; size?: 's' | 'm' | 'l'; + className?: string; } export const Modal: FC> = props => { const { isOpen, onClose, size = 's', children } = props; return ( -
{children}
-
+ ); }; diff --git a/client/src/shared/ui/skeleton/index.ts b/client/src/shared/ui/skeleton/index.ts deleted file mode 100644 index 9147f13a9..000000000 --- a/client/src/shared/ui/skeleton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Skeleton } from './skeleton'; diff --git a/client/src/shared/ui/skeleton/skeleton.module.scss b/client/src/shared/ui/skeleton/skeleton.module.scss index 98645f0f1..783919c77 100644 --- a/client/src/shared/ui/skeleton/skeleton.module.scss +++ b/client/src/shared/ui/skeleton/skeleton.module.scss @@ -48,12 +48,12 @@ animation-iteration-count: infinite; } +/*@media (prefers-reduced-motion) {*/ +/* .reactLoadingSkeleton {*/ +/* --pseudo-element-display: none; !* Disable animation *!*/ +/* }*/ +/*}*/ + .baseSpan { line-height: 1; } - -//@media (prefers-reduced-motion) { -// .reactLoadingSkeleton { -// --pseudo-element-display: none; -// } -//} diff --git a/client/src/shared/ui/skeleton/skeleton.tsx b/client/src/shared/ui/skeleton/skeleton.tsx index b536b2414..b0724ecb8 100644 --- a/client/src/shared/ui/skeleton/skeleton.tsx +++ b/client/src/shared/ui/skeleton/skeleton.tsx @@ -79,10 +79,6 @@ export interface SkeletonProps { * // Skeleton elements with custom styles and class names * * ``` - * - * ISSUES: - * If you have a problem where your outer span is not the same height as internal, - * wrap skeleton into any element with display: block, inline-block, flex, etc */ export const Skeleton = memo((props: SkeletonProps) => { const { diff --git a/client/src/widgets/index.ts b/client/src/widgets/index.ts new file mode 100644 index 000000000..8ebfc58cd --- /dev/null +++ b/client/src/widgets/index.ts @@ -0,0 +1 @@ +export * from './modals'; diff --git a/client/src/widgets/modals/action-modal/action-modal.tsx b/client/src/widgets/modals/action-modal/action-modal.tsx new file mode 100644 index 000000000..7ffc1c848 --- /dev/null +++ b/client/src/widgets/modals/action-modal/action-modal.tsx @@ -0,0 +1,24 @@ +import { useGetScreenWidth } from '@/shared/lib'; +import { Desktop } from './desktop'; +import { Phone } from './phone'; +import { FC, PropsWithChildren } from 'react'; +import { ActionModalProps } from './interfaces'; + +export const ActionModal: FC> = props => { + const { heading, sub, isOpen, handleClose, children } = props; + const width = useGetScreenWidth(); + + return ( + <> + {width > 520 ? ( + + {children} + + ) : ( + + {children} + + )} + + ); +}; diff --git a/client/src/widgets/modals/action-modal/desktop/desktop.module.scss b/client/src/widgets/modals/action-modal/desktop/desktop.module.scss new file mode 100644 index 000000000..05fc25296 --- /dev/null +++ b/client/src/widgets/modals/action-modal/desktop/desktop.module.scss @@ -0,0 +1,4 @@ +.text { + text-align: center; + margin-bottom: 24px; +} diff --git a/client/src/widgets/modals/action-modal/desktop/desktop.stories.tsx b/client/src/widgets/modals/action-modal/desktop/desktop.stories.tsx new file mode 100644 index 000000000..8deba6e36 --- /dev/null +++ b/client/src/widgets/modals/action-modal/desktop/desktop.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta } from '@storybook/react'; +import { Desktop } from './desktop'; +import { useState } from 'react'; +import { Button } from '@/shared/ui'; + +const meta: Meta = { + title: 'widgets/modals/action/desktop', + component: Desktop, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +export const ActionModal_desktop = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + + return ( +
+ + + + + +
+ ); +}; diff --git a/client/src/widgets/modals/action-modal/desktop/desktop.tsx b/client/src/widgets/modals/action-modal/desktop/desktop.tsx new file mode 100644 index 000000000..1d4c8de6e --- /dev/null +++ b/client/src/widgets/modals/action-modal/desktop/desktop.tsx @@ -0,0 +1,22 @@ +import { Flex, Modal, Typography } from '@/shared/ui'; +import styles from './desktop.module.scss'; +import { FC, PropsWithChildren } from 'react'; +import { ActionModalProps } from '../interfaces'; +export const Desktop: FC> = props => { + const { heading, sub, isOpen, handleClose, children } = props; + return ( +
+ + + + {heading} + {sub} + + + {children} + + + +
+ ); +}; diff --git a/client/src/widgets/modals/action-modal/desktop/index.ts b/client/src/widgets/modals/action-modal/desktop/index.ts new file mode 100644 index 000000000..cf4d78d5f --- /dev/null +++ b/client/src/widgets/modals/action-modal/desktop/index.ts @@ -0,0 +1 @@ +export { Desktop } from './desktop'; diff --git a/client/src/widgets/modals/action-modal/interfaces/action-modal-interface.ts b/client/src/widgets/modals/action-modal/interfaces/action-modal-interface.ts new file mode 100644 index 000000000..5c3a9ddc9 --- /dev/null +++ b/client/src/widgets/modals/action-modal/interfaces/action-modal-interface.ts @@ -0,0 +1,6 @@ +export interface ActionModalProps { + heading: string; + sub: string; + isOpen: boolean; + handleClose: () => void; +} diff --git a/client/src/widgets/modals/action-modal/interfaces/index.ts b/client/src/widgets/modals/action-modal/interfaces/index.ts new file mode 100644 index 000000000..7827baf77 --- /dev/null +++ b/client/src/widgets/modals/action-modal/interfaces/index.ts @@ -0,0 +1 @@ +export { type ActionModalProps } from './action-modal-interface'; diff --git a/client/src/widgets/modals/action-modal/phone/index.ts b/client/src/widgets/modals/action-modal/phone/index.ts new file mode 100644 index 000000000..87039760e --- /dev/null +++ b/client/src/widgets/modals/action-modal/phone/index.ts @@ -0,0 +1 @@ +export { Phone } from './phone'; diff --git a/client/src/widgets/modals/action-modal/phone/phone.module.scss b/client/src/widgets/modals/action-modal/phone/phone.module.scss new file mode 100644 index 000000000..353e3bdd4 --- /dev/null +++ b/client/src/widgets/modals/action-modal/phone/phone.module.scss @@ -0,0 +1,18 @@ +.text { + text-align: center; + margin-bottom: 24px; +} + +.close_button { + position: absolute; + top: 0; + right: 0; + width: 52px; + height: 52px; + padding: 16px; + cursor: pointer; + + &:hover { + fill: red; + } +} diff --git a/client/src/widgets/modals/action-modal/phone/phone.stories.tsx b/client/src/widgets/modals/action-modal/phone/phone.stories.tsx new file mode 100644 index 000000000..ae6fdb649 --- /dev/null +++ b/client/src/widgets/modals/action-modal/phone/phone.stories.tsx @@ -0,0 +1,43 @@ +import type { Meta } from '@storybook/react'; +import { Phone } from './phone'; +import { Button } from '@/shared/ui'; +import { useState } from 'react'; + +const meta: Meta = { + title: 'widgets/modals/action/phone', + component: Phone, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +export const ActionModal_phone = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + return ( +
+ + + + + +
+ ); +}; diff --git a/client/src/widgets/modals/action-modal/phone/phone.tsx b/client/src/widgets/modals/action-modal/phone/phone.tsx new file mode 100644 index 000000000..609ec51b5 --- /dev/null +++ b/client/src/widgets/modals/action-modal/phone/phone.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { Drawer, Flex, Typography } from '@/shared/ui'; +import { FC, PropsWithChildren } from 'react'; +import styles from './phone.module.scss'; +import { Cross } from '@/shared/assets'; +import { ActionModalProps } from '../interfaces'; + +export const Phone: FC> = props => { + const { heading, sub, isOpen, handleClose, children } = props; + return ( + <> + + + + + + {heading} + + + {sub} + + + + {children} + + + + + ); +}; diff --git a/client/src/widgets/modals/index.ts b/client/src/widgets/modals/index.ts new file mode 100644 index 000000000..dc058ce13 --- /dev/null +++ b/client/src/widgets/modals/index.ts @@ -0,0 +1,3 @@ +export { ActionModal } from './action-modal/action-modal'; +export { TeamInfoModal } from './info-modal/team'; +export { UserInfoModal } from './info-modal/user'; diff --git a/client/src/widgets/modals/info-modal/team/desktop/desktop.module.scss b/client/src/widgets/modals/info-modal/team/desktop/desktop.module.scss new file mode 100644 index 000000000..dfa9a446b --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/desktop/desktop.module.scss @@ -0,0 +1,3 @@ +.span_text { + color: var(--green-bright-color); +} diff --git a/client/src/widgets/modals/info-modal/team/desktop/desktop.stories.tsx b/client/src/widgets/modals/info-modal/team/desktop/desktop.stories.tsx new file mode 100644 index 000000000..6e69109f5 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/desktop/desktop.stories.tsx @@ -0,0 +1,43 @@ +import type { Meta } from '@storybook/react'; +import { TeamDesktop } from './desktop'; +import { Button } from '@/shared/ui'; +import { useState } from 'react'; +import { userResponseFixture } from '@/shared/fixtures/user'; +import { teamFixture } from '@/shared/fixtures/team'; + +const meta: Meta = { + title: 'widgets/modals/info/team/desktop', + component: TeamDesktop, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +const handleJoin = () => { + console.log('Join button clicked'); +}; + +export const InfoModalTeam_desktop = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + return ( +
+ + +
+ ); +}; diff --git a/client/src/widgets/modals/info-modal/team/desktop/desktop.tsx b/client/src/widgets/modals/info-modal/team/desktop/desktop.tsx new file mode 100644 index 000000000..7da0dd3b2 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/desktop/desktop.tsx @@ -0,0 +1,121 @@ +import { FC } from 'react'; +import styles from './desktop.module.scss'; +import { Typography, Button, Modal, Flex } from '@/shared/ui'; +import { ArrowRight } from '@/shared/assets'; +import { InfoModalTeamProps } from '../interfaces'; +import { ImageLoader } from '@/shared/ui/image-loader/image-loader'; +import { capitalize, getCountryFlag } from '@/shared/lib'; + +const mockNavigate = (path: string) => { + console.log(`navigate to ${path}`); + // TODO: add real navigation here +}; + +export const TeamDesktop: FC = ({ + user, + team, + handleJoin, + isOpenModal, + handleClose, +}) => { + return ( + <> + + + + + + + + + {team?.name} + + + + {capitalize(team?.type)} Type, {team?.country} + + + + + + + + + Tournaments: 0 + + + Wins: {team?.wins} + + + Points: {team?.points} + + + {team?.description && ( + + {team?.description} + + )} + + +
+ {team.members.length && ( + + {team?.members?.map((teammate, index) => ( + + ))} + + )} +
+
+ + + + + +
+
+ + ); +}; diff --git a/client/src/widgets/modals/info-modal/team/index.ts b/client/src/widgets/modals/info-modal/team/index.ts new file mode 100644 index 000000000..e13591428 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/index.ts @@ -0,0 +1 @@ +export { TeamInfoModal } from './team'; diff --git a/client/src/widgets/modals/info-modal/team/interfaces/index.ts b/client/src/widgets/modals/info-modal/team/interfaces/index.ts new file mode 100644 index 000000000..6ac33b33e --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/interfaces/index.ts @@ -0,0 +1 @@ +export { type InfoModalTeamProps } from './info-modal-team-interface'; diff --git a/client/src/widgets/modals/info-modal/team/interfaces/info-modal-team-interface.ts b/client/src/widgets/modals/info-modal/team/interfaces/info-modal-team-interface.ts new file mode 100644 index 000000000..2524f7c6f --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/interfaces/info-modal-team-interface.ts @@ -0,0 +1,9 @@ +import { ITeam, IUserResponse } from '@teameights/types'; + +export interface InfoModalTeamProps { + team: ITeam; + user: IUserResponse; + isOpenModal: boolean; + handleClose: () => void; + handleJoin: () => void; +} diff --git a/client/src/widgets/modals/info-modal/team/phone/phone.module.scss b/client/src/widgets/modals/info-modal/team/phone/phone.module.scss new file mode 100644 index 000000000..f8722485b --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/phone/phone.module.scss @@ -0,0 +1,8 @@ +.span_text { + color: var(--green-bright-color); +} + +.container { + overflow: auto; +} + diff --git a/client/src/widgets/modals/info-modal/team/phone/phone.stories.tsx b/client/src/widgets/modals/info-modal/team/phone/phone.stories.tsx new file mode 100644 index 000000000..5e9a158b8 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/phone/phone.stories.tsx @@ -0,0 +1,43 @@ +import type { Meta } from '@storybook/react'; +import { TeamPhone } from './phone'; +import { useState } from 'react'; +import { Button } from '@/shared/ui'; +import { userResponseFixture } from '@/shared/fixtures/user'; +import { teamFixture } from '@/shared/fixtures/team'; + +const meta: Meta = { + title: 'widgets/modals/info/team/phone', + component: TeamPhone, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +const handleJoin = () => { + console.log('Join button clicked'); +}; + +export const InfoModalTeam_phone = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + return ( +
+ + +
+ ); +}; diff --git a/client/src/widgets/modals/info-modal/team/phone/phone.tsx b/client/src/widgets/modals/info-modal/team/phone/phone.tsx new file mode 100644 index 000000000..4bb59dc07 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/phone/phone.tsx @@ -0,0 +1,150 @@ +import { ArrowLeft, ArrowRight } from '@/shared/assets'; +import { Button, Drawer, Flex, Typography } from '@/shared/ui'; +import { FC } from 'react'; +import styles from './phone.module.scss'; +import { InfoModalTeamProps } from '../interfaces'; +import { ImageLoader } from '@/shared/ui/image-loader/image-loader'; +import { capitalize, getCountryFlag } from '@/shared/lib'; + +export const TeamPhone: FC = ({ + team, + user, + isOpenModal, + handleJoin, + handleClose, +}) => { + return ( + <> + + + + + + + + + + + + + {team.name} + + + + + {capitalize(team?.type)} Type, {team?.country} + + + + + + + + Tournaments: 0 + + + Wins: {team?.wins} + + + Points: {team.points} + + + + + + {team?.description && ( + + {team.description} + + )} + + + + + + + + {team?.leader?.username} + + + + + {team?.leader?.concentration} + + + + {team.members?.map((teammate, index) => ( + + + + + {teammate?.username} + + + + {teammate?.concentration} + + + + ))} + + + + + ); +}; diff --git a/client/src/widgets/modals/info-modal/team/team.tsx b/client/src/widgets/modals/info-modal/team/team.tsx new file mode 100644 index 000000000..4d0a96a52 --- /dev/null +++ b/client/src/widgets/modals/info-modal/team/team.tsx @@ -0,0 +1,31 @@ +import { FC } from 'react'; +import { InfoModalTeamProps } from './interfaces'; +import { useGetScreenWidth } from '@/shared/lib'; +import { TeamDesktop } from '../team/desktop/desktop'; +import { TeamPhone } from '../team/phone/phone'; +export const TeamInfoModal: FC = props => { + const { team, user, isOpenModal, handleClose, handleJoin } = props; + const width = useGetScreenWidth(); + + return ( + <> + {width > 600 ? ( + + ) : ( + + )} + + ); +}; diff --git a/client/src/widgets/modals/info-modal/user/desktop/desktop.module.scss b/client/src/widgets/modals/info-modal/user/desktop/desktop.module.scss new file mode 100644 index 000000000..8e745082d --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/desktop/desktop.module.scss @@ -0,0 +1,6 @@ +.grid_container { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-row-gap: 10px; + grid-column-gap: 8px; +} diff --git a/client/src/widgets/modals/info-modal/user/desktop/desktop.stories.tsx b/client/src/widgets/modals/info-modal/user/desktop/desktop.stories.tsx new file mode 100644 index 000000000..8723d1834 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/desktop/desktop.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta } from '@storybook/react'; +import { UserDesktop } from './desktop'; +import { Button } from '@/shared/ui'; +import { userResponseFixture } from '@/shared/fixtures/user'; +import { useState } from 'react'; + +const meta: Meta = { + title: 'widgets/modals/info/user/desktop', + component: UserDesktop, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +export const InfoModalUser_desktop = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + return ( +
+ + +
+ ); +}; diff --git a/client/src/widgets/modals/info-modal/user/desktop/desktop.tsx b/client/src/widgets/modals/info-modal/user/desktop/desktop.tsx new file mode 100644 index 000000000..cd04ca016 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/desktop/desktop.tsx @@ -0,0 +1,106 @@ +import { + BadgeFramework, + BadgeLanguage, + Button, + Modal, + Typography, + Flex, + ImageLoader, +} from '@/shared/ui'; +import styles from './desktop.module.scss'; +import { FC } from 'react'; +import { ArrowRight, UserPlus, ChatCircleDots } from '@/shared/assets'; +import { calculateAge, getCountryFlag } from '@/shared/lib'; +import { InfoModalUserProps } from '../interfaces'; + +export const UserDesktop: FC = ({ user, isOpenModal, handleClose }) => { + const age = user?.dateOfBirth ? calculateAge(user.dateOfBirth) : null; + const showInviteButton = () => { + if (user?.team) { + if (!user.team.members?.some(member => member.id === user.id)) { + return true; + } + } + + return false; + }; + return ( + <> + + + + + + + + {user?.fullName.split(' ')[0]}, {age} + + + + + + + {user?.concentration} + + + {user?.experience} of experience + + + + + {user?.description && ( + + {user?.description} + + )} + {user?.frameworks && ( +
+ {user?.frameworks.map((framework, index) => ( + + ))} +
+ )} + {user?.programmingLanguages && ( + + {user?.programmingLanguages.map((language, index) => ( + + ))} + + )} + + + {showInviteButton() && ( + + )} + + + + + + +
+
+ + ); +}; diff --git a/client/src/widgets/modals/info-modal/user/index.ts b/client/src/widgets/modals/info-modal/user/index.ts new file mode 100644 index 000000000..daa7d6468 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/index.ts @@ -0,0 +1 @@ +export { UserInfoModal } from './user'; diff --git a/client/src/widgets/modals/info-modal/user/interfaces/index.ts b/client/src/widgets/modals/info-modal/user/interfaces/index.ts new file mode 100644 index 000000000..3d69d3f1a --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/interfaces/index.ts @@ -0,0 +1 @@ +export { type InfoModalUserProps } from './info-modal-user-interface'; diff --git a/client/src/widgets/modals/info-modal/user/interfaces/info-modal-user-interface.ts b/client/src/widgets/modals/info-modal/user/interfaces/info-modal-user-interface.ts new file mode 100644 index 000000000..5079acbcd --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/interfaces/info-modal-user-interface.ts @@ -0,0 +1,7 @@ +import { IUserResponse } from '@teameights/types'; + +export interface InfoModalUserProps { + user: IUserResponse; + isOpenModal: boolean; + handleClose: () => void; +} diff --git a/client/src/widgets/modals/info-modal/user/phone/phone.module.scss b/client/src/widgets/modals/info-modal/user/phone/phone.module.scss new file mode 100644 index 000000000..eb133fcc7 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/phone/phone.module.scss @@ -0,0 +1,10 @@ +.grid_container { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-row-gap: 10px; + grid-column-gap: 8px; +} + +.container { + overflow: auto; +} diff --git a/client/src/widgets/modals/info-modal/user/phone/phone.stories.tsx b/client/src/widgets/modals/info-modal/user/phone/phone.stories.tsx new file mode 100644 index 000000000..da8e1d689 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/phone/phone.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta } from '@storybook/react'; +import { UserPhone } from './phone'; +import { useState } from 'react'; +import { Button } from '@/shared/ui'; +import { userResponseFixture } from '@/shared/fixtures/user'; + +const meta: Meta = { + title: 'widgets/modals/info/user/phone', + component: UserPhone, + tags: ['autodocs'], + argTypes: {}, +}; + +export default meta; + +export const InfoModalUser_desktop = () => { + const [openModal, setOpenModal] = useState(false); + const openModalNew = () => { + setOpenModal(true); + }; + const closeModalNew = () => { + setOpenModal(false); + }; + return ( +
+ + +
+ ); +}; diff --git a/client/src/widgets/modals/info-modal/user/phone/phone.tsx b/client/src/widgets/modals/info-modal/user/phone/phone.tsx new file mode 100644 index 000000000..dd5c2ae7a --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/phone/phone.tsx @@ -0,0 +1,109 @@ +import { ArrowLeft, ArrowRight } from '@/shared/assets'; +import { BadgeFramework, BadgeLanguage, Button, Drawer, Flex, Typography } from '@/shared/ui'; +import { FC } from 'react'; +import styles from './phone.module.scss'; +import { calculateAge, getCountryFlag } from '@/shared/lib'; +import { InfoModalUserProps } from '../interfaces'; +import { ImageLoader } from '@/shared/ui/image-loader/image-loader'; + +export const UserPhone: FC = ({ user, isOpenModal, handleClose }) => { + const age = user?.dateOfBirth ? calculateAge(user.dateOfBirth) : null; + const showInviteButton = () => { + if (user?.team) { + if (!user.team.members?.some(member => member.id === user.id)) { + return true; + } + } + + return false; + }; + + return ( + <> + + + + + + + + + + + + + + {user?.fullName.split(' ')[0]}, {age} + + + + + + {user?.concentration} + + + {user?.experience} + + + + + + + {showInviteButton() && ( + + )} + + + + + {user?.description} + + +
+ {user?.frameworks && ( + <> + {user?.frameworks.map((framework: string, index: number) => ( + + ))} + + )} +
+ + {user?.programmingLanguages?.map((language: string, index: number) => ( + + ))} + +
+
+
+ + ); +}; diff --git a/client/src/widgets/modals/info-modal/user/user.tsx b/client/src/widgets/modals/info-modal/user/user.tsx new file mode 100644 index 000000000..51f50f4e4 --- /dev/null +++ b/client/src/widgets/modals/info-modal/user/user.tsx @@ -0,0 +1,20 @@ +import { FC } from 'react'; +import { InfoModalUserProps } from './interfaces'; +import { useGetScreenWidth } from '@/shared/lib'; +import { UserPhone } from './phone/phone'; +import { UserDesktop } from './desktop/desktop'; + +export const UserInfoModal: FC = props => { + const { user, isOpenModal, handleClose } = props; + const width = useGetScreenWidth(); + + return ( + <> + {width > 600 ? ( + + ) : ( + + )} + + ); +}; diff --git a/client/yarn.lock b/client/yarn.lock index fe566f228..5a3acc468 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1634,6 +1634,26 @@ __metadata: languageName: node linkType: hard +"@bedrock-layout/use-forwarded-ref@npm:^1.3.1": + version: 1.6.1 + resolution: "@bedrock-layout/use-forwarded-ref@npm:1.6.1" + dependencies: + "@bedrock-layout/use-stateful-ref": ^1.4.1 + peerDependencies: + react: ^16.8 || ^17 || ^18 + checksum: 5740618af79e54ff5eef71965807c28a87769dae5e032c93d401f87d4a140814605af0198c97d72a1487b3fdd17a762e0a0c8f9da6eec89c68dd8852043fb6ec + languageName: node + linkType: hard + +"@bedrock-layout/use-stateful-ref@npm:^1.4.1": + version: 1.4.1 + resolution: "@bedrock-layout/use-stateful-ref@npm:1.4.1" + peerDependencies: + react: ^16.8 || ^17 || ^18 + checksum: 6523e54908d7cb264f235d7b787fc08478fd516052dbe63482ee89943da79f7e136c2cb2ee3283310a8549cf61b4963bac8e3e127e2c8faabf434543762c4278 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -5137,12 +5157,12 @@ __metadata: languageName: node linkType: hard -"@teameights/types@npm:^1.1.14": - version: 1.1.14 - resolution: "@teameights/types@npm:1.1.14" +"@teameights/types@npm:^1.1.17": + version: 1.1.17 + resolution: "@teameights/types@npm:1.1.17" dependencies: esno: ^0.17.0 - checksum: 30a6994dd840a1409a05a40a3009c705e34206a9cb4714c1468e0525d8132b02a8c616468a0c3dd260915edea8d57a03bd82e36bcf59fd5bae88d3d29aac4a67 + checksum: 7ba9922ee457604eaf56069ff2b74e2740efbcae4c9d2303a06eacdef531f955760a01ed6f56fedfa61dd266192594792d22e625d874367f61f08f2be2e235b0 languageName: node linkType: hard @@ -7114,6 +7134,13 @@ __metadata: languageName: node linkType: hard +"body-scroll-lock@npm:^3.1.5": + version: 3.1.5 + resolution: "body-scroll-lock@npm:3.1.5" + checksum: 52c25b81d6e7a87cfbdd7870363c3e20a439dbc76535b735916e2df907abbce95c0da0d2c7a2cfe88f49775d53bbc4bd09445ffda2d61b999efa3f0b1dedfc5e + languageName: node + linkType: hard + "boolbase@npm:^1.0.0": version: 1.0.0 resolution: "boolbase@npm:1.0.0" @@ -7572,7 +7599,7 @@ __metadata: languageName: node linkType: hard -"classnames@npm:^2.3.0": +"classnames@npm:^2.3.0, classnames@npm:^2.3.1": version: 2.3.2 resolution: "classnames@npm:2.3.2" checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e @@ -7651,7 +7678,7 @@ __metadata: "@storybook/react": 7.2.1 "@storybook/testing-library": 0.2.0 "@tanstack/react-query": beta - "@teameights/types": ^1.1.14 + "@teameights/types": ^1.1.17 "@testing-library/jest-dom": ^6.1.3 "@testing-library/react": ^14.0.0 "@types/jest": ^29.5.5 @@ -7675,11 +7702,12 @@ __metadata: next: 13.4.12 prettier: ^3.0.1 react: 18.2.0 + react-content-loader: ^6.2.1 react-dom: 18.2.0 react-hook-form: ^7.45.4 - react-modal: ^3.16.1 react-modern-drawer: ^1.2.2 react-particles: ^2.12.2 + react-responsive-modal: ^6.4.2 react-select: ^5.7.4 react-tooltip: ^5.21.3 sass: ^1.64.2 @@ -9545,13 +9573,6 @@ __metadata: languageName: node linkType: hard -"exenv@npm:^1.2.0": - version: 1.2.2 - resolution: "exenv@npm:1.2.2" - checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -14657,6 +14678,15 @@ __metadata: languageName: node linkType: hard +"react-content-loader@npm:^6.2.1": + version: 6.2.1 + resolution: "react-content-loader@npm:6.2.1" + peerDependencies: + react: ">=16.0.0" + checksum: f777d408256a4218677e47f4cf3988d9fd8e556450e9b85ee1eb3952a5d5802573cea0df5eaf4dbc936c9522f355657de6f8ab0ecdf035d7dccdef15b45c9dae + languageName: node + linkType: hard + "react-docgen-typescript@npm:^2.2.2": version: 2.2.2 resolution: "react-docgen-typescript@npm:2.2.2" @@ -14758,28 +14788,6 @@ __metadata: languageName: node linkType: hard -"react-lifecycles-compat@npm:^3.0.0": - version: 3.0.4 - resolution: "react-lifecycles-compat@npm:3.0.4" - checksum: a904b0fc0a8eeb15a148c9feb7bc17cec7ef96e71188280061fc340043fd6d8ee3ff233381f0e8f95c1cf926210b2c4a31f38182c8f35ac55057e453d6df204f - languageName: node - linkType: hard - -"react-modal@npm:^3.16.1": - version: 3.16.1 - resolution: "react-modal@npm:3.16.1" - dependencies: - exenv: ^1.2.0 - prop-types: ^15.7.2 - react-lifecycles-compat: ^3.0.0 - warning: ^4.0.3 - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 - react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 - checksum: 978936e9320fad839c039b9ee4de55d40888156cb40e093615d6fbc2ff07139d5db06f14782cb7767f780bd5fb57956778669426c535ebc0068a7a03882c7e75 - languageName: node - linkType: hard - "react-modern-drawer@npm:^1.2.2": version: 1.2.2 resolution: "react-modern-drawer@npm:1.2.2" @@ -14854,6 +14862,20 @@ __metadata: languageName: node linkType: hard +"react-responsive-modal@npm:^6.4.2": + version: 6.4.2 + resolution: "react-responsive-modal@npm:6.4.2" + dependencies: + "@bedrock-layout/use-forwarded-ref": ^1.3.1 + body-scroll-lock: ^3.1.5 + classnames: ^2.3.1 + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: 4a00e13f88dc6d5929a0c69817e7369604b1cf541d11e2dc108a5d072a3d1c95ca28c3d8edf8897b3f4a3c5dbc17ccfba5b48963ed8c607edce2878c8fc42b50 + languageName: node + linkType: hard + "react-select@npm:^5.7.4": version: 5.7.4 resolution: "react-select@npm:5.7.4" @@ -17715,15 +17737,6 @@ __metadata: languageName: node linkType: hard -"warning@npm:^4.0.3": - version: 4.0.3 - resolution: "warning@npm:4.0.3" - dependencies: - loose-envify: ^1.0.0 - checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c - languageName: node - linkType: hard - "watchpack@npm:2.4.0, watchpack@npm:^2.2.0, watchpack@npm:^2.4.0": version: 2.4.0 resolution: "watchpack@npm:2.4.0"