| @@ -41,20 +41,21 @@ import ChatPage from "./pages/Chat/Chat"; | |||
| import MyOffers from "./pages/MyOffers/MyOffers"; | |||
| // import PricesPage from "./pages/Prices/PricesPage"; | |||
| import AboutPage from "./pages/About/AboutPage"; | |||
| import AuthRoute from "./components/Router/AuthRoute"; | |||
| // import PrivacyPolicyPage from "./pages/PrivacyPolicy/PrivacyPolicyPage"; | |||
| const AppRoutes = () => { | |||
| return ( | |||
| <Switch> | |||
| <Route exact path={BASE_PAGE} component={HomePage} /> | |||
| <Route exact path={LOGIN_PAGE} component={LoginPage} /> | |||
| <AuthRoute exact path={LOGIN_PAGE} component={LoginPage} /> | |||
| <Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> | |||
| <Route path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} /> | |||
| <Route path={REGISTER_PAGE} component={Register} /> | |||
| <Route path={ERROR_PAGE} component={ErrorPage} /> | |||
| <Route path={FORGOT_PASSWORD_MAIL_SENT} component={MailSent} /> | |||
| <Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | |||
| <Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} /> | |||
| <AuthRoute path={REGISTER_SUCCESSFUL_PAGE} component={RegisterSuccessful} /> | |||
| <AuthRoute path={REGISTER_PAGE} component={Register} /> | |||
| <AuthRoute path={FORGOT_PASSWORD_MAIL_SENT} component={MailSent} /> | |||
| <AuthRoute path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | |||
| <AuthRoute path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} /> | |||
| <Route path={CREATE_OFFER_PAGE} component={CreateOffer} /> | |||
| <Route path={ITEM_DETAILS_PAGE} component={ItemDetailsPage} /> | |||
| <Route path={PROFILE_PAGE} component={ProfilePage} /> | |||
| @@ -1,48 +0,0 @@ | |||
| body { | |||
| margin: 0; | |||
| -webkit-font-smoothing: antialiased; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| overflow-anchor: none; | |||
| background-color: #F1F1F1; | |||
| } | |||
| * { | |||
| box-sizing: border-box; | |||
| font-family: "DM Sans"; | |||
| } | |||
| html { | |||
| min-height: 100%; | |||
| font-size: 16px; | |||
| @include media-below($bp-xxl) { | |||
| font-size: 14px; | |||
| } | |||
| @include media-below($bp-xs) { | |||
| font-size: 13px; | |||
| } | |||
| @include media-below($bp-xxs) { | |||
| font-size: 10.5px; | |||
| } | |||
| } | |||
| html, | |||
| body, | |||
| #root { | |||
| @include flex-column; | |||
| flex: 1 0 auto; | |||
| } | |||
| input[type='search']::-webkit-search-decoration, | |||
| input[type='search']::-webkit-search-cancel-button, | |||
| input[type='search']::-webkit-search-results-button, | |||
| input[type='search']::-webkit-search-results-decoration { | |||
| -webkit-appearance: none; | |||
| } | |||
| ul { | |||
| list-style: none; | |||
| padding: 0; | |||
| } | |||
| @@ -1,7 +0,0 @@ | |||
| // @function pxToRem($target, $context: $base-font-size) { | |||
| // @return ($target / $context) * 1rem; | |||
| // } | |||
| // @function pxToRemMd($target, $context: $base-font-size-md) { | |||
| // @return calc($target / $context) * 1rem; | |||
| // } | |||
| @@ -1,81 +0,0 @@ | |||
| // @mixin desktop { | |||
| // @media (min-width: 1280px) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin desktop-lg { | |||
| // @media (min-width: 1480px) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin tablet { | |||
| // @media (max-width: 1024px) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin media-up($media) { | |||
| // @media only screen and (min-width: $media) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin media-below($media) { | |||
| // @media only screen and (max-width: #{$media - 0.02px}) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin media-between($mediaMin, $mediaMax) { | |||
| // @media screen and (min-width: $mediaMin) and (max-width: #{$mediaMax - 0.02px}) { | |||
| // @content; | |||
| // } | |||
| // } | |||
| // @mixin flex-center { | |||
| // display: flex; | |||
| // justify-content: center; | |||
| // align-items: center; | |||
| // } | |||
| // @mixin flex-column { | |||
| // display: flex; | |||
| // flex-direction: column; | |||
| // } | |||
| // @mixin button-clear { | |||
| // border: none; | |||
| // padding: 0; | |||
| // background-color: transparent; | |||
| // } | |||
| // @mixin outline-none { | |||
| // &, | |||
| // &:active, | |||
| // &:focus { | |||
| // outline: none; | |||
| // } | |||
| // } | |||
| // @mixin reset-position { | |||
| // position: relative; | |||
| // top: initial; | |||
| // left: initial; | |||
| // right: initial; | |||
| // bottom: initial; | |||
| // } | |||
| // @mixin text-ellipsis { | |||
| // white-space: nowrap; | |||
| // overflow: hidden; | |||
| // text-overflow: ellipsis; | |||
| // } | |||
| // @mixin line-clamp($lines) { | |||
| // display: -webkit-box; | |||
| // -webkit-line-clamp: $lines; | |||
| // -webkit-box-orient: vertical; | |||
| // overflow: hidden; | |||
| // } | |||
| @@ -1,244 +0,0 @@ | |||
| // Overwrite | |||
| .ais-ClearRefinements-button { | |||
| color: $grey-11; | |||
| font-size: pxToRem(14px); | |||
| letter-spacing: 0; | |||
| line-height: 1.15; | |||
| background-color: transparent; | |||
| border: none; | |||
| text-decoration: underline; | |||
| position: relative; | |||
| transition: all 0.2s; | |||
| outline: none; | |||
| cursor: pointer; | |||
| &[disabled] { | |||
| pointer-events: none; | |||
| opacity: 0.5; | |||
| cursor: auto; | |||
| } | |||
| &:hover { | |||
| color: $color-primary-light; | |||
| } | |||
| &:active { | |||
| color: $color-primary-dark; | |||
| } | |||
| } | |||
| .ais-RefinementList { | |||
| margin-bottom: pxToRem(32px); | |||
| margin-left: pxToRem(16px); | |||
| &.c-filter__refinement--closed { | |||
| display: none; | |||
| } | |||
| } | |||
| .ais-RefinementList.expanded { | |||
| .ais-RefinementList-showMore::before { | |||
| transform: rotate(180deg); | |||
| } | |||
| } | |||
| .ais-RefinementList-showMore { | |||
| color: $color-primary; | |||
| font-size: pxToRem(14px); | |||
| font-weight: 600; | |||
| letter-spacing: 0; | |||
| line-height: 1.56; | |||
| text-align: center; | |||
| background-color: transparent; | |||
| border: none; | |||
| position: relative; | |||
| margin-left: pxToRem(20px); | |||
| outline: none; | |||
| transition: all ease-in-out 0.3s; | |||
| cursor: pointer; | |||
| &[disabled] { | |||
| display: none; | |||
| } | |||
| &:hover { | |||
| color: $color-primary-light; | |||
| } | |||
| &:active { | |||
| color: $color-primary-dark; | |||
| } | |||
| &::before { | |||
| content: ''; | |||
| background-image: url('../images/chevron-down.svg'); | |||
| fill: $color-primary; | |||
| -webkit-text-stroke-color: $color-primary; | |||
| background-position: center; | |||
| width: pxToRem(20px); | |||
| height: pxToRem(20px); | |||
| position: absolute; | |||
| left: pxToRem(-22px); | |||
| transition: all 0.2s; | |||
| } | |||
| } | |||
| .ais-SearchBox { | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| margin-bottom: pxToRem(24px); | |||
| } | |||
| .ais-SearchBox-input { | |||
| border: none; | |||
| color: $blue; | |||
| font-size: pxToRem(16px); | |||
| letter-spacing: 0; | |||
| line-height: 1.5; | |||
| outline: none; | |||
| -moz-appearance: none; | |||
| -webkit-appearance: none; | |||
| flex-grow: 1; | |||
| &::placeholder { | |||
| color: $blue; | |||
| font-size: pxToRem(16px); | |||
| } | |||
| @include media-below($bp-xl) { | |||
| font-size: pxToRemMd(16px); | |||
| &::placeholder { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| } | |||
| .ais-SearchBox-form { | |||
| border: 1px solid $grey-6; | |||
| border-radius: $border-radius; | |||
| overflow: hidden; | |||
| padding: 0 pxToRem(12px); | |||
| height: pxToRem(33px); | |||
| align-items: center; | |||
| display: flex; | |||
| justify-content: space-between; | |||
| min-width: pxToRem(340px); | |||
| } | |||
| .ais-SearchBox-submit, | |||
| .ais-SearchBox-reset { | |||
| border: none; | |||
| background: transparent; | |||
| outline: none; | |||
| height: pxToRem(16px); | |||
| > svg { | |||
| color: $blue-1; | |||
| fill: $blue-1; | |||
| } | |||
| } | |||
| .ais-SearchBox-submitIcon { | |||
| width: pxToRem(14px); | |||
| height: pxToRem(14px); | |||
| color: $blue-1; | |||
| fill: $blue-1; | |||
| } | |||
| .ais-SearchBox-resetIcon { | |||
| width: pxToRem(14px); | |||
| height: pxToRem(14px); | |||
| } | |||
| .ais-SearchBox-reset { | |||
| margin-left: pxToRem(10px); | |||
| cursor: pointer; | |||
| } | |||
| .c-plaid-link { | |||
| padding: 0 !important; | |||
| background: transparent !important; | |||
| border-width: 0 !important; | |||
| border-radius: 0 !important; | |||
| box-shadow: $box-shadow !important; | |||
| &.c-plaid-link--select-card { | |||
| margin-top: pxToRem(40px); | |||
| .c-select-card__button { | |||
| margin-top: 0; | |||
| } | |||
| } | |||
| } | |||
| .ais-InfiniteHitsWrap { | |||
| min-height: pxToRem(200px); | |||
| } | |||
| .ais-Highlight-highlighted { | |||
| background: #fff1d6; | |||
| font-style: normal; | |||
| } | |||
| .acsb-trigger { | |||
| display: none !important; | |||
| visibility: hidden !important; | |||
| width: 0 !important; | |||
| height: 0 !important; | |||
| } | |||
| .ais-CurrentRefinements-list { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| > :not(:last-child) { | |||
| margin-right: pxToRem(16px); | |||
| } | |||
| } | |||
| .ais-CurrentRefinements-item { | |||
| border-radius: $border-radius; | |||
| background-color: $dark-blue; | |||
| padding: pxToRem(4px) pxToRem(8px); | |||
| flex-shrink: 0; | |||
| margin-bottom: pxToRem(16px); | |||
| } | |||
| .ais-CurrentRefinements-item-link { | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| font-weight: 600; | |||
| color: $white; | |||
| display: flex; | |||
| align-items: center; | |||
| text-decoration: none; | |||
| } | |||
| .ais-CurrentRefinements-close { | |||
| color: $white; | |||
| width: pxToRem(24px); | |||
| margin-left: pxToRem(8px); | |||
| } | |||
| .recharts-surface { | |||
| overflow: visible; | |||
| } | |||
| .recharts-cartesian-axis-tick-value { | |||
| color: #9aa1a9; | |||
| font-size: 10px; | |||
| letter-spacing: 0; | |||
| line-height: 20px; | |||
| } | |||
| .recharts-tooltip-wrapper:empty{ | |||
| display: 'none', | |||
| } | |||
| .recharts-text{ | |||
| &.recharts-pie-label-text{ | |||
| font-size: 14px; | |||
| @include media-below($bp-xl) { | |||
| font-size: 12px; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,127 +0,0 @@ | |||
| /** | |||
| * Reset | |||
| * | |||
| */ | |||
| *, | |||
| *:before, | |||
| *:after { | |||
| box-sizing: border-box; | |||
| } | |||
| *, | |||
| body, | |||
| button, | |||
| input, | |||
| textarea, | |||
| select { | |||
| text-rendering: optimizeLegibility; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| } | |||
| body, | |||
| div, | |||
| dl, | |||
| dt, | |||
| dd, | |||
| ul, | |||
| ol, | |||
| li, | |||
| h1, | |||
| h2, | |||
| h3, | |||
| h4, | |||
| h5, | |||
| h6, | |||
| pre, | |||
| form, | |||
| fieldset, | |||
| button, | |||
| input, | |||
| textarea, | |||
| p, | |||
| blockquote, | |||
| th, | |||
| td { | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| table { | |||
| border-collapse: collapse; | |||
| border-spacing: 0; | |||
| } | |||
| fieldset, | |||
| img { | |||
| border: 0; | |||
| } | |||
| address, | |||
| caption, | |||
| cite, | |||
| code, | |||
| dfn, | |||
| em, | |||
| th, | |||
| var { | |||
| font-style: normal; | |||
| font-weight: normal; | |||
| } | |||
| strong { | |||
| font-weight: 800; | |||
| } | |||
| ol, | |||
| ul { | |||
| list-style: none; | |||
| } | |||
| caption, | |||
| th { | |||
| text-align: left; | |||
| } | |||
| q:before, | |||
| q:after { | |||
| content: ''; | |||
| } | |||
| abbr, | |||
| acronym { | |||
| border: 0; | |||
| } | |||
| svg { | |||
| flex-shrink: 0; | |||
| } | |||
| textarea, | |||
| input:matches([type='email'], [type='number'], [type='password'], [type='search'], [type='tel'], [type='text'], [type='url']) { | |||
| -webkit-appearance: none; | |||
| &::-webkit-autofill, | |||
| &::-webkit-contacts-auto-fill-button, | |||
| &::-webkit-credentials-auto-fill-button { | |||
| visibility: hidden; | |||
| user-select: none; | |||
| display: none !important; | |||
| position: absolute; | |||
| } | |||
| } | |||
| input[type='number']::-webkit-inner-spin-button, | |||
| input[type='number']::-webkit-outer-spin-button { | |||
| -webkit-appearance: none; | |||
| margin: 0; | |||
| &::-webkit-autofill, | |||
| &::-webkit-contacts-auto-fill-button, | |||
| &::-webkit-credentials-auto-fill-button { | |||
| visibility: hidden; | |||
| user-select: none; | |||
| display: none !important; | |||
| position: absolute; | |||
| } | |||
| } | |||
| @@ -1,57 +0,0 @@ | |||
| body, | |||
| div, | |||
| dl, | |||
| dt, | |||
| dd, | |||
| ul, | |||
| ol, | |||
| li, | |||
| h1, | |||
| h2, | |||
| h3, | |||
| h4, | |||
| h5, | |||
| h6, | |||
| pre, | |||
| form, | |||
| fieldset, | |||
| button, | |||
| input, | |||
| textarea, | |||
| p, | |||
| blockquote, | |||
| th, | |||
| td { | |||
| font-family: $font-family; | |||
| } | |||
| p { | |||
| vertical-align: middle; | |||
| display: inline-block; | |||
| word-break: break-word; | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| @include media-below($bp-md) { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| a { | |||
| font-size: inherit; | |||
| line-height: inherit; | |||
| color: inherit; | |||
| } | |||
| strong { | |||
| font-weight: bold; | |||
| } | |||
| h1, | |||
| h2, | |||
| h3, | |||
| h4, | |||
| h5, | |||
| h6 { | |||
| font-weight: 500; | |||
| } | |||
| @@ -1,72 +0,0 @@ | |||
| $base-font-size: 16px; | |||
| $base-font-size-md: 14px; | |||
| $font-family: 'Avenir Next'; | |||
| // Colors | |||
| $color-primary: #024f86; | |||
| $color-primary-light: #024f86; | |||
| $color-primary-dark: #003246; | |||
| $yellow: #ffeac1; | |||
| $white: #ffffff; | |||
| $grey: #f4f4f4; | |||
| $grey-1: #ccced0; | |||
| $grey-2: #fafafa; | |||
| $grey-3: #c2c5c6; | |||
| $grey-4: #d8d8d8; | |||
| $grey-5: #808285; | |||
| $grey-6: #c9d6db; | |||
| $grey-7: rgba(128, 130, 133, 0.5); | |||
| $grey-8: rgba(201, 214, 219, 0.25); | |||
| $grey-9: #ebeff2; | |||
| $grey-10: #f0f5f6; | |||
| $grey-11: #8b8b8b; | |||
| $grey-12: #b0bfc540; | |||
| $grey-13: #9d9ea4; | |||
| $grey-14: #f7fafa; | |||
| $grey-15: #ebf2f2; | |||
| $dark-blue: #003246; | |||
| $blue: #4e7a8c; | |||
| $blue-1: #6e8fae; | |||
| $blue-2: #024f86; | |||
| $blue-3: #0f85ec; | |||
| $blue-4: #5c7e9f; | |||
| $blue-5: #dde5e7; | |||
| $black: #000; | |||
| $black-1: rgba(0, 0, 0, 0.3); | |||
| $black-2: rgba(32, 38, 43, 0.9); | |||
| $black-4: #172029; | |||
| $black-5: #272727; | |||
| $black-6: #1d2731; | |||
| $red: #ff5028; | |||
| $success: #09846b; | |||
| $success-1: #00876a; | |||
| $success-2: #008a68; | |||
| // Shadow | |||
| $button-shadow-hover: 0 5px 6px 0 rgba(112, 120, 135, 0.24); | |||
| $button-shadow-pressed: 0 2px 4px 0 rgba(112, 120, 135, 0.24); | |||
| $box-shadow: 0 3px 8px 0 rgba(112, 120, 135, 0.24); | |||
| $account-dropdown-shadow: 0 6px 38px 0 rgba(112, 120, 135, 0.56); | |||
| // Border Radius | |||
| $border-radius: 4px; | |||
| $border-radius-md: 8px; | |||
| // Breakpoints | |||
| $bp-xxs: 325px; | |||
| $bp-xs: 400px; | |||
| $bp-sm: 576px; | |||
| $bp-md: 768px; | |||
| $bp-lg: 992px; | |||
| $bp-xl: 1200px; | |||
| $bp-xxl: 1350px; | |||
| // z-index | |||
| $index-xxs: 1; | |||
| $index-xs: 2; | |||
| $index-sm: 3; | |||
| $index-md: 4; | |||
| $index-lg: 5; | |||
| $index-xl: 6; | |||
| $index-xxl: 7; | |||
| @@ -1,60 +0,0 @@ | |||
| .c-button { | |||
| display: inline-flex; | |||
| align-items: center; | |||
| border-radius: $border-radius; | |||
| background-color: $color-primary; | |||
| box-shadow: 0 2px 4px 0 rgba(112, 120, 135, 0.24); | |||
| border: transparent; | |||
| padding: 8px 0; | |||
| color: $white; | |||
| width: 100%; | |||
| text-align: center; | |||
| justify-content: center; | |||
| font-family: "Avenir Next"; | |||
| font-size: pxToRem(18px); | |||
| font-weight: 600; | |||
| letter-spacing: 0; | |||
| line-height: 26px; | |||
| outline: none; | |||
| text-transform: uppercase; | |||
| transition: all 0.3s ease-in-out; | |||
| cursor: pointer; | |||
| &.c-button--clean { | |||
| background: transparent; | |||
| border: 1px solid $color-primary; | |||
| color: $color-primary; | |||
| &:hover { | |||
| border-color: $color-primary-light; | |||
| color: $color-primary-light; | |||
| background-color: transparent; | |||
| } | |||
| &:active { | |||
| border-color: $color-primary-dark; | |||
| color: $color-primary-dark; | |||
| } | |||
| } | |||
| &.c-button--dropdown { | |||
| justify-content: flex-end; | |||
| padding: 8px 14px; | |||
| background-image: url("../../images/down.svg"); | |||
| background-repeat: no-repeat; | |||
| background-position: 8% 50%; | |||
| } | |||
| &[disabled] { | |||
| pointer-events: none; | |||
| opacity: 0.5; | |||
| } | |||
| &:hover { | |||
| background-color: $color-primary-light; | |||
| } | |||
| &:active { | |||
| background-color: $color-primary-dark; | |||
| } | |||
| } | |||
| @@ -1,45 +0,0 @@ | |||
| .c-auth-card { | |||
| max-width: pxToRem(624px); | |||
| width: 100%; | |||
| box-shadow: $box-shadow; | |||
| border-radius: $border-radius; | |||
| border: 1px solid $color-primary-light; | |||
| padding: pxToRem(24px) pxToRem(40px) pxToRem(32px); | |||
| @include media-below($bp-md) { | |||
| border: none; | |||
| box-shadow: none; | |||
| padding: 0; | |||
| max-width: 100%; | |||
| .c-auth-card__title { | |||
| text-align: left; | |||
| font-size: pxToRemMd(36px); | |||
| margin-bottom: pxToRemMd(6px); | |||
| } | |||
| .c-auth-card__subtitle { | |||
| font-size: pxToRemMd(16px); | |||
| text-align: left; | |||
| } | |||
| } | |||
| } | |||
| .c-auth-card__title { | |||
| text-align: left; | |||
| font-size: pxToRem(36px); | |||
| line-height: 1.22; | |||
| color: $dark-blue; | |||
| font-weight: 400; | |||
| margin-bottom: pxToRem(16px); | |||
| } | |||
| .c-auth-card__subtitle { | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| letter-spacing: 0; | |||
| color: $color-primary; | |||
| text-align: left; | |||
| width: 100%; | |||
| font-weight: 600; | |||
| } | |||
| @@ -1,23 +0,0 @@ | |||
| .c-auth { | |||
| @include flex-center; | |||
| flex-direction: column; | |||
| padding-bottom: pxToRem(56px); | |||
| @include media-below($bp-md) { | |||
| padding: 0 pxToRemMd(24px) pxToRemMd(92px); | |||
| .c-auth__title { | |||
| margin: pxToRemMd(48px) auto; | |||
| font-size: pxToRemMd(24px); | |||
| line-height: 1.35; | |||
| } | |||
| } | |||
| } | |||
| .c-auth__title { | |||
| margin: pxToRem(56px) auto pxToRem(80px); | |||
| font-size: pxToRem(36px); | |||
| line-height: 1.22; | |||
| color: $dark-blue; | |||
| font-weight: bold; | |||
| } | |||
| @@ -1,173 +0,0 @@ | |||
| .c-btn { | |||
| @include outline-none; | |||
| @include button-clear; | |||
| @include flex-center; | |||
| font-size: pxToRem(18px); | |||
| line-height: 1.35; | |||
| padding: pxToRem(8px) pxToRem(8px); | |||
| border-radius: $border-radius; | |||
| box-shadow: $button-shadow-pressed; | |||
| color: inherit; | |||
| font-weight: 600; | |||
| letter-spacing: 0; | |||
| text-align: center; | |||
| text-transform: uppercase; | |||
| user-select: none; | |||
| white-space: nowrap; | |||
| min-width: pxToRem(120px); | |||
| flex-shrink: 0; | |||
| cursor: pointer; | |||
| transition: background-color 0.2s, color 0.2s; | |||
| &:disabled { | |||
| opacity: 0.5; | |||
| cursor: auto; | |||
| } | |||
| &.c-btn--primary { | |||
| background-color: $color-primary; | |||
| color: $white; | |||
| border: 1px solid $color-primary; | |||
| &:disabled { | |||
| &:hover { | |||
| background-color: $color-primary; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| &:hover { | |||
| background-color: $color-primary-light; | |||
| box-shadow: $button-shadow-hover; | |||
| } | |||
| &:focus, | |||
| &:active { | |||
| background-color: $color-primary-dark; | |||
| box-shadow: $button-shadow-pressed; | |||
| } | |||
| } | |||
| &.c-btn--primary-outlined { | |||
| background-color: transparent; | |||
| color: $color-primary; | |||
| border: 1px solid $color-primary; | |||
| &:disabled { | |||
| &:hover { | |||
| color: $color-primary; | |||
| border: 1px solid $color-primary; | |||
| } | |||
| } | |||
| &:hover { | |||
| color: $color-primary; | |||
| border: 1px solid $color-primary; | |||
| } | |||
| &:focus, | |||
| &:active { | |||
| color: $color-primary; | |||
| border: 1px solid $color-primary; | |||
| } | |||
| } | |||
| &.c-btn--blue { | |||
| background-color: $blue-3; | |||
| color: $white; | |||
| background-color: $blue-3; | |||
| } | |||
| &.c-btn--white { | |||
| background-color: $white; | |||
| color: $grey-3; | |||
| border: 1px solid $grey-4; | |||
| box-shadow: $box-shadow; | |||
| &:disabled { | |||
| &:hover { | |||
| background-color: $white; | |||
| color: $grey-3; | |||
| } | |||
| } | |||
| &:hover { | |||
| color: $grey-5; | |||
| } | |||
| &:focus, | |||
| &:active { | |||
| background-color: $grey; | |||
| } | |||
| } | |||
| &.c-btn--primary-clear { | |||
| background-color: transparent; | |||
| color: $color-primary; | |||
| border: none; | |||
| box-shadow: none; | |||
| padding: 0; | |||
| } | |||
| &.c-btn--auto { | |||
| min-width: auto; | |||
| } | |||
| &.c-btn--sm { | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| padding: pxToRem(4px) pxToRem(15px); | |||
| } | |||
| &.c-btn--capitalize { | |||
| text-transform: capitalize; | |||
| } | |||
| &.c-btn--bank-acount-card { | |||
| padding: 0 pxToRem(16px); | |||
| min-height: pxToRem(32px); | |||
| min-width: pxToRem(120px); | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| } | |||
| &.c-btn--hidden { | |||
| visibility: hidden; | |||
| height: 0; | |||
| } | |||
| @include media-below($bp-md) { | |||
| padding: pxToRemMd(4px) pxToRemMd(25px); | |||
| font-size: pxToRemMd(16px); | |||
| line-height: 1.5; | |||
| min-width: pxToRemMd(80px); | |||
| &.c-btn--auth { | |||
| padding: pxToRemMd(12px) pxToRemMd(25px); | |||
| line-height: 1.35; | |||
| font-size: pxToRemMd(18px); | |||
| } | |||
| &.c-btn--sm { | |||
| padding: pxToRemMd(4px) pxToRemMd(15px); | |||
| } | |||
| &.c-btn--bank-acount-card { | |||
| flex-grow: 1; | |||
| min-height: pxToRemMd(40px); | |||
| padding: pxToRemMd(8px) pxToRemMd(16px); | |||
| font-size: pxToRemMd(18px); | |||
| line-height: 1.33; | |||
| } | |||
| &.c-btn--lg { | |||
| padding: pxToRemMd(7.5px) pxToRemMd(15px); | |||
| font-size: pxToRemMd(18px); | |||
| line-height: 1.5; | |||
| } | |||
| } | |||
| @include media-below($bp-xs) { | |||
| white-space: unset; | |||
| } | |||
| } | |||
| @@ -1,46 +0,0 @@ | |||
| .c-error-page { | |||
| margin-top: pxToRem(120px); | |||
| @include media-below($bp-md) { | |||
| margin-top: pxToRemMd(120px); | |||
| .c-error-page__title { | |||
| font-size: pxToRemMd(160px); | |||
| margin-bottom: pxToRemMd(27px); | |||
| } | |||
| .c-error-page__text { | |||
| margin-bottom: pxToRem(24px); | |||
| } | |||
| } | |||
| } | |||
| .c-error-page__content-container { | |||
| @include flex-center; | |||
| } | |||
| .c-error-page__content { | |||
| @include flex-column; | |||
| align-items: center; | |||
| padding: 0 pxToRem(32px); | |||
| } | |||
| .c-error-page__title { | |||
| font-size: pxToRem(160px); | |||
| line-height: 1.35; | |||
| color: $dark-blue; | |||
| margin-bottom: pxToRem(32px); | |||
| color: $color-primary; | |||
| font-weight: bold; | |||
| } | |||
| .c-error-page__text { | |||
| font-weight: 600; | |||
| margin-bottom: pxToRem(24px); | |||
| text-align: center; | |||
| } | |||
| .c-error-page__button { | |||
| margin-bottom: pxToRem(16px); | |||
| min-width: pxToRem(250px); | |||
| } | |||
| @@ -1,23 +0,0 @@ | |||
| .c-reset-security { | |||
| padding-top: pxToRem(56px); | |||
| @include media-below($bp-md) { | |||
| padding-top: pxToRemMd(40px); | |||
| .c-reset-security__button { | |||
| width: 100%; | |||
| margin-top: pxToRemMd(44px); | |||
| } | |||
| } | |||
| } | |||
| .c-reset-security__question { | |||
| color: $dark-blue; | |||
| font-weight: 600; | |||
| margin-bottom: pxToRem(20px); | |||
| } | |||
| .c-reset-security__button { | |||
| width: 100%; | |||
| margin-top: pxToRem(48px); | |||
| } | |||
| @@ -1,7 +0,0 @@ | |||
| .c-icon-button { | |||
| @include flex-center; | |||
| @include outline-none; | |||
| @include button-clear; | |||
| user-select: none; | |||
| cursor: pointer; | |||
| } | |||
| @@ -1,479 +0,0 @@ | |||
| .c-input { | |||
| @include flex-column; | |||
| position: relative; | |||
| &.c-input--error { | |||
| .c-input__field, | |||
| .c-select__control, | |||
| .c-select__control:hover { | |||
| border-color: $red; | |||
| } | |||
| } | |||
| &.c-input--password { | |||
| .c-input__icon { | |||
| position: absolute; | |||
| right: 0; | |||
| top: 50%; | |||
| transform: translate(0, -50%); | |||
| margin-right: pxToRem(12px); | |||
| width: pxToRem(24px); | |||
| height: pxToRem(24px); | |||
| display: flex; | |||
| svg { | |||
| width: pxToRem(24px); | |||
| height: pxToRem(24px); | |||
| color: $black; | |||
| } | |||
| } | |||
| .c-input__caps-lock { | |||
| position: absolute; | |||
| right: 0; | |||
| top: 50%; | |||
| transform: translate(0, -50%); | |||
| margin-right: pxToRem(40px); | |||
| width: pxToRem(24px); | |||
| height: pxToRem(24px); | |||
| display: flex; | |||
| width: pxToRem(24px); | |||
| height: pxToRem(24px); | |||
| color: $black; | |||
| } | |||
| .c-input__field { | |||
| padding-right: pxToRem(72px); | |||
| } | |||
| } | |||
| &.c-input--demi-bold { | |||
| .c-input__field { | |||
| font-weight: 600; | |||
| } | |||
| } | |||
| &.c-input--search { | |||
| position: relative; | |||
| width: 100%; | |||
| .c-input__icon { | |||
| width: pxToRem(24px); | |||
| height: pxToRem(24px); | |||
| position: absolute; | |||
| right: 0; | |||
| top: 50%; | |||
| transform: translate(0, -50%); | |||
| color: $blue-1; | |||
| margin-right: pxToRem(12px); | |||
| } | |||
| &.c-input--search-management { | |||
| max-width: pxToRem(344px); | |||
| margin-right: pxToRem(24px); | |||
| .c-input__field { | |||
| height: pxToRem(34px); | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.5; | |||
| letter-spacing: 0; | |||
| } | |||
| } | |||
| } | |||
| &.c-input--center-text { | |||
| input { | |||
| text-align: center; | |||
| } | |||
| } | |||
| @include media-below($bp-xl) { | |||
| &.c-input--search { | |||
| &.c-input--search-management { | |||
| max-width: 100%; | |||
| margin-right: pxToRemMd(16px); | |||
| .c-input__field { | |||
| height: pxToRemMd(32px); | |||
| font-size: pxToRemMd(16px); | |||
| line-height: 1.5; | |||
| letter-spacing: 0; | |||
| } | |||
| } | |||
| } | |||
| .c-input__label { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| .c-input__field { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| .c-input__error { | |||
| font-size: pxToRemMd(14px); | |||
| } | |||
| .c-select__control { | |||
| &.c-select__control { | |||
| font-size: pxToRemMd(16px); | |||
| min-height: 0; | |||
| .c-select__input, | |||
| .c-select__placeholder { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| .c-select__indicator { | |||
| > svg { | |||
| width: pxToRemMd(16px); | |||
| height: pxToRemMd(16px); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .c-select__menu { | |||
| .c-select__option, | |||
| .c-select__menu-notice { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| .c-input__link { | |||
| a, | |||
| span { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| //Overide | |||
| .c-password-strength__container { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| .c-phone-number { | |||
| .PhoneInput { | |||
| font-size: pxToRemMd(16px); | |||
| &::placeholder { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| .PhoneInputInput { | |||
| font-size: pxToRemMd(16px); | |||
| } | |||
| } | |||
| } | |||
| &.c-input--dropdown-full-height { | |||
| .c-select__menu { | |||
| max-height: initial; | |||
| } | |||
| } | |||
| } | |||
| .c-input__label { | |||
| color: $blue; | |||
| font-size: pxToRem(16px); | |||
| font-weight: 600; | |||
| letter-spacing: 0; | |||
| line-height: 1.75; | |||
| margin-bottom: pxToRem(4px); | |||
| } | |||
| .c-input__field-wrap { | |||
| width: 100%; | |||
| position: relative; | |||
| } | |||
| .c-input__field { | |||
| @include outline-none; | |||
| border: 1px solid $grey-6; | |||
| border-radius: $border-radius; | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| height: pxToRem(50px); | |||
| padding: 0 pxToRem(12px); | |||
| color: $blue; | |||
| background-color: $white; | |||
| width: 100%; | |||
| &:disabled { | |||
| background-color: $grey-8; | |||
| border-color: $grey-6; | |||
| } | |||
| &:focus { | |||
| border-color: $color-primary; | |||
| } | |||
| } | |||
| .c-input__error { | |||
| position: absolute; | |||
| top: 100%; | |||
| left: 0; | |||
| right: 0; | |||
| color: $red; | |||
| font-size: pxToRem(14px); | |||
| line-height: 1.35; | |||
| font-weight: 500; | |||
| margin: pxToRem(4px) 0; | |||
| } | |||
| .c-select__control { | |||
| &.c-select__control { | |||
| @include outline-none; | |||
| border: 1px solid $grey-6; | |||
| border-radius: $border-radius; | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| height: pxToRem(50px); | |||
| padding: 0 pxToRem(12px); | |||
| color: $blue; | |||
| background-color: $white; | |||
| box-shadow: none; | |||
| &:hover { | |||
| border-color: $grey-6; | |||
| } | |||
| &.c-select__control--is-focused { | |||
| border-color: $color-primary; | |||
| box-shadow: none; | |||
| &:hover { | |||
| border-color: $color-primary; | |||
| } | |||
| &.c-select__control--menu-is-open{ | |||
| .c-select__indicator { | |||
| svg { | |||
| transform: rotate(-180deg); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .css-1uccc91-singleValue { | |||
| color: $blue; | |||
| margin: 0; | |||
| } | |||
| .css-b8ldur-Input { | |||
| margin: 0; | |||
| } | |||
| .c-select__value-container { | |||
| height: 100%; | |||
| padding: 0; | |||
| padding-right: pxToRem(32px); | |||
| } | |||
| .c-select__input, | |||
| .c-select__placeholder { | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| letter-spacing: 0; | |||
| color: $blue; | |||
| } | |||
| .c-select__indicator-separator { | |||
| display: none; | |||
| } | |||
| .c-select__indicator { | |||
| padding: 0; | |||
| > svg { | |||
| width: pxToRem(16px); | |||
| height: pxToRem(16px); | |||
| color: $blue; | |||
| transform: rotate(0); | |||
| transition: transform 0.2s; | |||
| } | |||
| } | |||
| &.c-select__control--is-disabled { | |||
| background-color: $grey-8; | |||
| } | |||
| } | |||
| } | |||
| .c-select__menu { | |||
| @include flex-column; | |||
| position: absolute; | |||
| top: 100%; | |||
| left: 0; | |||
| right: 0; | |||
| margin-top: pxToRem(4px); | |||
| margin-bottom: pxToRem(4px); | |||
| border: 1px solid $grey-6; | |||
| border-radius: $border-radius; | |||
| box-shadow: $box-shadow; | |||
| max-height: pxToRem(150px); | |||
| overflow: auto; | |||
| .c-select__menu-list { | |||
| @include flex-column; | |||
| padding: 0; | |||
| flex-grow: 1; | |||
| } | |||
| .c-select__option, | |||
| .c-select__menu-notice { | |||
| padding: pxToRem(12px) pxToRem(15px); | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| letter-spacing: 0; | |||
| color: $blue; | |||
| text-align: left; | |||
| &:hover { | |||
| background-color: $grey-2; | |||
| } | |||
| &.c-select__option--is-selected { | |||
| background-color: $grey-2; | |||
| } | |||
| &.c-select__option--is-focused { | |||
| background-color: $grey-2; | |||
| } | |||
| } | |||
| } | |||
| .c-input__link { | |||
| position: absolute; | |||
| top: 0; | |||
| right: 0; | |||
| a, | |||
| span { | |||
| color: $grey-11; | |||
| font-size: pxToRem(16px); | |||
| letter-spacing: 0; | |||
| line-height: 1.15; | |||
| text-decoration: underline; | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| //Overide | |||
| .c-password-strength__container { | |||
| margin-top: pxToRem(8px); | |||
| font-size: pxToRem(16px); | |||
| & .c-password-strength__line--wrapper { | |||
| border-radius: 8px; | |||
| overflow: hidden; | |||
| background-color: $grey; | |||
| height: pxToRem(5px); | |||
| .c-password-strength__line { | |||
| height: pxToRem(5px); | |||
| left: 0; | |||
| top: 0; | |||
| } | |||
| } | |||
| } | |||
| .c-password { | |||
| min-height: pxToRem(110px); | |||
| @include media-below($bp-xl) { | |||
| min-height: pxToRemMd(110px); | |||
| } | |||
| } | |||
| .c-phone-number { | |||
| .PhoneInput { | |||
| @include outline-none; | |||
| box-sizing: border-box; | |||
| border: 1px solid $grey-6; | |||
| border-radius: $border-radius; | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| min-height: pxToRem(50px); | |||
| color: $blue; | |||
| background-color: $white; | |||
| box-shadow: none; | |||
| width: 100%; | |||
| overflow: hidden; | |||
| &::placeholder { | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| } | |||
| &:disabled { | |||
| background-color: $grey-8; | |||
| border-color: $grey-6; | |||
| } | |||
| &.PhoneInput--focus { | |||
| border-color: $color-primary; | |||
| .PhoneInputCountry { | |||
| border-color: $color-primary; | |||
| } | |||
| } | |||
| } | |||
| .PhoneInputCountry { | |||
| @include flex-center; | |||
| width: pxToRem(96px); | |||
| border-right: 1px solid $grey-6; | |||
| } | |||
| .PhoneInputCountryIcon { | |||
| margin-right: pxToRem(16px); | |||
| width: auto; | |||
| height: auto; | |||
| border: none; | |||
| } | |||
| .PhoneInputCountryIconImg { | |||
| width: pxToRem(36px); | |||
| object-fit: contain; | |||
| } | |||
| .PhoneInputCountrySelectArrow { | |||
| border: none; | |||
| width: 0; | |||
| height: 0; | |||
| transform: translate(0); | |||
| border-left: pxToRem(8px) solid transparent; | |||
| border-right: pxToRem(8px) solid transparent; | |||
| border-top: pxToRem(8px) solid $blue; | |||
| } | |||
| .PhoneInputInput { | |||
| @include outline-none; | |||
| border-color: transparent; | |||
| height: 100%; | |||
| font-size: pxToRem(16px); | |||
| line-height: 1.75; | |||
| padding: 0; | |||
| color: $blue; | |||
| background-color: $white; | |||
| width: 100%; | |||
| margin: 0; | |||
| padding: 0 pxToRem(26px); | |||
| height: pxToRem(50px); | |||
| } | |||
| .PhoneInputCountry { | |||
| margin-right: 0; | |||
| } | |||
| &.c-input--error { | |||
| .PhoneInput { | |||
| border-color: $red; | |||
| .PhoneInputCountry { | |||
| border-color: $red; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,72 +0,0 @@ | |||
| .c-loader__wrapper { | |||
| @include flex-column; | |||
| flex: 1 1 auto; | |||
| position: relative; | |||
| min-height: 0; | |||
| min-width: 0; | |||
| &.c-loader__wrapper--block { | |||
| box-shadow: $box-shadow; | |||
| .c-loader { | |||
| position: relative; | |||
| top: unset; | |||
| left: unset; | |||
| right: unset; | |||
| bottom: unset; | |||
| } | |||
| } | |||
| &.c-loader__wrapper--full-height { | |||
| height: 100%; | |||
| } | |||
| &.c-loader__wrapper--no-shadow { | |||
| box-shadow: none; | |||
| } | |||
| .c-loader { | |||
| @include flex-center; | |||
| width: 100%; | |||
| height: 100%; | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| right: 0; | |||
| bottom: 0; | |||
| padding: pxToRem(15px) 0; | |||
| background-color: rgba(255, 255, 255, 0.4); | |||
| z-index: $index-lg; | |||
| &.c-loader--page { | |||
| position: fixed; | |||
| .c-loader__icon { | |||
| border: 20px solid transparent; | |||
| width: pxToRem(200px); | |||
| height: pxToRem(200px); | |||
| border-bottom-color: $color-primary; | |||
| border-top-color: $color-primary; | |||
| } | |||
| } | |||
| } | |||
| .c-loader__icon { | |||
| border-radius: 50%; | |||
| border: 10px solid transparent; | |||
| border-bottom-color: $color-primary; | |||
| border-top-color: $color-primary; | |||
| animation: 1s loader-animation linear infinite; | |||
| width: pxToRem(100px); | |||
| height: pxToRem(100px); | |||
| } | |||
| @keyframes loader-animation { | |||
| 0% { | |||
| transform: rotate(0deg); | |||
| } | |||
| 100% { | |||
| transform: rotate(360deg); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,31 +0,0 @@ | |||
| .c-login-card { | |||
| border: 1px solid $color-primary-light; | |||
| border-radius: $border-radius; | |||
| box-shadow: $box-shadow; | |||
| max-width: pxToRem(624px); | |||
| width: 100%; | |||
| margin: pxToRem(28px) auto 0; | |||
| padding: pxToRem(36px) pxToRem(40px) pxToRem(32px); | |||
| } | |||
| .c-login-card__note { | |||
| color: $color-primary; | |||
| font-weight: 600; | |||
| margin-bottom: pxToRem(37px); | |||
| } | |||
| .c-login-card__form { | |||
| display: grid; | |||
| grid-row-gap: pxToRem(24px); | |||
| } | |||
| .c-login-card__submit { | |||
| margin-top: pxToRem(24px); | |||
| width: 100%; | |||
| } | |||
| .c-login-card__question { | |||
| color: $blue; | |||
| font-weight: 600; | |||
| margin-bottom: pxToRem(16px); | |||
| } | |||
| @@ -1,72 +0,0 @@ | |||
| .c-login { | |||
| &.c-login--user { | |||
| .c-login__form { | |||
| .c-input:first-child { | |||
| margin-bottom: pxToRem(20px); | |||
| } | |||
| } | |||
| } | |||
| @include media-below($bp-xl) { | |||
| .c-login__link { | |||
| margin-top: pxToRemMd(70px); | |||
| } | |||
| } | |||
| @include media-below($bp-md) { | |||
| .c-login__form { | |||
| margin: pxToRemMd(36px) 0 0; | |||
| } | |||
| .c-login__button { | |||
| margin-bottom: pxToRemMd(40px); | |||
| margin-top: pxToRemMd(36px); | |||
| } | |||
| .c-login__link { | |||
| margin-top: pxToRemMd(80px); | |||
| } | |||
| &.c-login--user { | |||
| .c-login__form { | |||
| .c-input:first-child { | |||
| margin-bottom: pxToRemMd(20px); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .c-login__link { | |||
| color: $color-primary; | |||
| font-weight: 600; | |||
| margin-top: pxToRem(40px); | |||
| width: max-content; | |||
| } | |||
| .c-login__form { | |||
| margin: pxToRem(36px) 0 0; | |||
| > form { | |||
| @include flex-column; | |||
| } | |||
| } | |||
| .c-login__button { | |||
| width: 100%; | |||
| margin-top: pxToRem(68px); | |||
| margin-bottom: pxToRem(24px); | |||
| } | |||
| .c-login__text { | |||
| text-align: center; | |||
| width: 100%; | |||
| color: $blue; | |||
| a { | |||
| color: $color-primary; | |||
| font-weight: bold; | |||
| letter-spacing: inherit; | |||
| font-size: inherit; | |||
| line-height: inherit; | |||
| } | |||
| } | |||
| @@ -1,169 +0,0 @@ | |||
| $header-height-desktop: pxToRem(80px); | |||
| $header-height-mobile: pxToRemMd(74px); | |||
| .c-modal-wrap { | |||
| position: fixed; | |||
| top: 0; | |||
| left: 0; | |||
| right: 0; | |||
| bottom: 0; | |||
| z-index: $index-xl; | |||
| background-color: $black-1; | |||
| &.c-modal-wrap--no-bg { | |||
| background-color: transparent; | |||
| } | |||
| &.c-modal-wrap--over-modal { | |||
| background-color: transparent; | |||
| z-index: $index-xxl; | |||
| } | |||
| &.c-modal-wrap--sm { | |||
| .c-modal { | |||
| max-width: pxToRem(390px); | |||
| width: 100%; | |||
| } | |||
| .c-modal__header { | |||
| padding: pxToRem(12px); | |||
| } | |||
| } | |||
| .c-modal__header { | |||
| padding: pxToRem(12px); | |||
| } | |||
| &.c-modal-wrap--md { | |||
| .c-modal { | |||
| max-width: pxToRem(521px); | |||
| width: 100%; | |||
| } | |||
| .c-modal__header { | |||
| padding: pxToRem(12px) pxToRem(20px); | |||
| } | |||
| } | |||
| &.c-modal-wrap--lg { | |||
| .c-modal { | |||
| max-width: pxToRem(782px); | |||
| width: 100%; | |||
| } | |||
| .c-modal__header { | |||
| padding: pxToRem(12px) pxToRem(20px); | |||
| } | |||
| } | |||
| &.c-modal-wrap--close { | |||
| display: none; | |||
| } | |||
| @include media-below($bp-xl) { | |||
| &, | |||
| &.c-modal-wrap--sm, | |||
| &.c-modal-wrap--md { | |||
| .c-modal { | |||
| margin: $header-height-mobile auto $header-height-mobile; | |||
| max-height: calc(100vh - #{2 * $header-height-mobile}); | |||
| } | |||
| } | |||
| } | |||
| @include media-below($bp-md) { | |||
| &, | |||
| &.c-modal-wrap--sm, | |||
| &.c-modal-wrap--md { | |||
| .c-modal__header { | |||
| padding: pxToRemMd(16px); | |||
| } | |||
| .c-modal__title { | |||
| font-size: pxToRemMd(16px); | |||
| line-height: 1.4; | |||
| } | |||
| .c-modal__close { | |||
| width: pxToRemMd(24px); | |||
| height: pxToRemMd(24px); | |||
| } | |||
| .c-modal__back { | |||
| width: pxToRemMd(24px); | |||
| height: pxToRemMd(24px); | |||
| margin-right: pxToRemMd(8px); | |||
| } | |||
| .c-modal__back-button { | |||
| margin-left: -#{pxToRemMd(8px)}; | |||
| } | |||
| .c-modal, | |||
| &.c-modal-wrap--lg .c-modal { | |||
| max-width: 100%; | |||
| max-height: 100vh; | |||
| height: 100%; | |||
| margin: 0; | |||
| border-radius: 0; | |||
| } | |||
| &.c-modal-wrap--mobile-modal { | |||
| display: flex; | |||
| padding: 0 pxToRemMd(20px); | |||
| .c-modal { | |||
| height: auto; | |||
| margin: auto; | |||
| border-radius: 2px; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .c-modal { | |||
| @include flex-column; | |||
| box-shadow: $box-shadow; | |||
| border-radius: $border-radius; | |||
| background-color: $white; | |||
| margin: $header-height-desktop auto $header-height-desktop; | |||
| max-height: calc(100vh - #{2 * $header-height-desktop}); | |||
| position: relative; | |||
| } | |||
| .c-modal__header { | |||
| display: flex; | |||
| align-items: center; | |||
| box-shadow: $box-shadow; | |||
| z-index: $index-xxs; | |||
| } | |||
| .c-modal__title { | |||
| @include text-ellipsis; | |||
| font-size: pxToRem(16px); | |||
| font-weight: 600; | |||
| line-height: 1.5; | |||
| color: $dark-blue; | |||
| padding-right: pxToRem(10px); | |||
| margin-right: auto; | |||
| } | |||
| .c-modal__close { | |||
| width: pxToRem(16px); | |||
| height: pxToRem(16px); | |||
| color: $dark-blue; | |||
| } | |||
| .c-modal__back { | |||
| width: pxToRem(16px); | |||
| height: pxToRem(16px); | |||
| color: $dark-blue; | |||
| margin-right: pxToRem(10px); | |||
| } | |||
| .c-modal__body { | |||
| @include flex-column; | |||
| flex: 1 1 auto; | |||
| overflow: auto; | |||
| } | |||
| @@ -1,29 +0,0 @@ | |||
| .c-radio { | |||
| display: flex; | |||
| cursor: pointer; | |||
| &.c-radio--selected { | |||
| border-color: $dark-blue; | |||
| } | |||
| } | |||
| .c-radio__field { | |||
| display: none; | |||
| } | |||
| .c-radio__indicator { | |||
| margin-top: pxToRem(4px); | |||
| margin-right: pxToRem(16px); | |||
| } | |||
| .c-radio__icon { | |||
| width: pxToRem(16px); | |||
| height: pxToRem(16px); | |||
| } | |||
| .c-radio__text { | |||
| font-size: pxToRem(14px); | |||
| line-height: 1.15; | |||
| color: $blue; | |||
| user-select: none; | |||
| } | |||
| @@ -3,12 +3,11 @@ import PropTypes from "prop-types"; | |||
| import { CheckOffersButtonContainer } from "./CheckOffersButton.styled"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { HOME_PAGE } from "../../../constants/pages"; | |||
| import history from "../../../store/utils/history"; | |||
| const CheckOffersButton = () => { | |||
| const { t } = useTranslation(); | |||
| const history = useHistory(); | |||
| const handleClick = () => { | |||
| history.push(HOME_PAGE); | |||
| }; | |||
| @@ -1,7 +1,7 @@ | |||
| import IconButton from "../../IconButton/IconButton"; | |||
| import { ReactComponent as DownArrow } from "../../../assets/images/svg/arrow-down.svg"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { IconButton } from "../IconButton/IconButton"; | |||
| export const ArrowIcon = styled(DownArrow)` | |||
| ${(props) => | |||
| @@ -11,6 +11,9 @@ export const ArrowIcon = styled(DownArrow)` | |||
| `} | |||
| width: 18px; | |||
| height: 18px; | |||
| position: relative; | |||
| top: 1px; | |||
| left: 1px; | |||
| & path { | |||
| ${(props) => | |||
| props.disabled && | |||
| @@ -3,92 +3,54 @@ import PropTypes from "prop-types"; | |||
| import { | |||
| ChatInfo, | |||
| ChatCardContainer, | |||
| Col, | |||
| UserImage, | |||
| UserImgWrapper, | |||
| UserName, | |||
| LastMessage, | |||
| Line, | |||
| } from "./ChatCard.styled"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import LittleOfferDetails from "./LittleOfferDetails/LittleOfferDetails"; | |||
| import MobileOfferDetails from "./MobileOfferDetails/MobileOfferDetails"; | |||
| import OfferLocation from "./OfferLocation/OfferLocation"; | |||
| import ChatCommands from "./ChatCommands/ChatCommands"; | |||
| import useIsMobile from "../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter"; | |||
| import history from "../../../store/utils/history"; | |||
| import { replaceInRoute } from "../../../util/helpers/routeHelpers"; | |||
| import { CHAT_MESSAGE_PAGE } from "../../../constants/pages"; | |||
| // Chat card is shown only on mobile phones (route /messages) | |||
| const ChatCard = (props) => { | |||
| const history = useHistory(); | |||
| const { isMobile } = useIsMobile(); | |||
| const chat = useMemo(() => { | |||
| return props.chat; | |||
| }, [props.chat]); | |||
| const lastMessage = useMemo(() => { | |||
| if (chat?.chat?.messages && chat?.chat?.messages?.length > 0) { | |||
| return chat.chat.messages[chat.chat.messages.length - 1]?.text; | |||
| } | |||
| return ""; | |||
| }, [chat]); | |||
| const routeToItem = () => { | |||
| history.push(`/messages/${chat?.chat?._id}`); | |||
| history.push( | |||
| replaceInRoute(CHAT_MESSAGE_PAGE, { | |||
| idChat: chat?.chat?._id, | |||
| }) | |||
| ); | |||
| }; | |||
| return ( | |||
| <ChatCardContainer | |||
| onClick={isMobile ? () => routeToItem(chat?.chat?._id) : () => {}} | |||
| > | |||
| <Col> | |||
| <UserImgWrapper> | |||
| {/* <UserImage src={chat?.interlocutorData?.image} /> */} | |||
| <UserImage | |||
| src={getImageUrl( | |||
| chat?.interlocutorData?.image, | |||
| variants.chatCard, | |||
| isMobile | |||
| )} | |||
| /> | |||
| </UserImgWrapper> | |||
| <ChatInfo> | |||
| <UserName>{chat?.interlocutorData?.name}</UserName> | |||
| {/* Only shows on Mobile */} | |||
| <MobileOfferDetails chat={chat} /> | |||
| {/* ^^^^^ */} | |||
| <LastMessage>{lastMessage}</LastMessage> | |||
| <OfferLocation chat={chat} /> | |||
| </ChatInfo> | |||
| </Col> | |||
| <Line /> | |||
| {/* Only shows on Desktop */} | |||
| <LittleOfferDetails chat={chat} /> | |||
| {/* ^^^^^^^ */} | |||
| <ChatCommands routeToItem={() => routeToItem(chat?.chat?._id)} /> | |||
| return ( | |||
| <ChatCardContainer onClick={routeToItem}> | |||
| <UserImgWrapper> | |||
| <UserImage | |||
| src={getImageUrl( | |||
| chat?.interlocutorData?.image, | |||
| variants.chatCard, | |||
| isMobile | |||
| )} | |||
| /> | |||
| </UserImgWrapper> | |||
| <ChatInfo> | |||
| <UserName>{chat?.interlocutorData?.name}</UserName> | |||
| <MobileOfferDetails chat={chat} /> | |||
| </ChatInfo> | |||
| </ChatCardContainer> | |||
| ); | |||
| }; | |||
| ChatCard.propTypes = { | |||
| children: PropTypes.node, | |||
| title: PropTypes.string, | |||
| description: PropTypes.string, | |||
| category: PropTypes.string, | |||
| author: PropTypes.string, | |||
| location: PropTypes.string, | |||
| image: PropTypes.node, | |||
| quantity: PropTypes.number, | |||
| package: PropTypes.string, | |||
| numberOfViews: PropTypes.number, | |||
| halfwidth: PropTypes.bool, | |||
| sponsored: PropTypes.bool, | |||
| offer: PropTypes.any, | |||
| pinned: PropTypes.bool, | |||
| vertical: PropTypes.bool, | |||
| chat: PropTypes.any, | |||
| }; | |||
| ChatCard.defaultProps = { | |||
| @@ -18,7 +18,7 @@ export const ChatCardContainer = styled(Container)` | |||
| max-width: 2000px; | |||
| height: 180px; | |||
| position: relative; | |||
| justify-content: space-between; | |||
| gap: 18px; | |||
| @media (max-width: 550px) { | |||
| max-height: 108px; | |||
| margin: 0; | |||
| @@ -55,16 +55,6 @@ export const ChatInfo = styled(Box)` | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| `; | |||
| export const Col = styled(Box)` | |||
| display: flex; | |||
| align-items: center; | |||
| flex-direction: row; | |||
| gap: 18px; | |||
| flex: 1; | |||
| @media (max-width: 600px) { | |||
| ${props => props.mobileDisappear && 'display: none;'} | |||
| } | |||
| `; | |||
| export const UserName = styled(Typography)` | |||
| margin-bottom: 12px; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| @@ -76,28 +66,3 @@ export const UserName = styled(Typography)` | |||
| font-size: 18px; | |||
| } | |||
| `; | |||
| export const LastMessage = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| color: ${selectedTheme.colors.primaryDarkTextThird}; | |||
| line-height: 22px; | |||
| font-size: 16px; | |||
| max-width: 220px; | |||
| flex: 1; | |||
| overflow: hidden; | |||
| max-height: 66px; | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 3; | |||
| -webkit-box-orient: vertical; | |||
| position: relative; | |||
| @media (max-width: 600px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const Line = styled(Box)` | |||
| border-left: 1px solid rgba(0, 0, 0, 0.15); | |||
| height: 100px; | |||
| width: 0; | |||
| margin: auto 0; | |||
| @media (max-width: 600px) { | |||
| display: none; | |||
| }`; | |||
| @@ -11,6 +11,7 @@ import selectedTheme from "../../../../themes"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import PhonePopover from "../../../Popovers/PhonePopover/PhonePopover"; | |||
| // Not being used, but kept for potential future needs | |||
| const ChatCommands = (props) => { | |||
| const { t } = useTranslation(); | |||
| const [showPhonePopover, setShowPhonePopover] = useState(false); | |||
| @@ -2,8 +2,8 @@ import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import { ReactComponent as Phone } from "../../../../assets/images/svg/phone.svg"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { IconButton } from "../../../Buttons/IconButton/IconButton"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import IconButton from "../../../IconButton/IconButton"; | |||
| import PopoverComponent from "../../../Popovers/PopoverComponent"; | |||
| @@ -13,6 +13,7 @@ import { Col } from "../ChatCard.styled"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| // Not being used, but kept for potential future needs | |||
| const LittleOfferDetails = (props) => { | |||
| const { t } = useTranslation(); | |||
| const { isMobile } = useIsMobile(); | |||
| @@ -21,7 +22,6 @@ const LittleOfferDetails = (props) => { | |||
| <Col mobileDisappear> | |||
| <ChatOffer> | |||
| <OfferImgWrapper> | |||
| {/* <OfferImage src={chat?.offerData?.firstImage} /> */} | |||
| <OfferImage | |||
| src={getImageUrl( | |||
| chat?.offerData?.firstImage, | |||
| @@ -8,12 +8,11 @@ import { | |||
| } from "./MobileOfferDetails.styled"; | |||
| const MobileOfferDetails = (props) => { | |||
| const chat = props.chat; | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <OfferCardContainerMobile> | |||
| <OfferTextMobile>{t("messages.cardProduct")}</OfferTextMobile> | |||
| <OfferTitleMobile>{chat?.offerData?.name}</OfferTitleMobile> | |||
| <OfferTitleMobile>{props.chat?.offerData?.name}</OfferTitleMobile> | |||
| </OfferCardContainerMobile> | |||
| ); | |||
| }; | |||
| @@ -7,6 +7,7 @@ import { | |||
| XSText, | |||
| } from "./OfferLocation.styled"; | |||
| // Not being used, but kept for potential future needs | |||
| const OfferLocation = (props) => { | |||
| const chat = props.chat; | |||
| return ( | |||
| @@ -17,7 +17,7 @@ const BackButton = (props) => { | |||
| BackButton.propTypes = { | |||
| setCurrentStep: PropTypes.func, | |||
| currentStep: PropTypes.bool, | |||
| currentStep: PropTypes.number, | |||
| }; | |||
| export default BackButton; | |||
| @@ -24,104 +24,74 @@ import BackdropComponent from "../../MUI/BackdropComponent"; | |||
| import CloseButton from "./CloseButton/CloseButton"; | |||
| import BackButton from "./BackButton/BackButton"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useMemo } from "react"; | |||
| import { useLocation } from "react-router-dom"; | |||
| import { BASE_PAGE, HOME_PAGE } from "../../../constants/pages"; | |||
| import { routeMatches } from "../../../util/helpers/routeHelpers"; | |||
| const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| const dispatch = useDispatch(); | |||
| const location = useLocation(); | |||
| const [informations, setInformations] = useState({}); | |||
| const [currentStep, setCurrentStep] = useState(1); | |||
| const { t } = useTranslation(); | |||
| const userId = useSelector(selectUserId); | |||
| const handleApiResponseSuccess = () => { | |||
| if (editOffer) { | |||
| dispatch(fetchOneOffer(offer._id)); | |||
| dispatch(fetchProfileOffers(userId)); | |||
| } else { | |||
| if (routeMatches(BASE_PAGE) || routeMatches(HOME_PAGE)) | |||
| dispatch(fetchOffers({ queryString: "" })); | |||
| if (location.pathname.includes("profile")) | |||
| dispatch(fetchProfileOffers(userId)); | |||
| if (location.pathname.includes("proizvodi")) | |||
| dispatch(fetchOneOffer(offer._id)); | |||
| } | |||
| }; | |||
| // Go to next step and save typed values | |||
| const handleNext = (values) => { | |||
| setInformations({ ...informations, ...values }); | |||
| setCurrentStep((prevState) => prevState + 1); | |||
| console.log({ ...informations, ...values }); | |||
| }; | |||
| const newImgs = | |||
| informations.images && | |||
| informations.images | |||
| .filter((img) => img !== undefined) | |||
| .map( | |||
| (img) => img | |||
| // .replace("data:image/jpg;base64,", "") | |||
| // .replace("data:image/jpeg;base64,", "") | |||
| // .replace("data:image/png;base64,", "") | |||
| ); | |||
| const offerData = { | |||
| name: informations.nameOfProduct, | |||
| description: informations.description, | |||
| location: { | |||
| city: informations.location, | |||
| }, | |||
| condition: informations.condition, | |||
| category: { | |||
| name: informations.category, | |||
| }, | |||
| subcategory: informations.subcategory, | |||
| images: newImgs, | |||
| }; | |||
| const submitOffer = (values) => { | |||
| dispatch(addOffer({ values, handleApiResponseSuccess })); | |||
| }; | |||
| // Get new images from 2nd step | |||
| const newImgs = useMemo( | |||
| () => | |||
| informations.images && | |||
| informations.images.filter((img) => img !== undefined), | |||
| [informations.images] | |||
| ); | |||
| const submitEditOffer = (id, values) => { | |||
| dispatch(editOneOffer(id, values)); | |||
| }; | |||
| // Make offer data object with typed values | |||
| const offerData = useMemo( | |||
| () => ({ | |||
| name: informations.nameOfProduct, | |||
| description: informations.description, | |||
| location: { | |||
| city: informations.location, | |||
| }, | |||
| condition: informations.condition, | |||
| category: { | |||
| name: informations.category, | |||
| }, | |||
| subcategory: informations.subcategory, | |||
| images: newImgs, | |||
| }), | |||
| [informations, newImgs] | |||
| ); | |||
| // Create (or edit) offer | |||
| const handleSubmitOffer = () => { | |||
| if (editOffer === undefined) { | |||
| submitOffer({ offerData, handleApiResponseSuccess }); | |||
| if (editOffer) { | |||
| dispatch(editOneOffer(offer._id, offerData)); | |||
| } else { | |||
| const offerId = offer._id; | |||
| submitEditOffer({ offerId, offerData, handleApiResponseSuccess }); | |||
| dispatch(addOffer({ offerData, handleApiResponseSuccess })); | |||
| } | |||
| closeCreateOfferModal(false); | |||
| }; | |||
| const goStepBack = (stepNumber) => { | |||
| setCurrentStep(stepNumber); | |||
| const { | |||
| category, | |||
| condition, | |||
| description, | |||
| images, | |||
| location, | |||
| nameOfProduct, | |||
| subcategory, | |||
| } = informations; | |||
| if (stepNumber === 1) { | |||
| setInformations({ | |||
| category, | |||
| condition, | |||
| description, | |||
| location, | |||
| nameOfProduct, | |||
| subcategory, | |||
| }); | |||
| } | |||
| if (stepNumber === 2) { | |||
| setInformations({ | |||
| category, | |||
| condition, | |||
| description, | |||
| images, | |||
| location, | |||
| nameOfProduct, | |||
| subcategory, | |||
| }); | |||
| } | |||
| // Here goes any additional logic | |||
| }; | |||
| return ( | |||
| @@ -131,8 +101,9 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| handleClose={closeCreateOfferModal} | |||
| position="fixed" | |||
| /> | |||
| <ModalCreateOfferContainer currentStep={currentStep}> | |||
| <CreateOfferContainer currentStep={currentStep}> | |||
| <ModalCreateOfferContainer currentstep={currentStep}> | |||
| <CreateOfferContainer currentstep={currentStep}> | |||
| {/* Modal header */} | |||
| <ModalHeader> | |||
| <BackButton | |||
| currentStep={currentStep} | |||
| @@ -142,13 +113,14 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| {currentStep === 3 | |||
| ? `${t("offer.review")}` | |||
| : `${ | |||
| editOffer !== undefined | |||
| editOffer | |||
| ? `${t("offer.changeOffer")}` | |||
| : `${t("offer.newOffer")}` | |||
| }`} | |||
| </CreateOfferTitle> | |||
| <CloseButton closeCreateOfferModal={closeCreateOfferModal} /> | |||
| </ModalHeader> | |||
| {/* ^^^^^^^^ */} | |||
| <StepProgress | |||
| lineColor={selectedTheme.colors.stepProgressAltColor} | |||
| @@ -156,13 +128,16 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| numberOfSteps={3} | |||
| functions={[() => goStepBack(1), () => goStepBack(2)]} | |||
| /> | |||
| {currentStep === 1 && ( | |||
| <FirstPartCreateOffer | |||
| handleNext={handleNext} | |||
| offer={offer} | |||
| editOffer={editOffer} | |||
| informations={informations} | |||
| /> | |||
| )} | |||
| {currentStep === 2 && ( | |||
| <SecondPartCreateOffer | |||
| handleNext={handleNext} | |||
| @@ -170,6 +145,7 @@ const CreateOffer = ({ closeCreateOfferModal, editOffer, offer }) => { | |||
| informations={informations} | |||
| /> | |||
| )} | |||
| {currentStep === 3 && ( | |||
| <ThirdPartCreateOffer | |||
| handleSubmitOffer={handleSubmitOffer} | |||
| @@ -1,16 +1,17 @@ | |||
| import { Box, Container, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Label } from "../../CheckBox/Label"; | |||
| import Select from "../../Select/Select"; | |||
| export const ModalCreateOfferContainer = styled(Box)` | |||
| background-color: #fff; | |||
| position: fixed; | |||
| ${(props) => props.currentStep === 3 && `overflow-y: auto;`} | |||
| ${(props) => props.currentstep === 3 && `overflow-y: auto;`} | |||
| max-height: 90vh; | |||
| top: ${(props) => | |||
| props.currentStep === 1 ? "calc(50% - 400px);" : "calc(50% - 350px);"}; | |||
| props.currentstep === 1 ? "calc(50% - 400px);" : "calc(50% - 350px);"}; | |||
| left: ${(props) => | |||
| props.currentStep !== 3 ? "calc(50% - 310px);" : "calc(50% - 405px);"}; | |||
| z-index: 150; | |||
| @@ -32,7 +33,7 @@ export const ModalCreateOfferContainer = styled(Box)` | |||
| @media (max-height: 820px) { | |||
| top: ${(props) => | |||
| props.currentStep === 1 ? "calc(50% - 340px)" : "calc(50% - 340px)"}; | |||
| props.currentstep === 1 ? "calc(50% - 340px)" : "calc(50% - 340px)"}; | |||
| } | |||
| @media (max-width: 810px) { | |||
| @@ -204,3 +205,22 @@ export const SelectAltText = styled(Typography)` | |||
| position: relative; | |||
| bottom: -1px; | |||
| `; | |||
| export const NextButtonContainer = styled(PrimaryButton)` | |||
| margin-top: 16px; | |||
| width: 100%; | |||
| @media (min-width: 601px) { | |||
| margin-bottom: 18px; | |||
| } | |||
| @media screen and (max-width: 600px) { | |||
| position: absolute; | |||
| bottom: 18px; | |||
| height: 44px; | |||
| width: calc(100% - 18px); | |||
| left: 9px; | |||
| & button { | |||
| height: 44px; | |||
| } | |||
| } | |||
| `; | |||
| @@ -1,230 +1,74 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import React, { useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useFormik } from "formik"; | |||
| import { | |||
| CreateOfferFormContainer, | |||
| DescriptionField, | |||
| FieldLabel, | |||
| NextButton, | |||
| SelectOption, | |||
| TitleField, | |||
| } from "./FirstPartCreateOffer.styled"; | |||
| import * as Yup from "yup"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { SelectField } from "../CreateOffer.styled"; | |||
| import { CreateOfferFormContainer } from "./FirstPartCreateOffer.styled"; | |||
| import { useSelector } from "react-redux"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { useMemo } from "react"; | |||
| import OfferTitleField from "./OfferTitleField/OfferTitleField"; | |||
| import OfferDescriptionField from "./OfferDescriptionField/OfferDescriptionField"; | |||
| import OfferLocationField from "./OfferLocationField/OfferLocationField"; | |||
| import OfferCategoryField from "./OfferCategoryField/OfferCategoryField"; | |||
| import OfferSubcategoryField from "./OfferSubcategoryField/OfferSubcategoryField"; | |||
| import NextButton from "./NextButton/NextButton"; | |||
| import firstPartCreateOfferInitialValues from "../../../../initialValues/createOfferInitialValues/firstPartCreateOfferInitialValues"; | |||
| import firstPartCreateOfferValidation from "../../../../validations/createOfferValidation/firstPartCreateOfferValidation"; | |||
| const FirstPartCreateOffer = (props) => { | |||
| const [subcat, setSubcat] = useState([]); | |||
| const [subcategories, setSubcategories] = useState([]); | |||
| const locations = useSelector((state) => state.locations.locations); | |||
| const categories = useSelector((state) => state.categories.categories); | |||
| const { isMobile } = useIsMobile(); | |||
| const { t } = useTranslation(); | |||
| // Change subcategory select options on category change | |||
| const handleSubcategories = (category) => { | |||
| setSubcategories( | |||
| categories | |||
| .find((cat) => cat.name === category) | |||
| ?.subcategories?.map((subcategory) => subcategory.name) | |||
| ); | |||
| }; | |||
| useEffect(() => { | |||
| if (!props.offer) { | |||
| if (Object.keys(props.informations).length !== 0) { | |||
| formik.setFieldValue("nameOfProduct", props.informations.nameOfProduct); | |||
| formik.setFieldValue("description", props.informations.description); | |||
| formik.setFieldValue("location", props.informations.location); | |||
| formik.setFieldValue("category", props.informations.category); | |||
| formik.setFieldValue("subcategory", props.informations.subcategory); | |||
| let scat = categories.filter( | |||
| (cat) => cat.name === props.informations.category | |||
| ); | |||
| setSubcat(scat[0].subcategories.map((x) => x.name)); | |||
| } | |||
| } else { | |||
| formik.setFieldValue("location", props.offer.location.city); | |||
| formik.setFieldValue("category", props.offer.category.name); | |||
| formik.setFieldValue("subcategory", props.offer.subcategory); | |||
| // Get initial values | |||
| const initialValues = useMemo(() => { | |||
| const newInitialValues = firstPartCreateOfferInitialValues( | |||
| props.offer, | |||
| props.informations | |||
| ); | |||
| if (categories) { | |||
| handleSubcategories(newInitialValues.category); | |||
| } | |||
| }, [props.offer, props.informations]); | |||
| return newInitialValues; | |||
| }, [props.offer, props.informations, categories]); | |||
| useEffect(() => { | |||
| if (props.offer !== undefined) { | |||
| let scat = categories.filter( | |||
| (cat) => cat.name === props.offer.category.name | |||
| ); | |||
| setSubcat(scat[0].subcategories.map((x) => x.name)); | |||
| } | |||
| }, [props.offer]); | |||
| // Get validation schema | |||
| const validationSchema = useMemo(() => { | |||
| return firstPartCreateOfferValidation(locations, categories, subcategories); | |||
| }, [subcategories]); | |||
| const handleSubmit = (values) => { | |||
| props.handleNext(values); | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| nameOfProduct: `${!props.offer ? "" : props.offer.name}`, | |||
| description: `${!props.offer ? "" : props.offer.description}`, | |||
| location: "default", | |||
| category: "default", | |||
| subcategory: "default", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| nameOfProduct: Yup.string().required(t("login.nameOfProductRequired")), | |||
| description: Yup.string().required(t("login.descriptionRequired")).min(8), | |||
| location: Yup.string().oneOf(locations.map((l) => l.city)), | |||
| category: Yup.string().oneOf(categories.map((c) => c.name)), | |||
| // subcategory: Yup.string().oneOf( | |||
| // subcat[0]?.subcategories ? subcat[0].subcategories : [] | |||
| // ), | |||
| }), | |||
| initialValues, | |||
| validationSchema, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| const handleSubcategories = (category) => { | |||
| const filtered = categories.filter((cat) => cat.name === category); | |||
| setSubcat(filtered[0].subcategories.map((c) => c.name)); | |||
| }; | |||
| return ( | |||
| <> | |||
| <CreateOfferFormContainer component="form" onSubmit={formik.handleSubmit}> | |||
| <FieldLabel leftText={t("offer.title")} /> | |||
| <TitleField | |||
| name="nameOfProduct" | |||
| placeholder={t("offer.productName")} | |||
| italicPlaceholder | |||
| margin="normal" | |||
| value={formik.values.nameOfProduct} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.nameOfProduct && formik.errors.nameOfProduct} | |||
| helperText={ | |||
| formik.touched.nameOfProduct && formik.errors.nameOfProduct | |||
| } | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <FieldLabel leftText={t("offer.productDescription")} /> | |||
| {!isMobile ? ( | |||
| <DescriptionField | |||
| name="description" | |||
| placeholder={t("offer.description")} | |||
| margin="normal" | |||
| italicPlaceholder | |||
| value={formik.values.description} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.description && formik.errors.description} | |||
| helperText={formik.touched.description && formik.errors.description} | |||
| fullWidth | |||
| multiline | |||
| minRows={4} | |||
| height={"100px"} | |||
| /> | |||
| ) : ( | |||
| <DescriptionField | |||
| name="description" | |||
| placeholder={t("offer.description")} | |||
| margin="normal" | |||
| italicPlaceholder | |||
| value={formik.values.description} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.description && formik.errors.description} | |||
| helperText={formik.touched.description && formik.errors.description} | |||
| fullWidth | |||
| /> | |||
| )} | |||
| <FieldLabel leftText={t("offer.location")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.location} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("location", value.target.value); | |||
| }} | |||
| value={formik.values.location} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseLocation")} | |||
| </SelectOption> | |||
| {locations.map((loc) => { | |||
| return ( | |||
| <SelectOption key={loc._id} value={loc.city}> | |||
| {loc.city} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| <FieldLabel leftText={t("offer.category")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.category} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("category", value.target.value); | |||
| }} | |||
| value={formik.values.category} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseCategory")} | |||
| </SelectOption> | |||
| {categories.map((cat, i) => { | |||
| return ( | |||
| <SelectOption | |||
| key={i} | |||
| value={cat.name} | |||
| onClick={() => handleSubcategories(cat.name)} | |||
| > | |||
| {cat.name} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| <FieldLabel leftText={t("offer.subcategory")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.subcategory} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("subcategory", value.target.value); | |||
| }} | |||
| value={formik.values.subcategory} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseSubcategory")} | |||
| </SelectOption> | |||
| {subcat && | |||
| subcat.map((sub, i) => { | |||
| return ( | |||
| <SelectOption key={i} value={sub}> | |||
| {sub} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </CreateOfferFormContainer> | |||
| <NextButton | |||
| type="submit" | |||
| variant="contained" | |||
| height="48px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={formik.handleSubmit} | |||
| disabled={ | |||
| formik.values?.nameOfProduct?.length === 0 || | |||
| !formik.values?.nameOfProduct || | |||
| formik.values?.description?.length === 0 || | |||
| !formik.values?.description || | |||
| formik.values?.category?.length === 0 || | |||
| !formik.values?.category || | |||
| formik.values?.category === "default" || | |||
| formik.values?.subcategory?.length === 0 || | |||
| !formik.values?.subcategory || | |||
| formik.values?.subcategory === "default" || | |||
| formik.values?.location?.length === 0 || | |||
| !formik.values?.location || | |||
| formik.values?.location === "default" | |||
| } | |||
| > | |||
| {t("offer.continue")} | |||
| </NextButton> | |||
| </> | |||
| <CreateOfferFormContainer component="form" onSubmit={formik.handleSubmit}> | |||
| <OfferTitleField formik={formik} /> | |||
| <OfferDescriptionField formik={formik} /> | |||
| <OfferLocationField formik={formik} locations={locations} /> | |||
| <OfferCategoryField | |||
| formik={formik} | |||
| categories={categories} | |||
| handleSubcategories={handleSubcategories} | |||
| /> | |||
| <OfferSubcategoryField formik={formik} subcategories={subcategories} /> | |||
| <NextButton formik={formik} /> | |||
| </CreateOfferFormContainer> | |||
| ); | |||
| }; | |||
| @@ -232,6 +76,7 @@ FirstPartCreateOffer.propTypes = { | |||
| children: PropTypes.any, | |||
| handleNext: PropTypes.func, | |||
| offer: PropTypes.node, | |||
| editOffer: PropTypes.bool, | |||
| informations: PropTypes.any, | |||
| }; | |||
| @@ -1,10 +1,8 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Label } from "../../../CheckBox/Label"; | |||
| import Option from "../../../Select/Option/Option"; | |||
| import { TextField } from "../../../TextFields/TextField/TextField"; | |||
| export const CreateOfferTitle = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| @@ -56,59 +54,6 @@ export const FieldLabel = styled(Label)` | |||
| } | |||
| } | |||
| `; | |||
| export const DescriptionField = styled(TextField)` | |||
| margin-bottom: 4px; | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 0; | |||
| & div div input { | |||
| font-size: 12px !important; | |||
| } | |||
| & div { | |||
| height: 40px; | |||
| } | |||
| } | |||
| `; | |||
| export const TitleField = styled(TextField)` | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 0; | |||
| & div div input { | |||
| font-size: 12px !important; | |||
| } | |||
| & div { | |||
| height: 40px; | |||
| } | |||
| } | |||
| `; | |||
| export const NextButton = styled(PrimaryButton)` | |||
| margin-top: 16px; | |||
| width: 100%; | |||
| @media (min-width: 601px) { | |||
| margin-bottom: 18px; | |||
| } | |||
| @media screen and (max-width: 600px) { | |||
| position: absolute; | |||
| bottom: 18px; | |||
| height: 44px; | |||
| /* width: calc(100% - 18px); */ | |||
| width: 339px; | |||
| /* left: 18px; */ | |||
| & button { | |||
| height: 44px; | |||
| } | |||
| } | |||
| @media screen and (max-width: 400px) { | |||
| position: absolute; | |||
| bottom: 18px; | |||
| height: 44px; | |||
| width: calc(100% - 18px); | |||
| left: 9px; | |||
| & button { | |||
| height: 44px; | |||
| } | |||
| } | |||
| `; | |||
| export const SelectOption = styled(Option)` | |||
| height: 40px !important; | |||
| min-height: 40px; | |||
| @@ -0,0 +1,44 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { NextButtonContainer } from "../../CreateOffer.styled"; | |||
| const NextButton = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <NextButtonContainer | |||
| type="submit" | |||
| variant="contained" | |||
| height="48px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={formik.handleSubmit} | |||
| disabled={ | |||
| formik.values?.nameOfProduct?.length === 0 || | |||
| !formik.values?.nameOfProduct || | |||
| formik.values?.description?.length === 0 || | |||
| !formik.values?.description || | |||
| formik.values?.category?.length === 0 || | |||
| !formik.values?.category || | |||
| formik.values?.category === "default" || | |||
| formik.values?.subcategory?.length === 0 || | |||
| !formik.values?.subcategory || | |||
| formik.values?.subcategory === "default" || | |||
| formik.values?.location?.length === 0 || | |||
| !formik.values?.location || | |||
| formik.values?.location === "default" | |||
| } | |||
| > | |||
| {t("offer.continue")} | |||
| </NextButtonContainer> | |||
| ); | |||
| }; | |||
| NextButton.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default NextButton; | |||
| @@ -0,0 +1,45 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { FieldLabel, SelectOption } from "../FirstPartCreateOffer.styled"; | |||
| import { SelectField } from "../../CreateOffer.styled"; | |||
| const OfferCategoryField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| <FieldLabel leftText={t("offer.category")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.category} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("category", value.target.value); | |||
| }} | |||
| value={formik.values.category} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseCategory")} | |||
| </SelectOption> | |||
| {props.categories.map((cat, i) => { | |||
| return ( | |||
| <SelectOption | |||
| key={i} | |||
| value={cat.name} | |||
| onClick={() => props.handleSubcategories(cat.name)} | |||
| > | |||
| {cat.name} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </> | |||
| ); | |||
| }; | |||
| OfferCategoryField.propTypes = { | |||
| formik: PropTypes.any, | |||
| handleSubcategories: PropTypes.func, | |||
| categories: PropTypes.array, | |||
| }; | |||
| export default OfferCategoryField; | |||
| @@ -0,0 +1,51 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import useIsMobile from "../../../../../hooks/useIsMobile"; | |||
| import { FieldLabel } from "../FirstPartCreateOffer.styled"; | |||
| import { DescriptionField } from "./OfferDescriptionField.styled"; | |||
| const OfferDescriptionField = (props) => { | |||
| const formik = props.formik; | |||
| const { t } = useTranslation(); | |||
| const { isMobile } = useIsMobile(); | |||
| return ( | |||
| <> | |||
| <FieldLabel leftText={t("offer.productDescription")} /> | |||
| {!isMobile ? ( | |||
| <DescriptionField | |||
| name="description" | |||
| placeholder={t("offer.description")} | |||
| margin="normal" | |||
| italicPlaceholder | |||
| value={formik.values.description} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.description && formik.errors.description} | |||
| helperText={formik.touched.description && formik.errors.description} | |||
| fullWidth | |||
| multiline | |||
| minRows={4} | |||
| height={"100px"} | |||
| /> | |||
| ) : ( | |||
| <DescriptionField | |||
| name="description" | |||
| placeholder={t("offer.description")} | |||
| margin="normal" | |||
| italicPlaceholder | |||
| value={formik.values.description} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.description && formik.errors.description} | |||
| helperText={formik.touched.description && formik.errors.description} | |||
| fullWidth | |||
| /> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| OfferDescriptionField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default OfferDescriptionField; | |||
| @@ -0,0 +1,15 @@ | |||
| import styled from "styled-components"; | |||
| import { TextField } from "../../../../TextFields/TextField/TextField"; | |||
| export const DescriptionField = styled(TextField)` | |||
| margin-bottom: 4px; | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 0; | |||
| & div div input { | |||
| font-size: 12px !important; | |||
| } | |||
| & div { | |||
| height: 40px; | |||
| } | |||
| } | |||
| `; | |||
| @@ -0,0 +1,40 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { FieldLabel, SelectOption } from "../FirstPartCreateOffer.styled"; | |||
| import { SelectField } from "../../CreateOffer.styled"; | |||
| const OfferLocationField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| <FieldLabel leftText={t("offer.location")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.location} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("location", value.target.value); | |||
| }} | |||
| value={formik.values.location} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseLocation")} | |||
| </SelectOption> | |||
| {props.locations.map((loc) => { | |||
| return ( | |||
| <SelectOption key={loc._id} value={loc.city}> | |||
| {loc.city} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </> | |||
| ); | |||
| }; | |||
| OfferLocationField.propTypes = { | |||
| formik: PropTypes.any, | |||
| locations: PropTypes.array, | |||
| }; | |||
| export default OfferLocationField; | |||
| @@ -0,0 +1,41 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { FieldLabel, SelectOption } from "../FirstPartCreateOffer.styled"; | |||
| import { SelectField } from "../../CreateOffer.styled"; | |||
| const OfferSubcategoryField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| <FieldLabel leftText={t("offer.subcategory")} /> | |||
| <SelectField | |||
| defaultValue={formik.values.subcategory} | |||
| onChange={(value) => { | |||
| formik.setFieldValue("subcategory", value.target.value); | |||
| }} | |||
| value={formik.values.subcategory} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseSubcategory")} | |||
| </SelectOption> | |||
| {props.subcategories && | |||
| props.subcategories.map((sub, i) => { | |||
| return ( | |||
| <SelectOption key={i} value={sub}> | |||
| {sub} | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </> | |||
| ); | |||
| }; | |||
| OfferSubcategoryField.propTypes = { | |||
| formik: PropTypes.any, | |||
| subcategories: PropTypes.array, | |||
| }; | |||
| export default OfferSubcategoryField; | |||
| @@ -0,0 +1,33 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { FieldLabel } from "../FirstPartCreateOffer.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { TitleField } from "./OfferTitleField.styled"; | |||
| const OfferTitleField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| <FieldLabel leftText={t("offer.title")} /> | |||
| <TitleField | |||
| name="nameOfProduct" | |||
| placeholder={t("offer.productName")} | |||
| italicPlaceholder | |||
| margin="normal" | |||
| value={formik.values.nameOfProduct} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.nameOfProduct && formik.errors.nameOfProduct} | |||
| helperText={formik.touched.nameOfProduct && formik.errors.nameOfProduct} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| OfferTitleField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default OfferTitleField; | |||
| @@ -0,0 +1,14 @@ | |||
| import styled from "styled-components"; | |||
| import { TextField } from "../../../../TextFields/TextField/TextField"; | |||
| export const TitleField = styled(TextField)` | |||
| @media (max-width: 600px) { | |||
| margin-bottom: 0; | |||
| & div div input { | |||
| font-size: 12px !important; | |||
| } | |||
| & div { | |||
| height: 40px; | |||
| } | |||
| } | |||
| `; | |||
| @@ -0,0 +1,37 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { NextButtonContainer } from "../../CreateOffer.styled"; | |||
| const NextButton = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <NextButtonContainer | |||
| type="submit" | |||
| variant="contained" | |||
| height="48px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={formik.handleSubmit} | |||
| disabled={ | |||
| props.imagesEmpty === props.numberOfImages || | |||
| formik.values?.condition?.length === 0 || | |||
| !formik.values?.condition || | |||
| formik.values?.condition === "default" | |||
| } | |||
| > | |||
| {t("offer.continue")} | |||
| </NextButtonContainer> | |||
| ); | |||
| }; | |||
| NextButton.propTypes = { | |||
| formik: PropTypes.any, | |||
| imagesEmpty: PropTypes.any, | |||
| numberOfImages: PropTypes.any, | |||
| }; | |||
| export default NextButton; | |||
| @@ -0,0 +1,47 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { FieldLabel } from "../SecondPartCreateOffer.styled"; | |||
| import { | |||
| SelectAltText, | |||
| SelectField, | |||
| SelectText, | |||
| } from "../../CreateOffer.styled"; | |||
| import { SelectOption } from "../../FirstPart/FirstPartCreateOffer.styled"; | |||
| import { conditionSelectEnum } from "../../../../../enums/conditionEnum"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { InputButtonContainer } from "./OfferConditionField.styled"; | |||
| const OfferConditionField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const formik = props.formik; | |||
| return ( | |||
| <InputButtonContainer> | |||
| <FieldLabel leftText={t("offer.condition")} /> | |||
| <SelectField | |||
| onChange={(event) => { | |||
| formik.setFieldValue("condition", event.target.value); | |||
| }} | |||
| value={formik.values.condition} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseCondition")} | |||
| </SelectOption> | |||
| {Object.keys(conditionSelectEnum).map((key) => { | |||
| var item = conditionSelectEnum[key]; | |||
| return ( | |||
| <SelectOption value={item.mainText} key={item.value}> | |||
| <SelectText>{item.mainText}</SelectText> | |||
| <SelectAltText>{item.altText}</SelectAltText> | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </InputButtonContainer> | |||
| ); | |||
| }; | |||
| OfferConditionField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default OfferConditionField; | |||
| @@ -0,0 +1,10 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const InputButtonContainer = styled(Box)` | |||
| width: 332px; | |||
| margin: 25px auto; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| `; | |||
| @@ -0,0 +1,29 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import ImagePicker from "../../../../ImagePicker/ImagePicker"; | |||
| import { Scroller } from "./OfferImagePicker.styled"; | |||
| const OfferImagePicker = (props) => { | |||
| return ( | |||
| <Scroller> | |||
| {props.images.map((item, index) => { | |||
| return ( | |||
| <ImagePicker | |||
| key={index} | |||
| image={item} | |||
| setImage={(image) => props.setImage(index, image)} | |||
| deleteImage={() => props.setImage(index, null)} | |||
| showDeleteIcon | |||
| /> | |||
| ); | |||
| })} | |||
| </Scroller> | |||
| ); | |||
| }; | |||
| OfferImagePicker.propTypes = { | |||
| images: PropTypes.array, | |||
| setImage: PropTypes.func, | |||
| }; | |||
| export default OfferImagePicker; | |||
| @@ -0,0 +1,8 @@ | |||
| import styled from "styled-components"; | |||
| import HorizontalScroller from "../../../../Scroller/HorizontalScroller"; | |||
| export const Scroller = styled(HorizontalScroller)` | |||
| min-width: 100%; | |||
| position: relative; | |||
| margin-bottom: 36px; | |||
| `; | |||
| @@ -1,23 +1,17 @@ | |||
| import React, { useMemo, useState, useEffect } from "react"; | |||
| import React, { useMemo, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CreateOfferFormContainer, | |||
| FieldLabel, | |||
| Scroller, | |||
| SupportedFormats, | |||
| InputButtonContainer, | |||
| } from "./SecondPartCreateOffer.styled"; | |||
| import ImagePicker from "../../../ImagePicker/ImagePicker"; | |||
| import { useTranslation, Trans } from "react-i18next"; | |||
| import { SelectAltText, SelectField, SelectText } from "../CreateOffer.styled"; | |||
| import { | |||
| NextButton, | |||
| SelectOption, | |||
| } from "../FirstPart/FirstPartCreateOffer.styled"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { conditionSelectEnum } from "../../../../enums/conditionEnum"; | |||
| import { Trans } from "react-i18next"; | |||
| import { useFormik } from "formik"; | |||
| import * as Yup from "yup"; | |||
| import { useEffect } from "react"; | |||
| import OfferImagePicker from "./OfferImagePicker/OfferImagePicker"; | |||
| import OfferConditionField from "./OfferConditionField/OfferConditionField"; | |||
| import NextButton from "./NextButton/NextButton"; | |||
| import secondPartCreateOfferValidation from "../../../../validations/createOfferValidation/secondPartCreateOfferValidation"; | |||
| import secondPartCreateOfferInitialValues from "../../../../initialValues/createOfferInitialValues/secondPartCreateOfferInitialValues"; | |||
| const numberOfImages = 3; | |||
| @@ -25,41 +19,29 @@ const SecondPartCreateOffer = (props) => { | |||
| const [images, setImages] = useState( | |||
| Array.apply(null, Array(numberOfImages)).map(() => {}) | |||
| ); // 3 images | |||
| const { t } = useTranslation(); | |||
| useEffect(() => { | |||
| if (!props.offer) { | |||
| if (Object.keys(props.informations).length > 6) { | |||
| setImages( | |||
| props.informations?.images | |||
| ? [...props.informations.images] | |||
| : [...images] | |||
| ); | |||
| formik.setFieldValue( | |||
| "condition", | |||
| props.informations?.condition | |||
| ? props.informations.condition | |||
| : "default" | |||
| ); | |||
| } | |||
| } else { | |||
| formik.setFieldValue("condition", props.offer.condition); | |||
| } | |||
| }, [props.offer, props.informations]); | |||
| const initialValues = useMemo(() => { | |||
| return secondPartCreateOfferInitialValues( | |||
| props?.informations, | |||
| props?.offer, | |||
| images | |||
| ); | |||
| }, [props?.informations, props?.offer, images]); | |||
| useEffect(() => { | |||
| setImages((prevState) => { | |||
| let editedImages = [...prevState]; | |||
| if (props.offer !== undefined && props.offer.images.length !== 0) { | |||
| editedImages[0] = props.offer.images[0]; | |||
| props.offer.images.forEach((oldImage, index) => { | |||
| editedImages[index] = oldImage | |||
| }) | |||
| } | |||
| formik.setFieldValue("images", images); | |||
| }, [images]); | |||
| return [...editedImages]; | |||
| }); | |||
| }, [props.offer]); | |||
| useEffect(() => { | |||
| if (props?.offer?.images || props?.informations?.images) { | |||
| const oldImages = props?.informations?.images || props?.offer?.images; | |||
| let newImages = [...images]; | |||
| oldImages.forEach((newImage, index) => { | |||
| newImages[index] = newImage; | |||
| }); | |||
| setImages([...newImages]); | |||
| } | |||
| }, [props?.offer, props?.informations]); | |||
| const setImage = (index, image) => { | |||
| setImages((prevState) => { | |||
| @@ -69,10 +51,11 @@ const SecondPartCreateOffer = (props) => { | |||
| }); | |||
| }; | |||
| //How many images are empty | |||
| const imagesEmpty = useMemo(() => { | |||
| let numOfImagesEmpty = 0; | |||
| images.forEach((item) => { | |||
| if (item === null || item === undefined) numOfImagesEmpty++; | |||
| if (!item) numOfImagesEmpty++; | |||
| }); | |||
| return numOfImagesEmpty; | |||
| }, [images]); | |||
| @@ -81,22 +64,9 @@ const SecondPartCreateOffer = (props) => { | |||
| props.handleNext(values); | |||
| }; | |||
| const conditionSelectEnumArray = Object.values(conditionSelectEnum); | |||
| const filteredconditionSelectEnumArray = conditionSelectEnumArray.map( | |||
| (item) => item.mainText | |||
| ); | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| images: images, | |||
| condition: | |||
| props.informations?.condition || props.offer?.condition || "default", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| condition: Yup.string() | |||
| .required() | |||
| .oneOf(filteredconditionSelectEnumArray), | |||
| }), | |||
| initialValues, | |||
| validationSchema: secondPartCreateOfferValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| @@ -105,64 +75,18 @@ const SecondPartCreateOffer = (props) => { | |||
| return ( | |||
| <> | |||
| <CreateOfferFormContainer component="form" onSubmit={formik.handleSubmit}> | |||
| <Scroller> | |||
| {images.map((item, index) => { | |||
| return ( | |||
| <ImagePicker | |||
| key={index} | |||
| image={item} | |||
| setImage={(image) => setImage(index, image)} | |||
| deleteImage={() => setImage(index, null)} | |||
| showDeleteIcon | |||
| /> | |||
| ); | |||
| })} | |||
| </Scroller> | |||
| <OfferImagePicker images={images} setImage={setImage} /> | |||
| <SupportedFormats> | |||
| <Trans i18nKey="offer.supportedImagesFormats" /> | |||
| </SupportedFormats> | |||
| <InputButtonContainer> | |||
| <FieldLabel leftText={t("offer.condition")} /> | |||
| <SelectField | |||
| onChange={(value) => { | |||
| formik.setFieldValue("condition", value.target.value); | |||
| }} | |||
| value={formik.values.condition} | |||
| > | |||
| <SelectOption style={{ display: "none" }} value="default"> | |||
| {t("offer.choseCondition")} | |||
| </SelectOption> | |||
| {Object.keys(conditionSelectEnum).map((key) => { | |||
| var item = conditionSelectEnum[key]; | |||
| return ( | |||
| <SelectOption value={item.mainText} key={item.value}> | |||
| <SelectText>{item.mainText}</SelectText> | |||
| <SelectAltText>{item.altText}</SelectAltText> | |||
| </SelectOption> | |||
| ); | |||
| })} | |||
| </SelectField> | |||
| </InputButtonContainer> | |||
| <OfferConditionField formik={formik} /> | |||
| </CreateOfferFormContainer> | |||
| <NextButton | |||
| type="submit" | |||
| variant="contained" | |||
| height="48px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={formik.handleSubmit} | |||
| disabled={ | |||
| (props.offer === undefined | |||
| ? imagesEmpty === numberOfImages | |||
| : false) || | |||
| formik.values?.condition?.length === 0 || | |||
| !formik.values?.condition || | |||
| formik.values?.condition === "default" | |||
| } | |||
| > | |||
| {t("offer.continue")} | |||
| </NextButton> | |||
| formik={formik} | |||
| offer={props.offer} | |||
| imagesEmpty={imagesEmpty} | |||
| numberOfImages={numberOfImages} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -2,7 +2,6 @@ import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { Label } from "../../../CheckBox/Label"; | |||
| import HorizontalScroller from "../../../Scroller/HorizontalScroller"; | |||
| export const CreateOfferFormContainer = styled(Box)` | |||
| padding-top: 20px; | |||
| @@ -23,19 +22,6 @@ export const FieldLabel = styled(Label)` | |||
| } | |||
| `; | |||
| export const InputButtonContainer = styled(Box)` | |||
| width: 332px; | |||
| margin: 25px auto; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| `; | |||
| export const Scroller = styled(HorizontalScroller)` | |||
| min-width: 100%; | |||
| position: relative; | |||
| margin-bottom: 36px; | |||
| `; | |||
| export const SupportedFormats = styled(Typography)` | |||
| font-size: 13px; | |||
| @@ -1,26 +1,32 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { PreviewCard, PublishButton } from "./ThirdPartCreateOffer.styled"; | |||
| // import { NextButton } from "../FirstPart/FirstPartCreateOffer.styled"; | |||
| import { PreviewCard } from "./ThirdPartCreateOffer.styled"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { CreateOfferFormContainer } from "../CreateOffer.styled"; | |||
| import { | |||
| CreateOfferFormContainer, | |||
| NextButtonContainer, | |||
| } from "../CreateOffer.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useMemo } from "react"; | |||
| const ThirdPartCreateOffer = (props) => { | |||
| const { t } = useTranslation(); | |||
| const offer = { | |||
| offer: { | |||
| category: { | |||
| name: props.informations.category, | |||
| const offer = useMemo( | |||
| () => ({ | |||
| offer: { | |||
| category: { | |||
| name: props.informations.category, | |||
| }, | |||
| subcategory: props.informations.subcategory, | |||
| condition: props.informations.condition, | |||
| _created: new Date().toString(), | |||
| images: props.informations.images.filter((item) => item !== undefined), | |||
| name: props.informations.nameOfProduct, | |||
| description: props.informations.description, | |||
| }, | |||
| subcategory: props.informations.subcategory, | |||
| condition: props.informations.condition, | |||
| _created: new Date().toString(), | |||
| images: props.informations.images.filter((item) => item !== undefined), | |||
| name: props.informations.nameOfProduct, | |||
| description: props.informations.description, | |||
| }, | |||
| }; | |||
| }), | |||
| [props.informations] | |||
| ); | |||
| const handleSubmit = (e) => { | |||
| e.preventDefault(); | |||
| props.handleSubmitOffer(); | |||
| @@ -44,7 +50,7 @@ const ThirdPartCreateOffer = (props) => { | |||
| previewCard | |||
| /> | |||
| </CreateOfferFormContainer> | |||
| <PublishButton | |||
| <NextButtonContainer | |||
| type="submit" | |||
| variant="contained" | |||
| height="48px" | |||
| @@ -53,7 +59,7 @@ const ThirdPartCreateOffer = (props) => { | |||
| onClick={handleSubmit} | |||
| > | |||
| {t("offer.publish")} | |||
| </PublishButton> | |||
| </NextButtonContainer> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -27,7 +27,6 @@ const FilterCard = (props) => { | |||
| skeleton={props.skeleton} | |||
| > | |||
| <SkeletonFilterCard | |||
| animationStage={props.animationStage} | |||
| skeleton={props.skeleton} | |||
| /> | |||
| {/* Header title for my offers */} | |||
| @@ -75,7 +74,6 @@ FilterCard.propTypes = { | |||
| closeResponsive: PropTypes.func, | |||
| myOffers: PropTypes.bool, | |||
| skeleton: PropTypes.bool, | |||
| animationStage: PropTypes.number, | |||
| filtersOpened: PropTypes.bool, | |||
| toggleFilters: PropTypes.func, | |||
| }; | |||
| @@ -1,68 +0,0 @@ | |||
| export default [[{ | |||
| string: "Beograd", | |||
| numberOfProducts: 17, | |||
| id: 0, | |||
| checked: false | |||
| } | |||
| ,{ | |||
| string: "Nis", | |||
| numberOfProducts: 137, | |||
| id: 1, | |||
| checked: false | |||
| } | |||
| ,{ | |||
| string: "Novi Sad", | |||
| numberOfProducts: 57, | |||
| id: 2, | |||
| checked: false | |||
| } | |||
| ,{ | |||
| string: "Kragujevac", | |||
| numberOfProducts: 62, | |||
| id: 3, | |||
| checked: false | |||
| } | |||
| ,{ | |||
| string: "Leskovac", | |||
| numberOfProducts: 1, | |||
| id: 4, | |||
| checked: false | |||
| } | |||
| ,{ | |||
| string: "Vranje", | |||
| numberOfProducts: 23, | |||
| id: 5, | |||
| checked: false | |||
| }], | |||
| [ | |||
| { | |||
| string: "SVE KATEGORIJE", | |||
| numberOfProducts: 259, | |||
| id: 0, | |||
| }, | |||
| { | |||
| string: "Kategorija 1", | |||
| numberOfProducts: 46, | |||
| id: 1, | |||
| }, | |||
| { | |||
| string: "Kategorija 2", | |||
| numberOfProducts: 26, | |||
| id: 2, | |||
| }, | |||
| { | |||
| string: "Kategorija 3", | |||
| numberOfProducts: 91, | |||
| id: 3, | |||
| }, | |||
| { | |||
| string: "Kategorija 4", | |||
| numberOfProducts: 23, | |||
| id: 4, | |||
| }, | |||
| { | |||
| string: "Kategorija 5", | |||
| numberOfProducts: 20, | |||
| id: 5, | |||
| }, | |||
| ]]; | |||
| @@ -8,21 +8,20 @@ import { | |||
| SkeletonChooserContainer, | |||
| } from "./SkeletonChooserHeader.styled"; | |||
| const SkeletonChooserHeader = (props) => { | |||
| const SkeletonChooserHeader = () => { | |||
| return ( | |||
| <SkeletonChooserContainer> | |||
| <LeftContainer> | |||
| <CircleOne animationStage={props.animationStage}/> | |||
| <Line animationStage={props.animationStage}/> | |||
| <CircleOne/> | |||
| <Line/> | |||
| </LeftContainer> | |||
| <CircleSecond animationStage={props.animationStage}/> | |||
| <CircleSecond/> | |||
| </SkeletonChooserContainer> | |||
| ); | |||
| }; | |||
| SkeletonChooserHeader.propTypes = { | |||
| children: PropTypes.node, | |||
| animationStage: PropTypes.any, | |||
| }; | |||
| export default SkeletonChooserHeader; | |||
| @@ -4,8 +4,8 @@ import { SkeletonChooserTitleContainer, SkeletonChooserTitleLine } from './Skele | |||
| const SkeletonChooserTitle = (props) => { | |||
| return ( | |||
| <SkeletonChooserTitleContainer center={props.center} animationStage={props.animationStage} > | |||
| <SkeletonChooserTitleLine center={props.center} animationStage={props.animationStage}/> | |||
| <SkeletonChooserTitleContainer center={props.center} > | |||
| <SkeletonChooserTitleLine center={props.center} /> | |||
| </SkeletonChooserTitleContainer> | |||
| ) | |||
| } | |||
| @@ -13,7 +13,6 @@ const SkeletonChooserTitle = (props) => { | |||
| SkeletonChooserTitle.propTypes = { | |||
| children: PropTypes.any, | |||
| center: PropTypes.bool, | |||
| animationStage: PropTypes.number, | |||
| } | |||
| export default SkeletonChooserTitle | |||
| @@ -13,26 +13,25 @@ import SkeletonSection from "./SkeletonSection/SkeletonSection"; | |||
| const SkeletonFilterCard = (props) => { | |||
| return ( | |||
| <SkeletonFilterCardContainer animationStage={props.animationStage} skeleton={props.skeleton}> | |||
| <SkeletonFilterCardContainer skeleton={props.skeleton}> | |||
| <SkeletonHeader> | |||
| <SkeletonHeaderLineOne animationStage={props.animationStage} /> | |||
| <SkeletonHeaderLineSecond animationStage={props.animationStage} /> | |||
| <SkeletonHeaderLineOne /> | |||
| <SkeletonHeaderLineSecond /> | |||
| </SkeletonHeader> | |||
| <SkeletonChooserHeader animationStage={props.animationStage}/> | |||
| <SkeletonChooserTitle animationStage={props.animationStage} /> | |||
| <SkeletonSection numberOfOptions={14} animationStage={props.animationStage} /> | |||
| <SkeletonChooserHeader animationStage={props.animationStage} /> | |||
| <SkeletonChooserHeader animationStage={props.animationStage} /> | |||
| <SkeletonChooserTitle animationStage={props.animationStage} /> | |||
| <SkeletonSection numberOfOptions={3} animationStage={props.animationStage} /> | |||
| <SkeletonChooserTitle center animationStage={props.animationStage} /> | |||
| <SkeletonChooserHeader/> | |||
| <SkeletonChooserTitle /> | |||
| <SkeletonSection numberOfOptions={14} /> | |||
| <SkeletonChooserHeader /> | |||
| <SkeletonChooserHeader /> | |||
| <SkeletonChooserTitle /> | |||
| <SkeletonSection numberOfOptions={3} /> | |||
| <SkeletonChooserTitle center /> | |||
| </SkeletonFilterCardContainer> | |||
| ); | |||
| }; | |||
| SkeletonFilterCard.propTypes = { | |||
| children: PropTypes.any, | |||
| animationStage: PropTypes.number, | |||
| skeleton: PropTypes.bool, | |||
| }; | |||
| @@ -10,7 +10,7 @@ const SkeletonSection = (props) => { | |||
| return ( | |||
| <SkeletonSectionContainer> | |||
| {arrayForMapping.map((item, index) => ( | |||
| <SkeletonSectionOption key={index} animationStage={props.animationStage} /> | |||
| <SkeletonSectionOption key={index} /> | |||
| ))} | |||
| </SkeletonSectionContainer> | |||
| ); | |||
| @@ -19,7 +19,6 @@ const SkeletonSection = (props) => { | |||
| SkeletonSection.propTypes = { | |||
| children: PropTypes.node, | |||
| numberOfOptions: PropTypes.number, | |||
| animationStage: PropTypes.number, | |||
| }; | |||
| export default SkeletonSection; | |||
| @@ -2,21 +2,20 @@ import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { Circle, EndLine, Line, OptionLeftContainer, SkeletonSectionOptionContainer } from './SkeletonSectionOption.styled' | |||
| const SkeletonSectionOption = (props) => { | |||
| const SkeletonSectionOption = () => { | |||
| return ( | |||
| <SkeletonSectionOptionContainer> | |||
| <OptionLeftContainer> | |||
| <Circle animationStage={props.animationStage} /> | |||
| <Line animationStage={props.animationStage} /> | |||
| <Circle/> | |||
| <Line/> | |||
| </OptionLeftContainer> | |||
| <EndLine animationStage={props.animationStage} /> | |||
| <EndLine/> | |||
| </SkeletonSectionOptionContainer> | |||
| ) | |||
| } | |||
| SkeletonSectionOption.propTypes = { | |||
| children: PropTypes.any, | |||
| animationStage: PropTypes.number, | |||
| } | |||
| export default SkeletonSectionOption | |||
| @@ -17,7 +17,6 @@ const LittleOfferCard = (props) => { | |||
| const { isMobile } = useIsMobile(); | |||
| return ( | |||
| <LittleOfferCardContainer> | |||
| {/* <OfferImage src={props.image} /> */} | |||
| <OfferImage | |||
| src={getImageUrl(props.image, variants.reviewCard, isMobile)} | |||
| /> | |||
| @@ -18,7 +18,6 @@ const MessageCard = (props) => { | |||
| const dateString = formatDateTime(new Date(message._created)); | |||
| return ( | |||
| <MessageCardContainer ismymessage={props.isMyMessage}> | |||
| {/* <ProfileImage src={props.image} /> */} | |||
| <ProfileImage | |||
| src={getImageUrl(props.image, variants.chatMessage, isMobile)} | |||
| /> | |||
| @@ -9,20 +9,24 @@ import { | |||
| ProfileProductName, | |||
| } from "./MiniChatCard.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import useIsMobile from "../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter"; | |||
| import history from "../../../store/utils/history"; | |||
| import { replaceInRoute } from "../../../util/helpers/routeHelpers"; | |||
| import { CHAT_MESSAGE_PAGE } from "../../../constants/pages"; | |||
| const MiniChatCard = (props) => { | |||
| const { t } = useTranslation(); | |||
| const history = useHistory(); | |||
| const { isMobile } = useIsMobile(); | |||
| const changeChat = () => { | |||
| history.push(`/messages/${props?.chat?.chat?._id}`); | |||
| history.push( | |||
| replaceInRoute(CHAT_MESSAGE_PAGE, { | |||
| idChat: props?.chat?.chat?._id, | |||
| }) | |||
| ); | |||
| }; | |||
| return ( | |||
| <MiniChatCardContainer selected={props.selected} onClick={changeChat}> | |||
| {/* <ProfileImage src={props?.chat?.interlocutorData?.image} /> */} | |||
| <ProfileImage | |||
| src={getImageUrl( | |||
| props?.chat?.interlocutorData?.image, | |||
| @@ -0,0 +1,36 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { replaceInRoute } from "../../../../util/helpers/routeHelpers"; | |||
| import { ITEM_DETAILS_PAGE } from "../../../../constants/pages"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { CheckButtonContainer } from "./CheckButton.styled"; | |||
| const CheckButton = (props) => { | |||
| const { t } = useTranslation(); | |||
| const routeToItem = (itemId) => { | |||
| history.push( | |||
| replaceInRoute(ITEM_DETAILS_PAGE, { | |||
| idProizvod: itemId, | |||
| }) | |||
| ); | |||
| }; | |||
| return ( | |||
| <CheckButtonContainer | |||
| variant={props.sponsored ? "contained" : "outlined"} | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={props.sponsored ? "white" : selectedTheme.colors.primaryPurple} | |||
| style={{ fontWeight: "600" }} | |||
| onClick={() => routeToItem(props.offerId)} | |||
| > | |||
| {t("offer.checkButtonLabel")} | |||
| </CheckButtonContainer> | |||
| ); | |||
| }; | |||
| CheckButton.propTypes = { | |||
| sponsored: PropTypes.bool, | |||
| offerId: PropTypes.string, | |||
| }; | |||
| export default CheckButton; | |||
| @@ -0,0 +1,18 @@ | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| export const CheckButtonContainer = styled(PrimaryButton)` | |||
| width: 180px; | |||
| height: 48px; | |||
| position: absolute; | |||
| bottom: 9px; | |||
| right: 9px; | |||
| &:hover button { | |||
| background-color: ${selectedTheme.colors.primaryPurple} !important; | |||
| color: white !important; | |||
| } | |||
| @media (max-width: 650px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,28 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { CancelButtonContainer } from "./CancelButton.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const CancelButton = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <CancelButtonContainer | |||
| variant="contained" | |||
| height="48px" | |||
| width="180px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={props.onClick} | |||
| > | |||
| {t("deleteOffer.cancel")} | |||
| </CancelButtonContainer> | |||
| ); | |||
| }; | |||
| CancelButton.propTypes = { | |||
| onClick: PropTypes.func, | |||
| }; | |||
| export default CancelButton; | |||
| @@ -0,0 +1,9 @@ | |||
| import styled from "styled-components"; | |||
| import { PrimaryButton } from "../../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| export const CancelButtonContainer = styled(PrimaryButton)` | |||
| @media screen and (max-width: 600px) { | |||
| width: 140px; | |||
| height: 45px; | |||
| } | |||
| `; | |||
| @@ -7,17 +7,9 @@ import { | |||
| OfferInfo, | |||
| OfferImageContainer, | |||
| OfferImage, | |||
| OfferDescription, | |||
| OfferDescriptionTitle, | |||
| OfferDescriptionCategory, | |||
| CancelButton, | |||
| RemoveIconContainer, | |||
| RemoveIcon, | |||
| SaveButton, | |||
| CategoryIcon, | |||
| } from "./DeleteOffer.styled"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { ReactComponent as Category } from "../../../../assets/images/svg/category.svg"; | |||
| import BackdropComponent from "../../../MUI/BackdropComponent"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { | |||
| @@ -25,15 +17,17 @@ import { | |||
| fetchProfileOffers, | |||
| removeOffer, | |||
| } from "../../../../store/actions/offers/offersActions"; | |||
| import { useTranslation, Trans } from "react-i18next"; | |||
| import { Trans } from "react-i18next"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../../util/helpers/imageUrlGetter"; | |||
| import { selectQueryString } from "../../../../store/selectors/queryStringSelectors"; | |||
| import OfferDescription from "./OfferDescription/OfferDescription"; | |||
| import CancelButton from "./CancelButton/CancelButton"; | |||
| import SaveButton from "./SaveButton/SaveButton"; | |||
| const DeleteOffer = (props) => { | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const queryString = useSelector(selectQueryString); | |||
| const history = useHistory(); | |||
| const userId = props.offer.userId; | |||
| @@ -66,7 +60,6 @@ const DeleteOffer = (props) => { | |||
| <DeleteOfferContainer> | |||
| <OfferInfo> | |||
| <OfferImageContainer> | |||
| {/* <OfferImage src={props.offer.images[0]} /> */} | |||
| <OfferImage | |||
| src={getImageUrl( | |||
| props.offer.images[0], | |||
| @@ -75,15 +68,10 @@ const DeleteOffer = (props) => { | |||
| )} | |||
| /> | |||
| </OfferImageContainer> | |||
| <OfferDescription> | |||
| <OfferDescriptionTitle>{props.offer.name}</OfferDescriptionTitle> | |||
| <OfferDescriptionCategory> | |||
| <CategoryIcon color="#c4c4c4" component="span" size="16px"> | |||
| <Category /> | |||
| </CategoryIcon> | |||
| {props.offer.category.name} | |||
| </OfferDescriptionCategory> | |||
| </OfferDescription> | |||
| <OfferDescription | |||
| offerName={props.offer.name} | |||
| categoryName={props.offer.category.name} | |||
| /> | |||
| <RemoveIconContainer> | |||
| <RemoveIcon /> | |||
| </RemoveIconContainer> | |||
| @@ -92,29 +80,8 @@ const DeleteOffer = (props) => { | |||
| <Trans i18nKey="deleteOffer.areYouSure" /> | |||
| </DeleteQuestion> | |||
| <ButtonsContainer> | |||
| <CancelButton | |||
| variant="contained" | |||
| height="48px" | |||
| width="180px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| onClick={closeDeleteModalHandler} | |||
| > | |||
| {t("deleteOffer.cancel")} | |||
| </CancelButton> | |||
| <SaveButton | |||
| type="submit" | |||
| variant="outlined" | |||
| height="48px" | |||
| width="180px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={selectedTheme.colors.primaryPurple} | |||
| onClick={removeOfferHandler} | |||
| > | |||
| {t("deleteOffer.delete")} | |||
| </SaveButton> | |||
| <CancelButton onClick={closeDeleteModalHandler} /> | |||
| <SaveButton onClick={removeOfferHandler} /> | |||
| </ButtonsContainer> | |||
| </DeleteOfferContainer> | |||
| </> | |||
| @@ -1,8 +1,6 @@ | |||
| import { Typography } from "@mui/material"; | |||
| import { Box } from "@mui/system"; | |||
| import styled from "styled-components"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Icon } from "../../../Icon/Icon"; | |||
| import { ReactComponent as Remove } from "../../../../assets/images/svg/trash-gold.svg"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { IconButton } from "../../../Buttons/IconButton/IconButton"; | |||
| @@ -60,36 +58,6 @@ export const OfferImage = styled.img` | |||
| border-radius: 2px; | |||
| `; | |||
| export const OfferDescription = styled(Box)` | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-left: 9px; | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Typography)` | |||
| font-size: 16px; | |||
| font-weight: 600; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| @media screen and (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const OfferDescriptionCategory = styled(Typography)` | |||
| font-size: 12px; | |||
| letter-spacing: 2%; | |||
| `; | |||
| export const CategoryIcon = styled(Icon)` | |||
| margin-right: 4px; | |||
| & svg { | |||
| width: 14px; | |||
| position: relative; | |||
| top: -1px; | |||
| } | |||
| `; | |||
| export const DeleteQuestion = styled(Typography)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| @@ -134,17 +102,3 @@ export const ButtonsContainer = styled(Box)` | |||
| width: 100%; | |||
| justify-content: space-between; | |||
| `; | |||
| export const CancelButton = styled(PrimaryButton)` | |||
| @media screen and (max-width: 600px) { | |||
| width: 140px; | |||
| height: 45px; | |||
| } | |||
| `; | |||
| export const SaveButton = styled(PrimaryButton)` | |||
| @media screen and (max-width: 600px) { | |||
| width: 140px; | |||
| height: 45px; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,35 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CategoryIcon, | |||
| CategoryIconContainer, | |||
| OfferDescriptionCategory, | |||
| OfferDescriptionContainer, | |||
| OfferDescriptionTitle, | |||
| } from "./OfferDescription.styled"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| const OfferDescription = (props) => { | |||
| return ( | |||
| <OfferDescriptionContainer> | |||
| <OfferDescriptionTitle>{props.offerName}</OfferDescriptionTitle> | |||
| <OfferDescriptionCategory> | |||
| <CategoryIconContainer | |||
| color={selectedTheme.colors.iconStrokeDisabledColor} | |||
| component="span" | |||
| size="16px" | |||
| > | |||
| <CategoryIcon /> | |||
| </CategoryIconContainer> | |||
| {props.categoryName} | |||
| </OfferDescriptionCategory> | |||
| </OfferDescriptionContainer> | |||
| ); | |||
| }; | |||
| OfferDescription.propTypes = { | |||
| offerName: PropTypes.string, | |||
| categoryName: PropTypes.string, | |||
| }; | |||
| export default OfferDescription; | |||
| @@ -0,0 +1,37 @@ | |||
| import { Box, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { Icon } from "../../../../Icon/Icon"; | |||
| import { ReactComponent as Category } from "../../../../../assets/images/svg/category.svg"; | |||
| export const OfferDescriptionContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-left: 9px; | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Typography)` | |||
| font-size: 16px; | |||
| font-weight: 600; | |||
| color: ${selectedTheme.colors.primaryPurple}; | |||
| @media screen and (max-width: 600px) { | |||
| font-size: 14px; | |||
| } | |||
| `; | |||
| export const OfferDescriptionCategory = styled(Typography)` | |||
| font-size: 12px; | |||
| letter-spacing: 2%; | |||
| `; | |||
| export const CategoryIconContainer = styled(Icon)` | |||
| margin-right: 4px; | |||
| & svg { | |||
| width: 14px; | |||
| position: relative; | |||
| top: -1px; | |||
| } | |||
| `; | |||
| export const CategoryIcon = styled(Category)` | |||
| ` | |||
| @@ -0,0 +1,29 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { SaveButtonContainer } from "./SaveButton.styled"; | |||
| const SaveButton = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <SaveButtonContainer | |||
| type="submit" | |||
| variant="outlined" | |||
| height="48px" | |||
| width="180px" | |||
| fullWidth | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={selectedTheme.colors.primaryPurple} | |||
| onClick={props.onClick} | |||
| > | |||
| {t("deleteOffer.delete")} | |||
| </SaveButtonContainer> | |||
| ); | |||
| }; | |||
| SaveButton.propTypes = { | |||
| onClick: PropTypes.func, | |||
| }; | |||
| export default SaveButton; | |||
| @@ -0,0 +1,9 @@ | |||
| import styled from "styled-components"; | |||
| import { PrimaryButton } from "../../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| export const SaveButtonContainer = styled(PrimaryButton)` | |||
| @media screen and (max-width: 600px) { | |||
| width: 140px; | |||
| height: 45px; | |||
| } | |||
| `; | |||
| @@ -1,7 +1,6 @@ | |||
| import React, { useMemo, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CheckButton, | |||
| DetailIcon, | |||
| DetailText, | |||
| EditIcon, | |||
| @@ -13,9 +12,6 @@ import { | |||
| OfferAuthorName, | |||
| OfferCardContainer, | |||
| OfferCategory, | |||
| OfferDescription, | |||
| OfferDescriptionText, | |||
| OfferDescriptionTitle, | |||
| OfferDetails, | |||
| OfferFlexContainer, | |||
| OfferImage, | |||
| @@ -34,13 +30,14 @@ import { | |||
| } from "./OfferCard.styled"; | |||
| import DeleteOffer from "./DeleteOffer/DeleteOffer"; | |||
| import { ReactComponent as Message } from "../../../assets/images/svg/mail.svg"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import CreateOffer from "../CreateOfferCard/CreateOffer"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import useIsMobile from "../../../hooks/useIsMobile"; | |||
| import { getImageUrl, variants } from "../../../util/helpers/imageUrlGetter"; | |||
| import OfferDescription from "./OfferDescription/OfferDescription"; | |||
| import CheckButton from "./CheckButton/CheckButton"; | |||
| const OfferCard = (props) => { | |||
| const [deleteOfferModal, setDeleteOfferModal] = useState(false); | |||
| @@ -93,17 +90,20 @@ const OfferCard = (props) => { | |||
| } | |||
| halfwidth={props.halfwidth ? 1 : 0} | |||
| > | |||
| {/* This only shows on vertical offer card */} | |||
| <OfferTitleAboveImage | |||
| vertical={props.vertical} | |||
| onClick={() => routeToItem(props?.offer?._id)} | |||
| > | |||
| {props?.offer?.name} | |||
| </OfferTitleAboveImage> | |||
| {/* ^^^^^^^ */} | |||
| <OfferFlexContainer vertical={props.vertical}> | |||
| <OfferImageContainer vertical={props.vertical}> | |||
| <OfferImage | |||
| src={ | |||
| props?.offer?.images | |||
| props?.offer?.images | |||
| ? getImageUrl( | |||
| props?.offer?.images[0], | |||
| variants.offerCard, | |||
| @@ -151,24 +151,11 @@ const OfferCard = (props) => { | |||
| {!props.halfwidth ? ( | |||
| <React.Fragment> | |||
| <Line /> | |||
| <OfferDescription> | |||
| <OfferDescriptionTitle>Opis:</OfferDescriptionTitle> | |||
| <OfferDescriptionText> | |||
| {props?.offer?.description} | |||
| </OfferDescriptionText> | |||
| </OfferDescription> | |||
| <OfferDescription description={props?.offer?.description} /> | |||
| <CheckButton | |||
| variant={props.sponsored ? "contained" : "outlined"} | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={ | |||
| props.sponsored ? "white" : selectedTheme.colors.primaryPurple | |||
| } | |||
| style={{ fontWeight: "600" }} | |||
| onClick={() => routeToItem(props?.offer?._id)} | |||
| > | |||
| Pogledaj proizvod | |||
| </CheckButton> | |||
| offerId={props?.offer?._id} | |||
| sponsored={props.sponsored} | |||
| /> | |||
| </React.Fragment> | |||
| ) : ( | |||
| <></> | |||
| @@ -2,7 +2,6 @@ import { Box, Container, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| import { IconButton } from "../../Buttons/IconButton/IconButton"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Icon } from "../../Icon/Icon"; | |||
| import { ReactComponent as Eye } from "../../../assets/images/svg/eye-striked.svg"; | |||
| import { ReactComponent as Remove } from "../../../assets/images/svg/trash.svg"; | |||
| @@ -195,35 +194,7 @@ export const OfferViews = styled(Box)` | |||
| display: none; | |||
| `} | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Box)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| line-height: 16px; | |||
| `; | |||
| export const OfferDescriptionText = styled(Box)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| line-height: 22px; | |||
| max-width: calc(100% - 230px); | |||
| max-height: 120px; | |||
| overflow: hidden; | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 5; | |||
| -webkit-box-orient: vertical; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const OfferDescription = styled(Box)` | |||
| flex: 3; | |||
| margin: auto 0; | |||
| padding-left: 35px; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const Line = styled(Box)` | |||
| border-left: 1px solid rgba(0, 0, 0, 0.15); | |||
| height: 100px; | |||
| @@ -249,20 +220,6 @@ export const DetailText = styled(Typography)` | |||
| top: -2px; | |||
| left: 3px; | |||
| `; | |||
| export const CheckButton = styled(PrimaryButton)` | |||
| width: 180px; | |||
| height: 48px; | |||
| position: absolute; | |||
| bottom: 9px; | |||
| right: 9px; | |||
| &:hover button { | |||
| background-color: ${selectedTheme.colors.primaryPurple} !important; | |||
| color: white !important; | |||
| } | |||
| @media (max-width: 650px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const MessageIcon = styled(IconButton)` | |||
| ${(props) => !props.showMessageIcon && "display: none;"} | |||
| width: 40px; | |||
| @@ -0,0 +1,26 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| OfferDescriptionContainer, | |||
| OfferDescriptionText, | |||
| OfferDescriptionTitle, | |||
| } from "./OfferDescription.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const OfferDescription = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <OfferDescriptionContainer> | |||
| <OfferDescriptionTitle> | |||
| {t("offer.descriptionLabel")} | |||
| </OfferDescriptionTitle> | |||
| <OfferDescriptionText>{props.description}</OfferDescriptionText> | |||
| </OfferDescriptionContainer> | |||
| ); | |||
| }; | |||
| OfferDescription.propTypes = { | |||
| description: PropTypes.string, | |||
| }; | |||
| export default OfferDescription; | |||
| @@ -0,0 +1,33 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const OfferDescriptionText = styled(Box)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 16px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| line-height: 22px; | |||
| max-width: calc(100% - 230px); | |||
| max-height: 120px; | |||
| overflow: hidden; | |||
| display: -webkit-box; | |||
| -webkit-line-clamp: 5; | |||
| -webkit-box-orient: vertical; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const OfferDescriptionContainer = styled(Box)` | |||
| flex: 3; | |||
| margin: auto 0; | |||
| padding-left: 35px; | |||
| @media (max-width: 1500px) { | |||
| display: none; | |||
| } | |||
| `; | |||
| export const OfferDescriptionTitle = styled(Box)` | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| font-size: 12px; | |||
| color: ${selectedTheme.colors.primaryDarkText}; | |||
| line-height: 16px; | |||
| `; | |||
| @@ -57,7 +57,6 @@ const SkeletonOfferCard = (props) => { | |||
| SkeletonOfferCard.propTypes = { | |||
| children: PropTypes.node, | |||
| skeleton: PropTypes.bool, | |||
| animationStage: PropTypes.number, | |||
| }; | |||
| export default SkeletonOfferCard; | |||
| @@ -0,0 +1,25 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { InputField, InputFieldLabel } from "../EditProfile.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const AppLinkField = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <> | |||
| <InputFieldLabel leftText={t("editProfile.applink").toUpperCase()} /> | |||
| <InputField | |||
| name="firmApplink" | |||
| values={props.formik.values.firmApplink} | |||
| margin="normal" | |||
| fullWidth | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| AppLinkField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default AppLinkField; | |||
| @@ -0,0 +1,167 @@ | |||
| import React, { useState, useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import BackdropComponent from "../../../MUI/BackdropComponent"; | |||
| import { | |||
| EditProfileContainer, | |||
| ProfileImageContainer, | |||
| BackButton, | |||
| CloseButton, | |||
| SaveButton, | |||
| ProfileHeader, | |||
| BasicInfo, | |||
| DetailsInfo, | |||
| ButtonsContainer, | |||
| ProfileImagePicker, | |||
| } from "./EditProfile.styled"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import { useFormik } from "formik"; | |||
| import { ReactComponent as ArrowBack } from "../../../../assets/images/svg/arrow-back.svg"; | |||
| import { ReactComponent as CloseIcon } from "../../../../assets/images/svg/close-modal.svg"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { | |||
| editMineProfile, | |||
| fetchMineProfile, | |||
| } from "../../../../store/actions/profile/profileActions"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectUserId } from "../../../../store/selectors/loginSelectors"; | |||
| import editProfileValidation from "../../../../validations/editProfileValidation"; | |||
| import useIsMobile from "../../../../hooks/useIsMobile"; | |||
| import { useMemo } from "react"; | |||
| import editProfileInitialValues from "../../../../initialValues/editProfileInitialValues"; | |||
| import FirmNameField from "./FirmNameField/FirmNameField"; | |||
| import PIBField from "./PIBField/PIBField"; | |||
| import LocationField from "./LocationField/LocationField"; | |||
| import WebsiteField from "./WebsiteField/WebsiteField"; | |||
| import AppLinkField from "./AppLinkField/AppLinkField"; | |||
| import PhoneField from "./PhoneField/PhoneField"; | |||
| import FormikErrorMessage from "./FormikErrorMessage/FormikErrorMessage"; | |||
| const EditProfile = (props) => { | |||
| const [profileImage, setProfileImage] = useState(props.profile.image); | |||
| const [showBasic, setShowBasic] = useState(true); | |||
| const [showDetails, setShowDetails] = useState(true); | |||
| const { t } = useTranslation(); | |||
| const dispatch = useDispatch(); | |||
| const { isMobile } = useIsMobile(); | |||
| const userId = useSelector(selectUserId); | |||
| useEffect(() => { | |||
| setShowDetails(!isMobile); | |||
| }, [isMobile]); | |||
| const handleApiResponseSuccess = () => { | |||
| dispatch(fetchMineProfile(userId)); | |||
| props.reFetchProfile(); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| dispatch(editMineProfile({ ...values, handleApiResponseSuccess })); | |||
| props.closeModalHandler(); | |||
| }; | |||
| const initialValues = useMemo( | |||
| () => editProfileInitialValues(props?.profile), | |||
| [props?.profile] | |||
| ); | |||
| const formik = useFormik({ | |||
| initialValues, | |||
| validationSchema: editProfileValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| const closeEditModalHandler = () => { | |||
| props.closeModalHandler(); | |||
| }; | |||
| const showDetailsHandler = () => { | |||
| setShowDetails(!showDetails); | |||
| setShowBasic(!showBasic); | |||
| }; | |||
| const setImage = (image) => { | |||
| setProfileImage(image); | |||
| }; | |||
| return ( | |||
| <> | |||
| <BackdropComponent | |||
| handleClose={closeEditModalHandler} | |||
| isLoading | |||
| position="fixed" | |||
| /> | |||
| <EditProfileContainer component="form" onSubmit={formik.handleSubmit}> | |||
| {!showBasic && ( | |||
| <BackButton onClick={showDetailsHandler}> | |||
| <ArrowBack /> | |||
| </BackButton> | |||
| )} | |||
| <ProfileImageContainer> | |||
| <ProfileImagePicker | |||
| image={profileImage} | |||
| setImage={setImage} | |||
| ></ProfileImagePicker> | |||
| <ProfileHeader>{props.profile.company.name}</ProfileHeader> | |||
| </ProfileImageContainer> | |||
| <CloseButton onClick={closeEditModalHandler}> | |||
| <CloseIcon /> | |||
| </CloseButton> | |||
| {showBasic && ( | |||
| <BasicInfo> | |||
| <FirmNameField formik={formik} /> | |||
| <PIBField formik={formik} /> | |||
| <LocationField formik={formik} /> | |||
| </BasicInfo> | |||
| )} | |||
| {showDetails && ( | |||
| <DetailsInfo> | |||
| <WebsiteField formik={formik} /> | |||
| <AppLinkField formik={formik} /> | |||
| <PhoneField formik={formik} /> | |||
| </DetailsInfo> | |||
| )} | |||
| <FormikErrorMessage formik={formik} /> | |||
| <ButtonsContainer> | |||
| {isMobile && ( | |||
| <> | |||
| <SaveButton | |||
| height="44px" | |||
| width="155px" | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor={selectedTheme.colors.primaryPurple} | |||
| onClick={showDetailsHandler} | |||
| > | |||
| {showDetails | |||
| ? t("editProfile.showBasic") | |||
| : t("editProfile.showDetails")} | |||
| </SaveButton> | |||
| </> | |||
| )} | |||
| <SaveButton | |||
| type="submit" | |||
| variant="contained" | |||
| height={isMobile ? "44px" : "48px"} | |||
| width={isMobile ? "155px" : "335px"} | |||
| buttoncolor={selectedTheme.colors.primaryPurple} | |||
| textcolor="white" | |||
| > | |||
| {t("common.save")} | |||
| </SaveButton> | |||
| </ButtonsContainer> | |||
| </EditProfileContainer> | |||
| </> | |||
| ); | |||
| }; | |||
| EditProfile.propTypes = { | |||
| children: PropTypes.node, | |||
| profile: PropTypes.any, | |||
| closeModalHandler: PropTypes.func, | |||
| setImage: PropTypes.func, | |||
| reFetchProfile: PropTypes.func, | |||
| }; | |||
| export default EditProfile; | |||
| @@ -1,9 +1,9 @@ | |||
| import styled from "styled-components"; | |||
| import { Box, TextField, Typography } from "@mui/material"; | |||
| import ImagePicker from "../../ImagePicker/ImagePicker"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Label } from "../../CheckBox/Label"; | |||
| import selectedTheme from "../../../themes"; | |||
| import ImagePicker from "../../../ImagePicker/ImagePicker"; | |||
| import { PrimaryButton } from "../../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import { Label } from "../../../CheckBox/Label"; | |||
| import selectedTheme from "../../../../themes"; | |||
| export const EditProfileContainer = styled(Box)` | |||
| background-color: #fff; | |||
| @@ -120,29 +120,6 @@ export const InputField = styled(TextField)` | |||
| } | |||
| `; | |||
| export const InputFieldLabelLocation = styled(Label)` | |||
| position: relative; | |||
| bottom: -8px; | |||
| margin: 10px 0; | |||
| & label { | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| line-height: 20px; | |||
| color: #808080; | |||
| cursor: auto; | |||
| letter-spacing: 0.2px; | |||
| } | |||
| @media screen and (max-width: 600px) { | |||
| bottom: -12px; | |||
| margin: 5px 0 17px 0; | |||
| & label { | |||
| font-size: 9px; | |||
| margin-top: 0; | |||
| } | |||
| } | |||
| `; | |||
| export const SaveButton = styled(PrimaryButton)` | |||
| font-size: 12px; | |||
| letter-spacing: 1.5px; | |||
| @@ -158,14 +135,6 @@ export const ButtonsContainer = styled(Box)` | |||
| } | |||
| `; | |||
| export const ErrorMessage = styled(Typography)` | |||
| color: red; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| position: relative; | |||
| top: 20px; | |||
| font-size: 14px; | |||
| `; | |||
| export const BasicInfo = styled(Box)``; | |||
| export const DetailsInfo = styled(Box)``; | |||
| @@ -0,0 +1,27 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { InputField, InputFieldLabel } from "../EditProfile.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const FirmNameField = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <> | |||
| <InputFieldLabel leftText={t("common.labelFirm").toUpperCase()} /> | |||
| <InputField | |||
| name="firmName" | |||
| value={props.formik.values.firmName} | |||
| onChange={props.formik.handleChange} | |||
| error={props.formik.touched.firmName && props.formik.errors.firmName} | |||
| margin="normal" | |||
| fullWidth | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| FirmNameField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default FirmNameField; | |||
| @@ -0,0 +1,28 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { ErrorMessage } from "./FormikErrorMessage.styled"; | |||
| const FormikErrorMessage = (props) => { | |||
| return ( | |||
| <> | |||
| {props.formik.errors.firmName && props.formik.touched.firmName ? ( | |||
| <ErrorMessage>{props.formik.errors.firmName}</ErrorMessage> | |||
| ) : props.formik.errors.firmPIB && props.formik.touched.firmPIB ? ( | |||
| <ErrorMessage>{props.formik.errors.firmPIB}</ErrorMessage> | |||
| ) : props.formik.errors.firmLocation && | |||
| props.formik.touched.firmLocation ? ( | |||
| <ErrorMessage>{props.formik.errors.firmLocation}</ErrorMessage> | |||
| ) : props.formik.errors.firmPhone && props.formik.touched.firmPhone ? ( | |||
| <ErrorMessage>{props.formik.errors.firmPhone}</ErrorMessage> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| FormikErrorMessage.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default FormikErrorMessage; | |||
| @@ -0,0 +1,11 @@ | |||
| import { Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| export const ErrorMessage = styled(Typography)` | |||
| color: red; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| position: relative; | |||
| top: 20px; | |||
| font-size: 14px; | |||
| `; | |||
| @@ -0,0 +1,42 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import AutoSuggestTextField from "../../../../TextFields/AutoSuggestTextField/AutoSuggestTextField"; | |||
| import { InputFieldLabelLocation } from "./LocationField.styled"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { selectLocations } from "../../../../../store/selectors/locationsSelectors"; | |||
| import { useEffect } from "react"; | |||
| import { fetchLocations } from "../../../../../store/actions/locations/locationsActions"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const LocationField = (props) => { | |||
| const { t } = useTranslation(); | |||
| const locations = useSelector(selectLocations); | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| if (locations?.length === 0) { | |||
| dispatch(fetchLocations()); | |||
| } | |||
| }, [locations]); | |||
| return ( | |||
| <> | |||
| <InputFieldLabelLocation | |||
| leftText={t("common.labelLocation").toUpperCase()} | |||
| /> | |||
| <AutoSuggestTextField | |||
| editLocation | |||
| data={locations.map((item) => ({ name: item.city }))} | |||
| value={props.formik.values.firmLocation} | |||
| onChange={(event, { newValue }) => | |||
| props.formik.setFieldValue("firmLocation", newValue) | |||
| } | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| LocationField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default LocationField; | |||
| @@ -0,0 +1,25 @@ | |||
| import styled from "styled-components"; | |||
| import { Label } from "../../../../CheckBox/Label"; | |||
| export const InputFieldLabelLocation = styled(Label)` | |||
| position: relative; | |||
| bottom: -8px; | |||
| margin: 10px 0; | |||
| & label { | |||
| font-size: 12px; | |||
| font-weight: 600; | |||
| line-height: 20px; | |||
| color: #808080; | |||
| cursor: auto; | |||
| letter-spacing: 0.2px; | |||
| } | |||
| @media screen and (max-width: 600px) { | |||
| bottom: -12px; | |||
| margin: 5px 0 17px 0; | |||
| & label { | |||
| font-size: 9px; | |||
| margin-top: 0; | |||
| } | |||
| } | |||
| `; | |||
| @@ -0,0 +1,29 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { InputField, InputFieldLabel } from "../EditProfile.styled"; | |||
| const PIBField = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <> | |||
| <InputFieldLabel leftText={t("common.labelPIB")} /> | |||
| <InputField | |||
| name="firmPIB" | |||
| type="number" | |||
| value={props.formik.values.firmPIB} | |||
| onChange={props.formik.handleChange} | |||
| error={props.formik.touched.firmPIB && props.formik.errors.firmPIB} | |||
| margin="normal" | |||
| fullWidth | |||
| disabled | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| PIBField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default PIBField; | |||
| @@ -0,0 +1,39 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { InputField, InputFieldLabel } from "../EditProfile.styled"; | |||
| const PhoneField = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <> | |||
| <InputFieldLabel leftText={t("editProfile.phoneNumber").toUpperCase()} /> | |||
| <InputField | |||
| type="number" | |||
| name="firmPhone" | |||
| value={props.formik.values.firmPhone} | |||
| onChange={(event) => { | |||
| props.formik.setFieldValue("firmPhone", event.target.value); | |||
| }} | |||
| error={props.formik.touched.firmPhone && props.formik.errors.firmPhone} | |||
| margin="normal" | |||
| fullWidth | |||
| onInput={(e) => { | |||
| e.target.value = | |||
| e.target.value[0] === "0" && e.target.value.length > 1 | |||
| ? "0" + | |||
| String( | |||
| Math.max(0, parseInt(e.target.value)).toString().slice(0, 14) | |||
| ) | |||
| : Math.max(0, parseInt(e.target.value)).toString().slice(0, 14); | |||
| }} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| PhoneField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default PhoneField; | |||
| @@ -0,0 +1,29 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { InputField, InputFieldLabel } from "../EditProfile.styled"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const WebsiteField = (props) => { | |||
| const { t } = useTranslation(); | |||
| return ( | |||
| <> | |||
| <InputFieldLabel | |||
| leftText={t("editProfile.website").toUpperCase()} | |||
| labelWebsite | |||
| /> | |||
| <InputField | |||
| name="firmWebsite" | |||
| value={props.formik.values.firmWebsite} | |||
| onChange={props.formik.handleChange} | |||
| margin="normal" | |||
| fullWidth | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| WebsiteField.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default WebsiteField; | |||
| @@ -13,23 +13,23 @@ import { | |||
| } from "./ProfileCard.styled"; | |||
| import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; | |||
| import { useRouteMatch } from "react-router-dom"; | |||
| import { fetchProfile } from "../../store/actions/profile/profileActions"; | |||
| import { fetchProfile } from "../../../store/actions/profile/profileActions"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { useEffect } from "react"; | |||
| import { selectProfile } from "../../store/selectors/profileSelectors"; | |||
| import { selectUserId } from "../../store/selectors/loginSelectors"; | |||
| import { selectProfile } from "../../../store/selectors/profileSelectors"; | |||
| import { selectUserId } from "../../../store/selectors/loginSelectors"; | |||
| import { useState } from "react"; | |||
| import { fetchProfileOffers } from "../../store/actions/offers/offersActions"; | |||
| import { fetchProfileOffers } from "../../../store/actions/offers/offersActions"; | |||
| import EditProfile from "./EditProfile/EditProfile"; | |||
| import ProfileMainInfo from "./ProfileMainInfo/ProfileMainInfo"; | |||
| import ProfileContact from "./ProfileContact/ProfileContact"; | |||
| import ProfileStats from "./ProfileStats/ProfileStats"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { PROFILE_SCOPE } from "../../store/actions/profile/profileActionConstants"; | |||
| import { selectIsLoadingByActionType } from "../../../store/selectors/loadingSelectors"; | |||
| import { PROFILE_SCOPE } from "../../../store/actions/profile/profileActionConstants"; | |||
| import SkeletonProfileCard from "./SkeletonProfileCard/SkeletonProfileCard"; | |||
| import { useMemo } from "react"; | |||
| import companyData from "../../notFoundData/companyData"; | |||
| import companyData from "../../../notFoundData/companyData"; | |||
| const ProfileCard = () => { | |||
| const [isMyProfile, setIsMyProfile] = useState(false); | |||
| @@ -51,7 +51,6 @@ const ProfileCard = () => { | |||
| return companyData; | |||
| }, [profileFromRedux]); | |||
| console.log("profile", profile); | |||
| useEffect(() => { | |||
| if (idProfile?.length > 0) { | |||