From b6a8385adcf55b5ee80c6976c7d6fdaf230e0c38 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Wed, 7 Oct 2020 19:58:43 +0200 Subject: [PATCH 01/23] WIP/ Feature :Register wizard coding style --- .../containers/Authentication/AuthInsider.js | 16 +- .../Authentication/RegisterLeftSidebar.js | 75 ++++ .../RegisterOrganizationForm.js | 368 ++++++++++++++++++ .../RegisterSubscriptionForm.js | 40 ++ .../Authentication/RegisterWizardPage.js | 17 + .../Authentication/RegisterWizardSteps.js | 36 ++ .../containers/Subscriptions/BillingTab.js | 5 +- client/src/lang/en/index.js | 21 + client/src/routes/authentication.js | 8 +- client/src/style/App.scss | 6 +- client/src/style/pages/authentication.scss | 99 ++--- client/src/style/pages/billing.scss | 14 +- .../src/style/pages/register-organizaton.scss | 83 ++++ .../src/style/pages/register-wizard-page.scss | 125 ++++++ 14 files changed, 843 insertions(+), 70 deletions(-) create mode 100644 client/src/containers/Authentication/RegisterLeftSidebar.js create mode 100644 client/src/containers/Authentication/RegisterOrganizationForm.js create mode 100644 client/src/containers/Authentication/RegisterSubscriptionForm.js create mode 100644 client/src/containers/Authentication/RegisterWizardPage.js create mode 100644 client/src/containers/Authentication/RegisterWizardSteps.js create mode 100644 client/src/style/pages/register-organizaton.scss create mode 100644 client/src/style/pages/register-wizard-page.scss diff --git a/client/src/containers/Authentication/AuthInsider.js b/client/src/containers/Authentication/AuthInsider.js index 7484693c5..8e52a4b28 100644 --- a/client/src/containers/Authentication/AuthInsider.js +++ b/client/src/containers/Authentication/AuthInsider.js @@ -7,23 +7,17 @@ export default function AuthInsider({ copyright = true, children, }) { - return (
- -
- -
- { children } + {/* */}
+
{children}
+
- ) -} \ No newline at end of file + ); +} diff --git a/client/src/containers/Authentication/RegisterLeftSidebar.js b/client/src/containers/Authentication/RegisterLeftSidebar.js new file mode 100644 index 000000000..3d1054d7a --- /dev/null +++ b/client/src/containers/Authentication/RegisterLeftSidebar.js @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import Icon from 'components/Icon'; +import { FormattedMessage as T, useIntl } from 'react-intl'; +import RegisterWizardSteps from './RegisterWizardSteps'; +import RegisterOrganizationForm from './RegisterOrganizationForm'; +import Register from './Register'; +import Login from './Login'; +import RegisterSubscriptionForm from './RegisterSubscriptionForm'; + +function RegisterLeftSidebar() { + const { formatMessage } = useIntl(); + const [org] = useState('LibyanSpider'); + + console.log(org, 'EE'); + return ( +
+
+
+
+
+ +
+

+ +

+ +

+ +

+
+ + + {org}, + + + + + + +
+ + +
+
+
+
+
+ + {/* */} + {/* */} + {/* */} + +
+
+
+ ); +} + +export default RegisterLeftSidebar; diff --git a/client/src/containers/Authentication/RegisterOrganizationForm.js b/client/src/containers/Authentication/RegisterOrganizationForm.js new file mode 100644 index 000000000..d23c684d7 --- /dev/null +++ b/client/src/containers/Authentication/RegisterOrganizationForm.js @@ -0,0 +1,368 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { Row, Col } from 'react-grid-system'; +import { + Button, + Intent, + FormGroup, + MenuItem, + Classes, + Position, +} from '@blueprintjs/core'; +import moment from 'moment'; +import classNames from 'classnames'; +import { TimezonePicker } from '@blueprintjs/timezone'; +import { FormattedMessage as T, useIntl } from 'react-intl'; +import { DateInput } from '@blueprintjs/datetime'; +import { momentFormatter, tansformDateValue } from 'utils'; +import AppToaster from 'components/AppToaster'; + +import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components'; + +function RegisterOrganizationForm() { + const { formatMessage } = useIntl(); + const [selected, setSelected] = useState(); + + const baseCurrency = [ + { id: 0, name: 'LYD - Libyan Dinar', value: 'LYD' }, + { id: 1, name: 'USD - American Dollar', value: 'USD' }, + ]; + + const languages = [ + { id: 0, name: 'English', value: 'en' }, + { id: 1, name: 'Arabic', value: 'ar' }, + ]; + + const fiscalYear = [ + { + id: 0, + name: `${formatMessage({ id: 'january' })} - ${formatMessage({ + id: 'december', + })}`, + value: 'january', + }, + { + id: 1, + name: `${formatMessage({ id: 'february' })} - ${formatMessage({ + id: 'january', + })}`, + value: 'february', + }, + { + id: 2, + name: `${formatMessage({ id: 'march' })} - ${formatMessage({ + id: 'february', + })}`, + value: 'March', + }, + { + id: 3, + name: `${formatMessage({ id: 'april' })} - ${formatMessage({ + id: 'march', + })}`, + value: 'april', + }, + { + id: 4, + name: `${formatMessage({ id: 'may' })} - ${formatMessage({ + id: 'april', + })}`, + value: 'may', + }, + { + id: 5, + name: `${formatMessage({ id: 'june' })} - ${formatMessage({ + id: 'may', + })}`, + value: 'june', + }, + { + id: 6, + name: `${formatMessage({ id: 'july' })} - ${formatMessage({ + id: 'june', + })}`, + value: 'july', + }, + { + id: 7, + name: `${formatMessage({ id: 'august' })} - ${formatMessage({ + id: 'july', + })}`, + value: 'August', + }, + { + id: 8, + name: `${formatMessage({ id: 'september' })} - ${formatMessage({ + id: 'august', + })}`, + value: 'september', + }, + { + id: 9, + name: `${formatMessage({ id: 'october' })} - ${formatMessage({ + id: 'november', + })}`, + value: 'october', + }, + { + id: 10, + name: `${formatMessage({ id: 'november' })} - ${formatMessage({ + id: 'october', + })}`, + value: 'november', + }, + { + id: 11, + name: `${formatMessage({ id: 'december' })} - ${formatMessage({ + id: 'november', + })}`, + value: 'december', + }, + ]; + + const ValidationSchema = Yup.object().shape({ + date_start: Yup.date().required(), + // .label(formatMessage({id:''})) + base_currency: Yup.string().required( + formatMessage({ id: 'base_currency_' }), + ), + language: Yup.string() + .required() + .label(formatMessage({ id: 'language' })), + fiscal_year: Yup.string() + .required() + .label(formatMessage({ id: 'fiscal_year_' })), + time_zone: Yup.string(), + }); + + const initialValues = useMemo(() => ({}), []); + + const { + values, + errors, + touched, + handleSubmit, + setFieldValue, + getFieldProps, + isSubmitting, + } = useFormik({ + enableReinitialize: true, + validationSchema: ValidationSchema, + initialValues: { + ...initialValues, + }, + onSubmit: (values, { setSubmitting, setErrors }) => {}, + }); + + const onItemsSelect = (filedName) => { + return (filed) => { + setSelected({ + ...selected, + [filedName]: filed, + }); + setFieldValue(filedName, filed.value); + }; + }; + + const filterItems = (query, item, _index, exactMatch) => { + const normalizedTitle = item.name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return normalizedTitle.indexOf(normalizedQuery) >= 0; + } + }; + + const onItemRenderer = (item, { handleClick }) => ( + + ); + + const handleTimeZoneChange = useCallback( + (time_zone) => { + setFieldValue('time_zone', time_zone); + }, + [setFieldValue], + ); + + const handleDateChange = useCallback( + (date_filed) => (date) => { + const formatted = moment(date).format('YYYY-MM-DD'); + setFieldValue(date_filed, formatted); + }, + [setFieldValue], + ); + + return ( +
+
+

+ +

+

+ +

+
+ +
+

+ +

+ + {/* financial starting date */} + } + labelInfo={} + intent={errors.date_start && touched.date_start && Intent.DANGER} + helperText={ + + } + > + + + + {/* base currency */} + + } + labelInfo={} + className={classNames( + 'form-group--base-currency', + 'form-group--select-list', + Classes.FILL, + )} + intent={ + errors.base_currency && touched.base_currency && Intent.DANGER + } + helperText={ + + } + > + } + itemRenderer={onItemRenderer} + popoverProps={{ minimal: true }} + onItemSelect={onItemsSelect('base_currency')} + itemPredicate={filterItems} + selectedItem={values.base_currency} + selectedItemProp={'value'} + // defaultText={} + defaultText={'LYD - Libyan Dinar'} + labelProp={'name'} + /> + + + + {/* language */} + + } + labelInfo={} + className={classNames( + 'form-group--language', + 'form-group--select-list', + Classes.FILL, + )} + intent={errors.language && touched.language && Intent.DANGER} + helperText={ + + } + > + } + itemRenderer={onItemRenderer} + popoverProps={{ minimal: true }} + onItemSelect={onItemsSelect('language')} + itemPredicate={filterItems} + selectedItem={values.language} + selectedItemProp={'value'} + defaultText={'English'} + // defaultText={} + labelProp={'name'} + /> + + + + {/* fiscal Year */} + } + labelInfo={} + className={classNames( + 'form-group--fiscal_year', + 'form-group--select-list', + Classes.FILL, + )} + intent={errors.fiscal_year && touched.fiscal_year && Intent.DANGER} + helperText={ + + } + > + } + itemRenderer={onItemRenderer} + popoverProps={{ minimal: true }} + onItemSelect={onItemsSelect('fiscal_year')} + itemPredicate={filterItems} + selectedItem={values.fiscal_year} + selectedItemProp={'value'} + defaultText={'January - December'} + // defaultText={} + labelProp={'name'} + /> + + + {/* Time zone */} + } + className={classNames( + 'form-group--time-zone', + 'form-group--select-list', + Classes.FILL, + )} + intent={errors.time_zone && touched.time_zone && Intent.DANGER} + helperText={ + + } + > + } + /> + + +

+ +

+
+ +
+
+
+ ); +} + +export default RegisterOrganizationForm; diff --git a/client/src/containers/Authentication/RegisterSubscriptionForm.js b/client/src/containers/Authentication/RegisterSubscriptionForm.js new file mode 100644 index 000000000..6274ac3aa --- /dev/null +++ b/client/src/containers/Authentication/RegisterSubscriptionForm.js @@ -0,0 +1,40 @@ +import React, { useMemo } from 'react'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { FormattedMessage as T, useIntl } from 'react-intl'; +import { Button, Intent } from '@blueprintjs/core'; +import BillingTab from 'containers/Subscriptions/BillingTab'; + +function RegisterSubscriptionForm({}) { + const ValidationSchema = Yup.object().shape({}); + + const initialValues = useMemo(() => ({}), []); + + const formik = useFormik({ + enableReinitialize: true, + validationSchema: ValidationSchema, + initialValues: { + ...initialValues, + }, + onSubmit: (values, { setSubmitting, setErrors }) => {}, + }); + return ( +
+
+ + +
+ +
+ +
+ ); +} + +export default RegisterSubscriptionForm; diff --git a/client/src/containers/Authentication/RegisterWizardPage.js b/client/src/containers/Authentication/RegisterWizardPage.js new file mode 100644 index 000000000..1ac0adca0 --- /dev/null +++ b/client/src/containers/Authentication/RegisterWizardPage.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { Redirect, Route, Switch, Link } from 'react-router-dom'; +import RegisterLeftSidebar from './RegisterLeftSidebar'; + +function RegisterWizardPage() { + return ( + <> + +
+ +
+
+ + ); +} + +export default RegisterWizardPage; diff --git a/client/src/containers/Authentication/RegisterWizardSteps.js b/client/src/containers/Authentication/RegisterWizardSteps.js new file mode 100644 index 000000000..f49c5e3cc --- /dev/null +++ b/client/src/containers/Authentication/RegisterWizardSteps.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { FormattedMessage as T, useIntl } from 'react-intl'; + +function RegisterWizardSteps() { + return ( +
+
+
    +
  • +

    + +

    +
  • +
  • +

    + +

    +
  • +
  • +

    + +

    +
  • + +
  • +

    + +

    +
  • +
+
+
+ ); +} + +export default RegisterWizardSteps; diff --git a/client/src/containers/Subscriptions/BillingTab.js b/client/src/containers/Subscriptions/BillingTab.js index df46a8977..2e7b9319b 100644 --- a/client/src/containers/Subscriptions/BillingTab.js +++ b/client/src/containers/Subscriptions/BillingTab.js @@ -121,7 +121,8 @@ function BillingTab({ formik }) {

@@ -135,7 +136,7 @@ function BillingTab({ formik }) {
- + diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 9098e57b0..190445456 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -740,4 +740,25 @@ export default { sell_account: 'Sell Account', cost_account: 'Cost Account', inventory_account: 'Inventory Account', + + register_a_new_organization_now: 'Register a New Organization now!.', + you_have_a_bigcapital_account: 'You have a Bigcapital account ', + contact_us_technical_support: 'Contact us - Technical Support', + let_s_get_started: 'Let’s Get Started', + tell_the_system_a_little_bit_about_your_organization: + 'Tell the system a little bit about your organization.', + organization_details: 'Organization details', + financial_starting_date: 'Financial starting date ', + base_currency: 'Base Currency', + note_you_can_change_your_preferences_later_in_dashboard_if_needed: + 'Note: You can change your preferences later in dashboard, if needed.', + save_continue: 'Save & Continue', + organization_register: 'Organization Register', + getting_started: 'Getting started', + payment_or_trial: 'Payment or trial', + initializing: 'Initializing', + fiscal_year_: 'Fiscal year', + welcome: 'Welcome ', + sign_out: 'Sign out', + we_re_here_to_help: 'We’re Here to Help!', }; diff --git a/client/src/routes/authentication.js b/client/src/routes/authentication.js index 0940781cc..1d9e11b0a 100644 --- a/client/src/routes/authentication.js +++ b/client/src/routes/authentication.js @@ -12,7 +12,7 @@ export default [ { path: `${BASE_URL}/register`, component: LazyLoader({ - loader: () => import('containers/Authentication/Register'), + loader: () => import('containers/Authentication/Register'), }), }, { @@ -33,4 +33,10 @@ export default [ loader: () => import('containers/Authentication/InviteAccept'), }), }, + { + path: `${BASE_URL}/wizard`, + component: LazyLoader({ + loader: () => import('containers/Authentication/RegisterWizardPage'), + }), + }, ]; diff --git a/client/src/style/App.scss b/client/src/style/App.scss index 0c6cdc201..520ce34f1 100644 --- a/client/src/style/App.scss +++ b/client/src/style/App.scss @@ -63,12 +63,14 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, @import 'pages/exchange-rate'; @import 'pages/customer'; @import 'pages/billing'; +@import 'pages/register-wizard-page.scss'; +@import 'pages/register-organizaton.scss'; // Views -@import 'views/filter-dropdown'; + @import 'views/filter-dropdown'; @import 'views/sidebar'; @import 'pages/estimate'; - + .App { min-width: 960px; } diff --git a/client/src/style/pages/authentication.scss b/client/src/style/pages/authentication.scss index 0d7cd6369..81d912177 100644 --- a/client/src/style/pages/authentication.scss +++ b/client/src/style/pages/authentication.scss @@ -1,30 +1,28 @@ - - -.authentication-insider{ +.authentication-insider { margin-top: 80px; margin-bottom: 40px; - &__logo-section{ + &__logo-section { text-align: center; margin-bottom: 60px; } - &__content{ + &__content { position: relative; } - &__footer{ - .auth-copyright{ + &__footer { + .auth-copyright { text-align: center; font-size: 12px; color: #666; - .bp3-icon-bigcapital{ + .bp3-icon-bigcapital { margin-top: 9px; - svg{ - path{ - fill: #A3A3A3; + svg { + path { + fill: #a3a3a3; } } } @@ -33,54 +31,55 @@ } .authentication-page { - - &__goto-bigcapital{ + &__goto-bigcapital { position: fixed; margin-top: 30px; margin-left: 30px; color: #777; } - + .bp3-input { - min-height: 40px; - border: 2px solid #E3E3E3; + min-height: 38px; + // border: 2px solid #E3E3E3; } - .bp3-form-group{ - margin-bottom: 25px; + // .bp3-form-group{ + // margin-bottom: 25px; - &.bp3-intent-danger{ - .bp3-input{ - border-color: #eea9a9; - } - } - } + // &.bp3-intent-danger{ + // .bp3-input{ + // border-color: #eea9a9; + // } + // } + // } - .bp3-form-group.has-password-revealer{ - - .bp3-label{ + .bp3-form-group.has-password-revealer { + .bp3-label { display: flex; justify-content: space-between; } - .password-revealer{ - .text{ + .password-revealer { + .text { font-size: 12px; } } } - .bp3-button.bp3-fill.bp3-intent-primary{ + .bp3-button.bp3-fill.bp3-intent-primary { font-size: 16px; } - &__label-section{ + &__label-section { margin-bottom: 34px; color: #555; - - h3{ - font-weight: 500; - font-size: 28px; - color: #444; + + h3 { + // font-weight: 500; + font-weight: 400; + // font-size: 28px; + font-size: 22px; + // color: #444; + color: #555555; margin: 0 0 12px; } @@ -92,24 +91,24 @@ &__form-wrapper { width: 100%; - max-width: 415px; - padding: 15px; + // max-width: 415px; + // padding: 15px; margin: 0 auto; } - &__footer-links{ + &__footer-links { padding: 9px; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; text-align: center; margin-bottom: 1.2rem; - a{ + a { color: #0052cc; } } - &__loading-overlay{ + &__loading-overlay { position: absolute; top: 0; left: 0; @@ -132,9 +131,11 @@ // Login Form // ------------------------------ .login-form { - - .checkbox{ - &--remember-me{ + width: 690px; + margin: 0px auto; + padding: 85px 50px; + .checkbox { + &--remember-me { margin: -4px 0 28px 0px; font-size: 14px; } @@ -143,10 +144,13 @@ // Register Form .register-form { + width: 690px; + margin: 0px auto; + padding: 85px 50px; &__agreement-section { margin-top: -10px; - + p { font-size: 13px; margin-top: -10px; @@ -166,7 +170,6 @@ } .invite-form { - &__statement-section { margin-top: -10px; p { @@ -176,8 +179,8 @@ } } - .authentication-page__loading-overlay{ + .authentication-page__loading-overlay { background: rgba(252, 253, 255, 0.9); } } -} \ No newline at end of file +} diff --git a/client/src/style/pages/billing.scss b/client/src/style/pages/billing.scss index 4ca989d44..cadd124e5 100644 --- a/client/src/style/pages/billing.scss +++ b/client/src/style/pages/billing.scss @@ -34,20 +34,20 @@ .plan-header { display: flex; justify-content: flex-start; - margin-bottom: 10px; } .plan-name { background: #3657ff; border-radius: 3px; - padding: 1px 8px 1px 8px; + padding: 2px 8px 2px 8px; font-size: 13px; color: #fff; - margin-bottom: 15px; + margin-bottom: 16px; + height: 21px; } .plan-description { font-size: 14px; font-weight: 400; - line-height: 2em; + line-height: 1.8rem; &.plan-description ul { list-style: none; @@ -113,10 +113,10 @@ .bg-title { font-size: 22px; font-weight: 400; - line-height: normal; + color: #666666; } .bg-message { - margin-bottom: 15px; + margin-bottom: 23px; font-size: 14px; } .license-container { @@ -139,9 +139,11 @@ } h4 { font-size: 18px; + font-weight: 400; color: #444444; } p { + margin-top: 15px; font-size: 14px; } } diff --git a/client/src/style/pages/register-organizaton.scss b/client/src/style/pages/register-organizaton.scss new file mode 100644 index 000000000..cf30a8958 --- /dev/null +++ b/client/src/style/pages/register-organizaton.scss @@ -0,0 +1,83 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +.register-organizaton-form { + width: 690px; + margin: 0px auto; + padding: 80px 50px; + + .register-org-title { + margin-bottom: 30px; + + h2 { + font-size: 22px; + font-weight: 400; + color: #555555; + line-height: 2em; + } + p { + font-size: 14px; + } + } + h3 { + font-size: 18px; + font-weight: 400; + color: #888888; + margin-bottom: 20px; + } + + .bp3-form-group { + .bp3-input-group { + .bp3-input { + position: relative; + width: 619px; + height: 38px; + } + } + } + + .form-group--base-currency, + .form-group--language { + .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) { + min-width: 300px; + min-height: 38px; + } + } + .form-group--language { + margin-left: 18px; + } + .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) { + min-width: 619px; + min-height: 38px; + } + + .form-group--time-zone { + .bp3-text-muted { + color: #000000; + } + .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) { + background: #fff; + box-shadow: 0 0 0 transparent; + border: 1px solid #ced4da; + padding: 8px; + } + } + + .register-org-note { + width: 618px; + font-size: 14px; + line-height: 2.7rem; + margin-bottom: 20px; + border-bottom: 3px solid #f5f5f5; + } + .register-org-button { + .bp3-button { + background-color: #0052cc; + width: 174px; + min-height: 40px; + } + } +} diff --git a/client/src/style/pages/register-wizard-page.scss b/client/src/style/pages/register-wizard-page.scss new file mode 100644 index 000000000..3e316f569 --- /dev/null +++ b/client/src/style/pages/register-wizard-page.scss @@ -0,0 +1,125 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +// Register Left Sidebar +.wizard-left-side { + position: fixed; + background: #778cab; + overflow: auto; + z-index: 1; + height: 100%; + max-width: 25%; + left: 0; + top: 0; + + .content { + display: flex; + flex-direction: column; + color: #ffffff; + padding: 25px; + margin: 0px auto; + border: none; + cursor: pointer; + .content-logo { + margin-bottom: 45px; + } + .content-title { + font-size: 25px; + font-weight: 700; + line-height: normal; + margin-bottom: 25px; + } + .content-text { + font-size: 14px; + } + .content-org { + font-size: 14px; + + span > a { + text-decoration: underline; + color: #ffffff; + margin-left: 5px; + } + span ::after { + border-top: 2px solid #707070; + } + } + .content-contact { + position: absolute; + bottom: 15px; + a { + text-decoration: none; + color: #ffffff; + font-size: 14px; + } + } + } +} +.wizard-right-side { + padding-left: 25%; +} + +// Register Wizard Steps +.wizard-container { + width: 80%; + margin: 60px auto; + + .wizard-wrapper li { + position: relative; + list-style-type: none; + width: 25%; + float: left; + text-align: center; + color: #000; + font-size: 15px; + } + .wizard-wrapper li::before { + width: 13px; + height: 13px; + content: ''; + line-height: 30px; + display: block; + text-align: center; + margin: 0 auto 10px auto; + border-radius: 50%; + background-color: #75859c; + } + .wizard-wrapper li::after { + width: 100%; + height: 2px; + content: ''; + position: absolute; + background-color: #75859c; + top: 6px; + left: -50%; + z-index: -1; + } + .wizard-wrapper li:first-child::after { + display: none; + } + + .wizard-wrapper > li.complete::before { + background-color: #75859c; + } + + .wizard-wrapper > li.complete ~ li::before { + background: #ebebeb; + } + + .wizard-wrapper > li.complete ~ li::after { + background: #ebebeb; + } + .wizard-wrapper > li.complete p.wizard-info { + color: #004dd0; + } +} + +// @import './billing.scss'; + +//Register Subscription form +.register-subscription-form { + padding-top: 50px; +} From 31a267a560cde34f66886bd1965754b9012c2f65 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Fri, 9 Oct 2020 19:28:51 +0200 Subject: [PATCH 02/23] Fix: RegisterOrganization --- .../src/containers/Authentication/Register.js | 62 ++++++-------- .../Authentication/RegisterLeftSidebar.js | 6 +- .../RegisterOrganizationForm.js | 82 +++++++++++++++---- .../withRegisterOrganizationActions.js | 12 +++ client/src/lang/en/index.js | 2 + .../organization/organization.actions.js | 28 +++++++ 6 files changed, 135 insertions(+), 57 deletions(-) create mode 100644 client/src/containers/Authentication/withRegisterOrganizationActions.js create mode 100644 client/src/store/organization/organization.actions.js diff --git a/client/src/containers/Authentication/Register.js b/client/src/containers/Authentication/Register.js index b35eb1bc0..2f15d64c7 100644 --- a/client/src/containers/Authentication/Register.js +++ b/client/src/containers/Authentication/Register.js @@ -22,7 +22,7 @@ import withAuthenticationActions from './withAuthenticationActions'; import { compose } from 'utils'; -function Register({ requestRegister }) { +function Register({ requestRegister, requestLogin }) { const { formatMessage } = useIntl(); const history = useHistory(); const [shown, setShown] = useState(false); @@ -31,9 +31,6 @@ function Register({ requestRegister }) { }, [shown]); const ValidationSchema = Yup.object().shape({ - organization_name: Yup.string() - .required() - .label(formatMessage({ id: 'organization_name_' })), first_name: Yup.string() .required() .label(formatMessage({ id: 'first_name_' })), @@ -56,7 +53,6 @@ function Register({ requestRegister }) { const initialValues = useMemo( () => ({ - organization_name: '', first_name: '', last_name: '', email: '', @@ -82,14 +78,28 @@ function Register({ requestRegister }) { onSubmit: (values, { setSubmitting, setErrors }) => { requestRegister(values) .then((response) => { - AppToaster.show({ - message: formatMessage({ - id: 'welcome_organization_account_has_been_created', - }), - intent: Intent.SUCCESS, - }); - setSubmitting(false); - history.push('/auth/login'); + // AppToaster.show({ + // message: formatMessage({ + // id: 'welcome_organization_account_has_been_created', + // }), + // intent: Intent.SUCCESS, + // }); + requestLogin({ + crediential: values.email, + password: values.password, + }) + .then(() => { + setSubmitting(false); + }) + .catch((errors) => { + AppToaster.show({ + message: formatMessage({ + id: 'something_wentwrong', + }), + intent: Intent.SUCCESS, + }); + }); + // history.push('/auth/login'); }) .catch((errors) => { if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) { @@ -150,31 +160,7 @@ function Register({ requestRegister }) {
- } - className={'form-group--name'} - intent={ - errors.organization_name && - touched.organization_name && - Intent.DANGER - } - helperText={ - - } - > - - - +
- - {/* */} + {/* */} {/* */} {/* */} - + + {/* */}
diff --git a/client/src/containers/Authentication/RegisterOrganizationForm.js b/client/src/containers/Authentication/RegisterOrganizationForm.js index d23c684d7..b35ae69ee 100644 --- a/client/src/containers/Authentication/RegisterOrganizationForm.js +++ b/client/src/containers/Authentication/RegisterOrganizationForm.js @@ -6,6 +6,7 @@ import { Button, Intent, FormGroup, + InputGroup, MenuItem, Classes, Position, @@ -17,12 +18,16 @@ import { FormattedMessage as T, useIntl } from 'react-intl'; import { DateInput } from '@blueprintjs/datetime'; import { momentFormatter, tansformDateValue } from 'utils'; import AppToaster from 'components/AppToaster'; - import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components'; +import { useHistory } from 'react-router-dom'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import withRegisterOrganizationActions from './withRegisterOrganizationActions'; +import { compose, optionsMapToArray } from 'utils'; -function RegisterOrganizationForm() { +function RegisterOrganizationForm({ requestSubmitOptions, requestSeedTenant }) { const { formatMessage } = useIntl(); const [selected, setSelected] = useState(); + const history = useHistory(); const baseCurrency = [ { id: 0, name: 'LYD - Libyan Dinar', value: 'LYD' }, @@ -122,11 +127,15 @@ function RegisterOrganizationForm() { ]; const ValidationSchema = Yup.object().shape({ - date_start: Yup.date().required(), - // .label(formatMessage({id:''})) - base_currency: Yup.string().required( - formatMessage({ id: 'base_currency_' }), - ), + name: Yup.string() + .required() + .label(formatMessage({ id: 'organization_name_' })), + date_start: Yup.date() + .required() + .label(formatMessage({ id: 'date_start_' })), + base_currency: Yup.string() + .required() + .label(formatMessage({ id: 'base_currency_' })), language: Yup.string() .required() .label(formatMessage({ id: 'language' })), @@ -136,7 +145,17 @@ function RegisterOrganizationForm() { time_zone: Yup.string(), }); - const initialValues = useMemo(() => ({}), []); + const initialValues = useMemo( + () => ({ + name: '', + date_start: moment(new Date()).format('YYYY-MM-DD'), + base_currency: '', + language: '', + fiscal_year: '', + time_zone: '', + }), + [], + ); const { values, @@ -152,7 +171,20 @@ function RegisterOrganizationForm() { initialValues: { ...initialValues, }, - onSubmit: (values, { setSubmitting, setErrors }) => {}, + onSubmit: (values, { setSubmitting, setErrors }) => { + const options = optionsMapToArray(values).map((option) => { + return { key: option.key, ...option, group: 'organization' }; + }); + requestSubmitOptions({ options }) + .then((response) => { + requestSeedTenant().then(() => { + setSubmitting(false); + }); + }) + .catch((erros) => { + setSubmitting(false); + }); + }, }); const onItemsSelect = (filedName) => { @@ -188,9 +220,9 @@ function RegisterOrganizationForm() { ); const handleDateChange = useCallback( - (date_filed) => (date) => { + (date) => { const formatted = moment(date).format('YYYY-MM-DD'); - setFieldValue(date_filed, formatted); + setFieldValue('date_start', formatted); }, [setFieldValue], ); @@ -211,20 +243,33 @@ function RegisterOrganizationForm() { + } + labelInfo={} + className={'form-group--name'} + intent={errors.name && touched.name && Intent.DANGER} + helperText={} + > + + + {/* financial starting date */} } labelInfo={} intent={errors.date_start && touched.date_start && Intent.DANGER} helperText={ - + } + className={classNames('form-group--select-list', Classes.FILL)} > @@ -237,6 +282,7 @@ function RegisterOrganizationForm() { className={classNames( 'form-group--base-currency', 'form-group--select-list', + Classes.LOADING, Classes.FILL, )} intent={ @@ -304,7 +350,7 @@ function RegisterOrganizationForm() { )} intent={errors.fiscal_year && touched.fiscal_year && Intent.DANGER} helperText={ - + } >
+ +
-
-

-
- - - {' '} - - - {' '} - - -

-
- -
- -
- - - -
- -
-
- - + +
+ +
+
+ ); } -export default compose(withAuthenticationActions)(Register); +export default compose(withAuthenticationActions)(RegisterUserForm); diff --git a/client/src/containers/Authentication/RegisterWizardSteps.js b/client/src/containers/Authentication/Register/RegisterWizardSteps.js similarity index 94% rename from client/src/containers/Authentication/RegisterWizardSteps.js rename to client/src/containers/Authentication/Register/RegisterWizardSteps.js index f49c5e3cc..3d390ffaf 100644 --- a/client/src/containers/Authentication/RegisterWizardSteps.js +++ b/client/src/containers/Authentication/Register/RegisterWizardSteps.js @@ -3,7 +3,7 @@ import { FormattedMessage as T, useIntl } from 'react-intl'; function RegisterWizardSteps() { return ( -
+
  • diff --git a/client/src/containers/Authentication/RegisterLeftSidebar.js b/client/src/containers/Authentication/RegisterLeftSidebar.js deleted file mode 100644 index fd3982581..000000000 --- a/client/src/containers/Authentication/RegisterLeftSidebar.js +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useState } from 'react'; -import Icon from 'components/Icon'; -import { FormattedMessage as T, useIntl } from 'react-intl'; -import RegisterWizardSteps from './RegisterWizardSteps'; -import RegisterOrganizationForm from './RegisterOrganizationForm'; -import Register from './Register'; -import Login from './Login'; -import RegisterSubscriptionForm from './RegisterSubscriptionForm'; - -function RegisterLeftSidebar() { - const { formatMessage } = useIntl(); - const [org] = useState('LibyanSpider'); - - console.log(org, 'EE'); - return ( -
    -
    -
    -
    -
    - -
    -

    - -

    - -

    - -

    -
    - - - {org}, - - - - - - -
    - - -
    -
    -
    -
    -
    - {/* */} - {/* */} - {/* */} - - {/* */} -
    -
    -
    - ); -} - -export default RegisterLeftSidebar; diff --git a/client/src/containers/Authentication/RegisterWizardPage.js b/client/src/containers/Authentication/RegisterWizardPage.js deleted file mode 100644 index 1ac0adca0..000000000 --- a/client/src/containers/Authentication/RegisterWizardPage.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { Redirect, Route, Switch, Link } from 'react-router-dom'; -import RegisterLeftSidebar from './RegisterLeftSidebar'; - -function RegisterWizardPage() { - return ( - <> - -
    - -
    -
    - - ); -} - -export default RegisterWizardPage; diff --git a/client/src/containers/Sales/Estimate/EntriesItemsTable.js b/client/src/containers/Sales/Estimate/EntriesItemsTable.js index 44192fcad..aeee8505e 100644 --- a/client/src/containers/Sales/Estimate/EntriesItemsTable.js +++ b/client/src/containers/Sales/Estimate/EntriesItemsTable.js @@ -3,7 +3,7 @@ import { omit } from 'lodash'; import { Button, Intent, Position, Tooltip } from '@blueprintjs/core'; import { FormattedMessage as T, useIntl } from 'react-intl'; -import CLASSES from 'components/classes'; +import { CLASSES } from 'common/classes'; import DataTable from 'components/DataTable'; import Icon from 'components/Icon'; import { diff --git a/client/src/routes/authentication.js b/client/src/routes/authentication.js index 1d9e11b0a..a4d1c32ac 100644 --- a/client/src/routes/authentication.js +++ b/client/src/routes/authentication.js @@ -9,12 +9,6 @@ export default [ loader: () => import('containers/Authentication/Login'), }), }, - { - path: `${BASE_URL}/register`, - component: LazyLoader({ - loader: () => import('containers/Authentication/Register'), - }), - }, { path: `${BASE_URL}/send_reset_password`, component: LazyLoader({ @@ -32,11 +26,5 @@ export default [ component: LazyLoader({ loader: () => import('containers/Authentication/InviteAccept'), }), - }, - { - path: `${BASE_URL}/wizard`, - component: LazyLoader({ - loader: () => import('containers/Authentication/RegisterWizardPage'), - }), - }, + } ]; diff --git a/client/src/routes/register.js b/client/src/routes/register.js new file mode 100644 index 000000000..44277cd21 --- /dev/null +++ b/client/src/routes/register.js @@ -0,0 +1,23 @@ +import LazyLoader from 'components/LazyLoader'; + +export default [ + + { + path: '/register/subscription', + component: LazyLoader({ + loader: () => import('containers/Authentication/Register/RegisterSubscriptionForm'), + }), + }, + { + path: '/register/organization', + component: LazyLoader({ + loader: () => import('containers/Authentication/Register/RegisterOrganizationForm'), + }), + }, + { + path: `/`, + component: LazyLoader({ + loader: () => import('containers/Authentication/Register/RegisterUserForm'), + }), + }, +]; diff --git a/client/src/style/pages/register-wizard-page.scss b/client/src/style/pages/register-wizard-page.scss index 3e316f569..57407a948 100644 --- a/client/src/style/pages/register-wizard-page.scss +++ b/client/src/style/pages/register-wizard-page.scss @@ -1,67 +1,66 @@ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} -// Register Left Sidebar -.wizard-left-side { - position: fixed; - background: #778cab; - overflow: auto; - z-index: 1; - height: 100%; - max-width: 25%; - left: 0; - top: 0; +.register-page { + + &__right-section { + padding-left: 25%; + } - .content { - display: flex; - flex-direction: column; - color: #ffffff; - padding: 25px; - margin: 0px auto; - border: none; - cursor: pointer; - .content-logo { - margin-bottom: 45px; - } - .content-title { - font-size: 25px; - font-weight: 700; - line-height: normal; - margin-bottom: 25px; - } - .content-text { - font-size: 14px; - } - .content-org { - font-size: 14px; + &__left-section{ + position: fixed; + background: #778cab; + overflow: auto; + z-index: 1; + height: 100%; + max-width: 25%; + left: 0; + top: 0; - span > a { - text-decoration: underline; - color: #ffffff; - margin-left: 5px; + .content { + display: flex; + flex-direction: column; + color: #ffffff; + padding: 25px; + margin: 0px auto; + border: none; + cursor: pointer; + .content-logo { + margin-bottom: 45px; } - span ::after { - border-top: 2px solid #707070; + .content-title { + font-size: 25px; + font-weight: 700; + line-height: normal; + margin-bottom: 25px; } - } - .content-contact { - position: absolute; - bottom: 15px; - a { - text-decoration: none; - color: #ffffff; + .content-text { font-size: 14px; } + .content-org { + font-size: 14px; + + span > a { + text-decoration: underline; + color: #ffffff; + margin-left: 5px; + } + span ::after { + border-top: 2px solid #707070; + } + } + .content-contact { + position: absolute; + bottom: 15px; + a { + text-decoration: none; + color: #ffffff; + font-size: 14px; + } + } } } } -.wizard-right-side { - padding-left: 25%; -} + // Register Wizard Steps .wizard-container { width: 80%; From 8622320eef56fd3579f976a30330a381efc8da53 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sat, 10 Oct 2020 17:04:51 +0200 Subject: [PATCH 04/23] fix: register wizard steps component. --- client/src/common/registerWizard.js | 16 +++++++ .../Register/RegisterWizardSteps.js | 46 +++++++++---------- .../containers/Subscriptions/BillingForm.js | 3 +- 3 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 client/src/common/registerWizard.js diff --git a/client/src/common/registerWizard.js b/client/src/common/registerWizard.js new file mode 100644 index 000000000..b7dedb9f3 --- /dev/null +++ b/client/src/common/registerWizard.js @@ -0,0 +1,16 @@ + + +export const registerWizardSteps = [ + { + label: 'organization_register', + }, + { + label: 'payment_or_trial', + }, + { + label: 'getting_started', + }, + { + label: 'initializing', + }, +]; \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterWizardSteps.js b/client/src/containers/Authentication/Register/RegisterWizardSteps.js index 3d390ffaf..b21a58a89 100644 --- a/client/src/containers/Authentication/Register/RegisterWizardSteps.js +++ b/client/src/containers/Authentication/Register/RegisterWizardSteps.js @@ -1,32 +1,32 @@ import React from 'react'; -import { FormattedMessage as T, useIntl } from 'react-intl'; +import classNames from 'classnames'; +import { FormattedMessage as T } from 'react-intl'; +import { registerWizardSteps } from 'common/registerWizard' -function RegisterWizardSteps() { +function RegisterWizardStep({ + label, + isActive = false +}) { + return ( +
  • +

    +
  • + ); +} + +function RegisterWizardSteps({ + currentStep = 1, +}) { return (
      -
    • -

      - -

      -
    • -
    • -

      - -

      -
    • -
    • -

      - -

      -
    • - -
    • -

      - -

      -
    • + {registerWizardSteps.map((step, index) => ( + + ))}
    diff --git a/client/src/containers/Subscriptions/BillingForm.js b/client/src/containers/Subscriptions/BillingForm.js index 33b1fafb8..458803c8b 100644 --- a/client/src/containers/Subscriptions/BillingForm.js +++ b/client/src/containers/Subscriptions/BillingForm.js @@ -55,7 +55,6 @@ function BillingForm({ initialValues: { ...initialValues, }, - onSubmit: (values, { setSubmitting, resetForm, setErrors }) => { requestSubmitBilling(values) .then((response) => { @@ -72,7 +71,7 @@ function BillingForm({ }); }, }); - console.log(formik.values, 'formik'); + return (
    From 0e6197241458f9e7c0e8c98e9df8c526201d4b98 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Sat, 10 Oct 2020 19:01:47 +0200 Subject: [PATCH 05/23] Fix: style authentication --- .../Authentication/ResetPassword.js | 19 ++++---- .../Authentication/SendResetPassword.js | 8 ++-- client/src/lang/en/index.js | 13 +++--- client/src/style/pages/authentication.scss | 44 +++++++++++-------- 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/client/src/containers/Authentication/ResetPassword.js b/client/src/containers/Authentication/ResetPassword.js index f824fdd67..c57a1863d 100644 --- a/client/src/containers/Authentication/ResetPassword.js +++ b/client/src/containers/Authentication/ResetPassword.js @@ -19,21 +19,20 @@ import withAuthenticationActions from './withAuthenticationActions'; import { compose } from 'utils'; - - function ResetPassword({ requestResetPassword }) { const { formatMessage } = useIntl(); const { token } = useParams(); const history = useHistory(); - const ValidationSchema = Yup.object().shape({ password: Yup.string() .min(4) - .required().label(formatMessage({id:'password'})), + .required() + .label(formatMessage({ id: 'password' })), confirm_password: Yup.string() .oneOf([Yup.ref('password'), null]) - .required().label(formatMessage({id:'confirm_password'})), + .required() + .label(formatMessage({ id: 'confirm_password' })), }); const initialValues = useMemo( @@ -41,7 +40,7 @@ function ResetPassword({ requestResetPassword }) { password: '', confirm_password: '', }), - [] + [], ); const { @@ -89,14 +88,14 @@ function ResetPassword({ requestResetPassword }) { {' '} - +
    } + label={} intent={errors.password && touched.password && Intent.DANGER} helperText={ @@ -144,10 +143,10 @@ function ResetPassword({ requestResetPassword }) { fill={true} className={'btn-new'} intent={Intent.PRIMARY} - type='submit' + type="submit" loading={isSubmitting} > - +
diff --git a/client/src/containers/Authentication/SendResetPassword.js b/client/src/containers/Authentication/SendResetPassword.js index 12195b289..637304e29 100644 --- a/client/src/containers/Authentication/SendResetPassword.js +++ b/client/src/containers/Authentication/SendResetPassword.js @@ -69,13 +69,13 @@ function SendResetPassword({ requestSendResetPassword }) { return ( -
+

- +

- +

@@ -104,7 +104,7 @@ function SendResetPassword({ requestSendResetPassword }) { fill={true} loading={isSubmitting} > - +
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 0462f75c0..83239f043 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -53,10 +53,10 @@ export default { you_remembered_your_password: 'You remembered your password ?', new_password: 'New Password', submit_new_password: 'Submit new password', - reset_your_password: 'Reset Your Password', - we_ll_send_you_a_link_to_reset_your_password: - 'Enter your email address and we’ll send you a link to reset your password.', - send_password_reset_link: 'Send password reset link', + you_can_t_login: 'You can’t login?', + we_ll_send_a_recovery_link_to_your_email: + 'We’ll send a recovery link to your email.', + send_reset_password_mail: 'Send Reset Password Mail', return_to_log_in: 'Return to log in', sub_account: 'Sub account?', account_type: 'Account Type', @@ -761,6 +761,7 @@ export default { welcome: 'Welcome ', sign_out: 'Sign out', we_re_here_to_help: 'We’re Here to Help!', - date_start_:'Date start', - something_wentwrong:'Something went wrong.' + date_start_: 'Date start', + something_wentwrong: 'Something went wrong.', + new_password: 'New password', }; diff --git a/client/src/style/pages/authentication.scss b/client/src/style/pages/authentication.scss index 81d912177..05379fe82 100644 --- a/client/src/style/pages/authentication.scss +++ b/client/src/style/pages/authentication.scss @@ -1,10 +1,12 @@ .authentication-insider { - margin-top: 80px; + width: 384px; + margin: 0 auto; margin-bottom: 40px; + margin-top: 40px; &__logo-section { text-align: center; - margin-bottom: 60px; + margin-bottom: 61px; } &__content { @@ -33,24 +35,24 @@ .authentication-page { &__goto-bigcapital { position: fixed; - margin-top: 30px; - margin-left: 30px; + margin-top: 40px; + margin-left: 40px; color: #777; } .bp3-input { - min-height: 38px; - // border: 2px solid #E3E3E3; + min-height: 40px; + border: 1px solid #ced4da; } - // .bp3-form-group{ - // margin-bottom: 25px; + .bp3-form-group { + margin-bottom: 25px; - // &.bp3-intent-danger{ - // .bp3-input{ - // border-color: #eea9a9; - // } - // } - // } + &.bp3-intent-danger { + .bp3-input { + border-color: #eea9a9; + } + } + } .bp3-form-group.has-password-revealer { .bp3-label { @@ -70,7 +72,7 @@ } &__label-section { - margin-bottom: 34px; + margin-bottom: 29px; color: #555; h3 { @@ -131,9 +133,10 @@ // Login Form // ------------------------------ .login-form { - width: 690px; - margin: 0px auto; - padding: 85px 50px; + // width: 690px; + // margin: 0px auto; + // padding: 85px 50px; + .checkbox { &--remember-me { margin: -4px 0 28px 0px; @@ -169,6 +172,11 @@ } } + .send-reset-password { + .form-group--crediential { + margin-bottom: 36px; + } + } .invite-form { &__statement-section { margin-top: -10px; From 507690fedfad961b613e866a5818b2d220e12a0c Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 11 Oct 2020 00:08:51 +0200 Subject: [PATCH 06/23] feat: register pages routes guards. feat: retrieve all organizations details to authenticated user. feat: redux organization reducers and actions. --- client/package.json | 2 + client/src/components/App.js | 2 +- client/src/components/Dashboard/Dashboard.js | 71 +++++++++++++------ .../Dashboard/DashboardLoadingIndicator.js | 25 +++++++ .../Dashboard/EnsureOrganizationIsReady.js | 16 +++++ client/src/containers/Authentication/Login.js | 3 + .../Register/RegisterLeftSection.js | 47 ++++++++---- .../Register/RegisterRightSection.js | 59 +++++++++++---- .../Register/RegisterUserForm.js | 8 +-- .../Organization/withOrganizationActions.js | 10 +++ .../Organization/withOrganizationByOrgId.js | 16 +++++ .../Organization/withOrganizationByTenId.js | 16 +++++ .../authentication/authentication.reducer.js | 8 +++ .../organizations/organizations.actions.js | 16 +++++ .../organizations/organizations.reducers.js | 25 +++++++ .../organizations/organizations.selectors.js | 18 +++++ .../organizations/organizations.types.js | 5 ++ client/src/store/reducers.js | 2 + client/src/store/types.js | 2 + server/src/api/controllers/Organization.ts | 29 +++++++- server/src/services/Organization/index.ts | 20 +++++- .../system/repositories/TenantRepository.ts | 14 +++- 22 files changed, 348 insertions(+), 66 deletions(-) create mode 100644 client/src/components/Dashboard/DashboardLoadingIndicator.js create mode 100644 client/src/components/Dashboard/EnsureOrganizationIsReady.js create mode 100644 client/src/containers/Organization/withOrganizationActions.js create mode 100644 client/src/containers/Organization/withOrganizationByOrgId.js create mode 100644 client/src/containers/Organization/withOrganizationByTenId.js create mode 100644 client/src/store/organizations/organizations.actions.js create mode 100644 client/src/store/organizations/organizations.reducers.js create mode 100644 client/src/store/organizations/organizations.selectors.js create mode 100644 client/src/store/organizations/organizations.types.js diff --git a/client/package.json b/client/package.json index 0fdd0e4ce..e88487f62 100644 --- a/client/package.json +++ b/client/package.json @@ -64,6 +64,7 @@ "postcss-preset-env": "6.7.0", "postcss-safe-parser": "4.0.1", "react": "^16.12.0", + "react-albus": "^2.0.0", "react-app-polyfill": "^1.0.6", "react-body-classname": "^1.3.1", "react-dev-utils": "^10.2.0", @@ -83,6 +84,7 @@ "react-split-pane": "^0.1.91", "react-table": "^7.0.0", "react-table-sticky": "^1.1.2", + "react-transition-group": "^4.4.1", "react-use": "^13.26.1", "react-window": "^1.8.5", "redux": "^4.0.5", diff --git a/client/src/components/App.js b/client/src/components/App.js index 51c3a8acb..aa7695535 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -20,7 +20,7 @@ function App({ locale }) { const queryConfig = { queries: { refetchOnWindowFocus: false, - } + }, }; return ( diff --git a/client/src/components/Dashboard/Dashboard.js b/client/src/components/Dashboard/Dashboard.js index 7be031c16..4b8dda00a 100644 --- a/client/src/components/Dashboard/Dashboard.js +++ b/client/src/components/Dashboard/Dashboard.js @@ -1,6 +1,8 @@ import React from 'react'; import { Switch, Route } from 'react-router'; -import classNames from 'classnames'; +import { useQuery } from 'react-query'; + +import DashboardLoadingIndicator from './DashboardLoadingIndicator'; import Sidebar from 'components/Sidebar/Sidebar'; import DashboardContent from 'components/Dashboard/DashboardContent'; @@ -9,29 +11,56 @@ import PreferencesContent from 'components/Preferences/PreferencesContent'; import PreferencesSidebar from 'components/Preferences/PreferencesSidebar'; import Search from 'containers/GeneralSearch/Search'; import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane'; +import EnsureOrganizationIsReady from './EnsureOrganizationIsReady'; -export default function Dashboard() { +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import withOrganizationsActions from 'containers/Organization/withOrganizationActions'; + +import { compose } from 'utils'; + + +function Dashboard({ + // #withSettings + requestFetchOptions, + + // #withOrganizations + requestOrganizationsList, +}) { + const fetchOrganizations = useQuery( + ['organizations'], + (key) => requestOrganizationsList(), + ); return ( -
- - - - - - - - + + + + + + + + + + - - - - - - - + + + + + + + - - -
+ + + + ); } + +export default compose( + withSettingsActions, + withOrganizationsActions, +)(Dashboard); \ No newline at end of file diff --git a/client/src/components/Dashboard/DashboardLoadingIndicator.js b/client/src/components/Dashboard/DashboardLoadingIndicator.js new file mode 100644 index 000000000..b07f92dd5 --- /dev/null +++ b/client/src/components/Dashboard/DashboardLoadingIndicator.js @@ -0,0 +1,25 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Choose, Icon } from 'components'; + +export default function Dashboard({ + isLoading = false, + children, +}) { + return ( +
+ + +
+ + Please wait while resources loading... +
+
+ + + { children } + +
+
+ ); +} diff --git a/client/src/components/Dashboard/EnsureOrganizationIsReady.js b/client/src/components/Dashboard/EnsureOrganizationIsReady.js new file mode 100644 index 000000000..a0b7e8929 --- /dev/null +++ b/client/src/components/Dashboard/EnsureOrganizationIsReady.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { Redirect } from 'react-router-dom'; + +export default function EnsureOrganizationIsReady({ + children, +}) { + const isOrganizationReady = false; + + return (isOrganizationReady) ? children : ( + + ); +} \ No newline at end of file diff --git a/client/src/containers/Authentication/Login.js b/client/src/containers/Authentication/Login.js index 9dc911ab9..fa73a5aab 100644 --- a/client/src/containers/Authentication/Login.js +++ b/client/src/containers/Authentication/Login.js @@ -19,6 +19,7 @@ import Icon from 'components/Icon'; import { If } from 'components'; import withAuthenticationActions from './withAuthenticationActions'; +import withOrganizationsActions from 'containers/Organization/withOrganizationActions'; import { compose } from 'utils'; @@ -29,6 +30,7 @@ const ERRORS_TYPES = { }; function Login({ requestLogin, + requestOrganizationsList, }) { const { formatMessage } = useIntl(); const history = useHistory(); @@ -168,4 +170,5 @@ function Login({ export default compose( withAuthenticationActions, + withOrganizationsActions, )(Login); \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterLeftSection.js b/client/src/containers/Authentication/Register/RegisterLeftSection.js index 3524a385e..aeb8ad0a0 100644 --- a/client/src/containers/Authentication/Register/RegisterLeftSection.js +++ b/client/src/containers/Authentication/Register/RegisterLeftSection.js @@ -1,12 +1,23 @@ -import React, { useState } from 'react'; -import { Icon } from 'components'; +import React, { useState, useCallback } from 'react'; +import { Icon, If } from 'components'; import { FormattedMessage as T } from 'react-intl'; -export default function RegisterLeftSection({ +import withAuthentication from 'containers/Authentication/withAuthentication'; +import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions'; +import { compose } from 'utils'; + + +function RegisterLeftSection({ + requestLogout, + isAuthorized }) { const [org] = useState('LibyanSpider'); + const onClickLogout = useCallback(() => { + requestLogout(); + }, [requestLogout]); + return (
@@ -27,17 +38,18 @@ export default function RegisterLeftSection({

-
- - - {org}, - - - - - - -
+ + +
+ + + {org}, + + + + +
+
) -} \ No newline at end of file +} + +export default compose( + withAuthentication(({ isAuthorized }) => ({ isAuthorized })), + withAuthenticationActions, +)(RegisterLeftSection); diff --git a/client/src/containers/Authentication/Register/RegisterRightSection.js b/client/src/containers/Authentication/Register/RegisterRightSection.js index 3f3231e26..ac628ac0b 100644 --- a/client/src/containers/Authentication/Register/RegisterRightSection.js +++ b/client/src/containers/Authentication/Register/RegisterRightSection.js @@ -1,26 +1,55 @@ import React from 'react'; -import { Route, Switch } from 'react-router-dom'; +import { TransitionGroup, CSSTransition } from 'react-transition-group'; +import { Wizard, Steps, Step } from 'react-albus'; +import { useHistory } from "react-router-dom"; import RegisterWizardSteps from './RegisterWizardSteps'; -import registerRoutes from 'routes/register'; +import PrivateRoute from 'components/PrivateRoute'; + +import RegisterUserForm from 'containers/Authentication/Register/RegisterUserForm'; +import RegisterSubscriptionForm from 'containers/Authentication/Register/RegisterSubscriptionForm'; +import RegisterOrganizationForm from 'containers/Authentication/Register/RegisterOrganizationForm'; export default function RegisterRightSection () { + const history = useHistory(); return (
- + ( +
+ -
- - { registerRoutes.map((route, index) => ( - - )) } - -
+ + +
+ + + + + + + + + + + + + + +

Ice King

+
+
+
+
+
+
+ )} />
) } \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterUserForm.js b/client/src/containers/Authentication/Register/RegisterUserForm.js index d2d05892b..7dd592543 100644 --- a/client/src/containers/Authentication/Register/RegisterUserForm.js +++ b/client/src/containers/Authentication/Register/RegisterUserForm.js @@ -77,17 +77,12 @@ function RegisterUserForm({ requestRegister, requestLogin }) { onSubmit: (values, { setSubmitting, setErrors }) => { requestRegister(values) .then((response) => { - // AppToaster.show({ - // message: formatMessage({ - // id: 'welcome_organization_account_has_been_created', - // }), - // intent: Intent.SUCCESS, - // }); requestLogin({ crediential: values.email, password: values.password, }) .then(() => { + history.push('/register/subscription'); setSubmitting(false); }) .catch((errors) => { @@ -98,7 +93,6 @@ function RegisterUserForm({ requestRegister, requestLogin }) { intent: Intent.SUCCESS, }); }); - // history.push('/auth/login'); }) .catch((errors) => { if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) { diff --git a/client/src/containers/Organization/withOrganizationActions.js b/client/src/containers/Organization/withOrganizationActions.js new file mode 100644 index 000000000..2a23bb6bb --- /dev/null +++ b/client/src/containers/Organization/withOrganizationActions.js @@ -0,0 +1,10 @@ +import { connect } from 'react-redux'; +import { + fetchOrganizations, +} from 'store/organizations/organizations.actions'; + +export const mapDispatchToProps = (dispatch) => ({ + requestOrganizationsList: () => dispatch(fetchOrganizations()), +}); + +export default connect(null, mapDispatchToProps); \ No newline at end of file diff --git a/client/src/containers/Organization/withOrganizationByOrgId.js b/client/src/containers/Organization/withOrganizationByOrgId.js new file mode 100644 index 000000000..b07c89167 --- /dev/null +++ b/client/src/containers/Organization/withOrganizationByOrgId.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; +import { + getOrganizationByOrgIdFactory, +} from 'store/organizations/organizations.selector'; + +export default (mapState) => { + const getOrganizationByOrgId = getOrganizationByOrgIdFactory(); + + const mapStateToProps = (state, props) => { + const mapped = { + organization: getOrganizationByOrgId(state, props), + }; + return mapState ? mapState(mapped, state, props) : mapped; + }; + return connect(mapStateToProps); +}; \ No newline at end of file diff --git a/client/src/containers/Organization/withOrganizationByTenId.js b/client/src/containers/Organization/withOrganizationByTenId.js new file mode 100644 index 000000000..998308914 --- /dev/null +++ b/client/src/containers/Organization/withOrganizationByTenId.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; +import { + getOrganizationByTenantIdFactory, +} from 'store/organizations/organizations.selector'; + +export default (mapState) => { + const getOrgByTenId = getOrganizationByTenantIdFactory(); + + const mapStateToProps = (state, props) => { + const mapped = { + organization: getOrgByTenId(state, props), + }; + return mapState ? mapState(mapped, state, props) : mapped; + }; + return connect(mapStateToProps); +}; \ No newline at end of file diff --git a/client/src/store/authentication/authentication.reducer.js b/client/src/store/authentication/authentication.reducer.js index 15a4d281f..4cd2f0adc 100644 --- a/client/src/store/authentication/authentication.reducer.js +++ b/client/src/store/authentication/authentication.reducer.js @@ -5,6 +5,7 @@ const initialState = { token: '', organization: '', user: '', + tenant: {}, locale: '', errors: [], }; @@ -15,6 +16,7 @@ export default createReducer(initialState, { state.token = token; state.user = user; state.organization = tenant.organization_id; + state.tenant = tenant; }, [t.LOGIN_FAILURE]: (state, action) => { @@ -36,3 +38,9 @@ export const isAuthenticated = (state) => !!state.authentication.token; export const hasErrorType = (state, errorType) => { return state.authentication.errors.find((e) => e.type === errorType); }; + +export const isTenantSeeded = (state) => !!state.tenant.seeded_at; +export const isTenantBuilt = (state) => !!state.tenant.initialized_at; + +export const isTenantHasSubscription = () => false; +export const isTenantSubscriptionExpired = () => false; \ No newline at end of file diff --git a/client/src/store/organizations/organizations.actions.js b/client/src/store/organizations/organizations.actions.js new file mode 100644 index 000000000..fec7dee04 --- /dev/null +++ b/client/src/store/organizations/organizations.actions.js @@ -0,0 +1,16 @@ +import ApiService from 'services/ApiService'; +import t from 'store/types'; + +export const fetchOrganizations = () => { + return (dispatch) => new Promise((resolve, reject) => { + ApiService.get('organization/all').then((response) => { + dispatch({ + type: t.ORGANIZATIONS_LIST_SET, + payload: { + organizations: response.data.organizations, + }, + }); + resolve(response) + }).catch(error => { reject(error); }); + }); +}; \ No newline at end of file diff --git a/client/src/store/organizations/organizations.reducers.js b/client/src/store/organizations/organizations.reducers.js new file mode 100644 index 000000000..b8407108d --- /dev/null +++ b/client/src/store/organizations/organizations.reducers.js @@ -0,0 +1,25 @@ +import { createReducer } from '@reduxjs/toolkit'; +import t from 'store/types'; + +const initialState = { + data: {}, + byOrganizationId: {}, +}; + +const reducer = createReducer(initialState, { + + [t.ORGANIZATIONS_LIST_SET]: (state, action) => { + const { organizations } = action.payload; + const _data = {}; + const _dataByOrganizationId = {}; + + organizations.forEach((organization) => { + _data[organization.id] = organization; + _dataByOrganizationId[organization.organization_id] = organization.id; + }); + state.data = _data; + state.byOrganizationId = _dataByOrganizationId; + }, +}) + +export default reducer; \ No newline at end of file diff --git a/client/src/store/organizations/organizations.selectors.js b/client/src/store/organizations/organizations.selectors.js new file mode 100644 index 000000000..4d5368b93 --- /dev/null +++ b/client/src/store/organizations/organizations.selectors.js @@ -0,0 +1,18 @@ +import { createSelector } from '@reduxjs/toolkit'; + +const oragnizationByTenantIdSelector = (state, props) => state.organizations[props.tenantId]; +const organizationByIdSelector = (state, props) => state.organizations.byOrganizationId[props.organizationId]; + +export const getOrganizationByOrgIdFactory = () => createSelector( + organizationByIdSelector, + (organization) => { + return organization; + }, +); + +export const getOrganizationByTenantIdFactory = () => createSelector( + oragnizationByTenantIdSelector, + (organization) => { + return organization; + } +) \ No newline at end of file diff --git a/client/src/store/organizations/organizations.types.js b/client/src/store/organizations/organizations.types.js new file mode 100644 index 000000000..1b9d47692 --- /dev/null +++ b/client/src/store/organizations/organizations.types.js @@ -0,0 +1,5 @@ + + +export default { + ORGANIZATIONS_LIST_SET: 'ORGANIZATIONS_LIST_SET', +}; \ No newline at end of file diff --git a/client/src/store/reducers.js b/client/src/store/reducers.js index 2e6e5f46e..2827e1eed 100644 --- a/client/src/store/reducers.js +++ b/client/src/store/reducers.js @@ -25,9 +25,11 @@ import bills from './Bills/bills.reducer'; import vendors from './vendors/vendors.reducer'; import paymentReceives from './PaymentReceive/paymentReceive.reducer'; import paymentMades from './PaymentMades/paymentMade.reducer'; +import organizations from './organizations/organizations.reducers'; export default combineReducers({ authentication, + organizations, dashboard, users, accounts, diff --git a/client/src/store/types.js b/client/src/store/types.js index b0adaa6cb..755bad8dc 100644 --- a/client/src/store/types.js +++ b/client/src/store/types.js @@ -24,6 +24,7 @@ import bills from './Bills/bills.type'; import vendors from './vendors/vendors.types'; import paymentReceives from './PaymentReceive/paymentReceive.type'; import paymentMades from './PaymentMades/paymentMade.type'; +import organizations from './organizations/organizations.types'; export default { ...authentication, @@ -52,4 +53,5 @@ export default { ...bills, ...paymentReceives, ...paymentMades, + ...organizations, }; diff --git a/server/src/api/controllers/Organization.ts b/server/src/api/controllers/Organization.ts index 9d2351e05..5c298d0c1 100644 --- a/server/src/api/controllers/Organization.ts +++ b/server/src/api/controllers/Organization.ts @@ -1,5 +1,5 @@ import { Inject, Service } from 'typedi'; -import { Router, Request, Response } from 'express'; +import { Router, Request, Response, NextFunction } from 'express'; import asyncMiddleware from "api/middleware/asyncMiddleware"; import JWTAuth from 'api/middleware/jwtAuth'; import TenancyMiddleware from 'api/middleware/TenancyMiddleware'; @@ -27,12 +27,14 @@ export default class OrganizationController extends BaseController{ router.use(JWTAuth); router.use(AttachCurrentTenantUser); router.use(TenancyMiddleware); - router.use(SubscriptionMiddleware('main')); - + // Should to seed organization tenant be configured. + router.use('/seed', SubscriptionMiddleware('main')); router.use('/seed', SettingsMiddleware); router.use('/seed', EnsureConfiguredMiddleware); + router.use('/build', SubscriptionMiddleware('main')); + router.post( '/build', asyncMiddleware(this.build.bind(this)) @@ -41,6 +43,10 @@ export default class OrganizationController extends BaseController{ '/seed', asyncMiddleware(this.seed.bind(this)), ); + router.get( + '/all', + asyncMiddleware(this.allOrganizations.bind(this)), + ); return router; } @@ -116,4 +122,21 @@ export default class OrganizationController extends BaseController{ next(error); } } + + /** + * Listing all organizations that assocaited to the authorized user. + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ + async allOrganizations(req: Request, res: Response, next: NextFunction) { + const { user } = req; + + try { + const organizations = await this.organizationService.listOrganizations(user); + return res.status(200).send({ organizations }); + } catch (error) { + next(error); + } + } } \ No newline at end of file diff --git a/server/src/services/Organization/index.ts b/server/src/services/Organization/index.ts index 88ef7515a..48fde80b3 100644 --- a/server/src/services/Organization/index.ts +++ b/server/src/services/Organization/index.ts @@ -1,6 +1,6 @@ import { Service, Inject } from 'typedi'; import { ServiceError } from 'exceptions'; -import { ITenant } from 'interfaces'; +import { ISystemService, ISystemUser, ITenant } from 'interfaces'; import { EventDispatcher, EventDispatcherInterface, @@ -38,7 +38,7 @@ export default class OrganizationService { * @param {srting} organizationId * @return {Promise} */ - async build(organizationId: string): Promise { + public async build(organizationId: string): Promise { const tenant = await this.getTenantByOrgIdOrThrowError(organizationId); this.throwIfTenantInitizalized(tenant); @@ -69,7 +69,7 @@ export default class OrganizationService { * @param {number} organizationId * @return {Promise} */ - async seed(organizationId: string): Promise { + public async seed(organizationId: string): Promise { const tenant = await this.getTenantByOrgIdOrThrowError(organizationId); this.throwIfTenantSeeded(tenant); @@ -91,6 +91,20 @@ export default class OrganizationService { } } + /** + * Listing all associated organizations to the given user. + * @param {ISystemUser} user - + * @return {Promise} + */ + public async listOrganizations(user: ISystemUser): Promise { + this.logger.info('[organization] trying to list all organizations.', { user }); + + const { tenantRepository } = this.sysRepositories; + const tenant = await tenantRepository.getByIdWithSubscriptions(user.tenantId); + + return [tenant]; + } + /** * Throws error in case the given tenant is undefined. * @param {ITenant} tenant diff --git a/server/src/system/repositories/TenantRepository.ts b/server/src/system/repositories/TenantRepository.ts index 23de76a6b..b5392740d 100644 --- a/server/src/system/repositories/TenantRepository.ts +++ b/server/src/system/repositories/TenantRepository.ts @@ -63,11 +63,23 @@ export default class TenantRepository extends SystemRepository { /** * Retrieve tenant details by the given tenant id. - * @param {string} tenantId + * @param {string} tenantId - Tenant id. */ getById(tenantId: number) { return this.cache.get(`tenant.id.${tenantId}`, () => { return Tenant.query().findById(tenantId); }); } + + /** + * Retrieve tenant details with associated subscriptions + * and plans by the given tenant id. + * @param {number} tenantId - Tenant id. + */ + getByIdWithSubscriptions(tenantId: number) { + return this.cache.get(`tenant.id.${tenantId}.subscriptions`, () => { + return Tenant.query().findById(tenantId) + .withGraphFetched('subscriptions.plan'); + }); + } } \ No newline at end of file From 06286e6c18232d8c562fe112f08312a1e8790743 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Sun, 11 Oct 2020 14:39:04 +0200 Subject: [PATCH 07/23] fix: Register & subscription Page style --- .../Register/RegisterWizardSteps.js | 2 +- client/src/style/pages/authentication.scss | 45 +++--- .../src/style/pages/register-wizard-page.scss | 135 ++++++++++++++++-- 3 files changed, 151 insertions(+), 31 deletions(-) diff --git a/client/src/containers/Authentication/Register/RegisterWizardSteps.js b/client/src/containers/Authentication/Register/RegisterWizardSteps.js index b21a58a89..b97255815 100644 --- a/client/src/containers/Authentication/Register/RegisterWizardSteps.js +++ b/client/src/containers/Authentication/Register/RegisterWizardSteps.js @@ -24,7 +24,7 @@ function RegisterWizardSteps({ {registerWizardSteps.map((step, index) => ( ))} diff --git a/client/src/style/pages/authentication.scss b/client/src/style/pages/authentication.scss index 05379fe82..81e1c4e75 100644 --- a/client/src/style/pages/authentication.scss +++ b/client/src/style/pages/authentication.scss @@ -145,32 +145,33 @@ } } - // Register Form - .register-form { - width: 690px; - margin: 0px auto; - padding: 85px 50px; + // // Register Form + // .register-form { + // // width: 690px; + // // margin: 0px auto; + // // padding: 85px 50px; + - &__agreement-section { - margin-top: -10px; + // &__agreement-section { + // margin-top: -10px; - p { - font-size: 13px; - margin-top: -10px; - margin-bottom: 24px; - line-height: 1.65; - } - } + // p { + // font-size: 13px; + // margin-top: -10px; + // margin-bottom: 24px; + // line-height: 1.65; + // } + // } - &__submit-button-wrap { - margin: 25px 0px 25px 0px; + // &__submit-button-wrap { + // margin: 25px 0px 25px 0px; - .bp3-button { - min-height: 45px; - background-color: #0052cc; - } - } - } + // .bp3-button { + // min-height: 45px; + // background-color: #0052cc; + // } + // } + // } .send-reset-password { .form-group--crediential { diff --git a/client/src/style/pages/register-wizard-page.scss b/client/src/style/pages/register-wizard-page.scss index 57407a948..2fc911bcb 100644 --- a/client/src/style/pages/register-wizard-page.scss +++ b/client/src/style/pages/register-wizard-page.scss @@ -1,11 +1,98 @@ - .register-page { - + .bp3-input { + min-height: 40px; + border: 1px solid #ced4da; + } + .bp3-form-group { + margin-bottom: 23px; + + &.bp3-intent-danger { + .bp3-input { + border-color: #eea9a9; + } + } + } + .bp3-form-group.has-password-revealer { + .bp3-label { + display: flex; + justify-content: space-between; + } + + .password-revealer { + .text { + font-size: 12px; + } + } + } + + .bp3-button.bp3-fill.bp3-intent-primary { + font-size: 16px; + } + + &__label-section { + margin-bottom: 29px; + color: #555; + + h3 { + // font-weight: 500; + font-weight: 400; + // font-size: 28px; + font-size: 22px; + // color: #444; + color: #555555; + margin: 0 0 12px; + } + + a { + text-decoration: underline; + color: #0040bd; + } + } + + &__form-wrapper { + width: 100%; + // max-width: 415px; + // padding: 15px; + margin: 0 auto; + } + + &__footer-links { + padding: 9px; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + text-align: center; + margin-bottom: 1.2rem; + + a { + color: #0052cc; + } + } + + &__loading-overlay { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: rgba(252, 253, 255, 0.5); + display: flex; + justify-content: center; + } + + &__submit-button-wrap { + margin: 0px 0px 24px 0px; + + .bp3-button { + background-color: #0052cc; + min-height: 45px; + } + } + &__right-section { padding-left: 25%; } - &__left-section{ + &__left-section { position: fixed; background: #778cab; overflow: auto; @@ -58,9 +145,41 @@ } } } + &-form { + width: 800px; + margin: 0 auto; + + // width: 690px; + // padding: 85px 60px; + // padding: 85px 105px; + + // Register Form + .register-form { + padding: 85px 105px; + + &__agreement-section { + margin-top: -10px; + + p { + font-size: 13px; + margin-top: -10px; + margin-bottom: 24px; + line-height: 1.65; + } + } + + &__submit-button-wrap { + margin: 25px 0px 25px 0px; + + .bp3-button { + min-height: 45px; + background-color: #0052cc; + } + } + } + } } - // Register Wizard Steps .wizard-container { width: 80%; @@ -100,18 +219,18 @@ display: none; } - .wizard-wrapper > li.complete::before { + .wizard-wrapper > li.is-active::before { background-color: #75859c; } - .wizard-wrapper > li.complete ~ li::before { + .wizard-wrapper > li.is-active ~ li::before { background: #ebebeb; } - .wizard-wrapper > li.complete ~ li::after { + .wizard-wrapper > li.is-active ~ li::after { background: #ebebeb; } - .wizard-wrapper > li.complete p.wizard-info { + .wizard-wrapper > li.is-active p.wizard-info { color: #004dd0; } } From e15a48dcdddee27a2e3bc59c667e524e00356844 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 11 Oct 2020 20:35:01 +0200 Subject: [PATCH 08/23] refactoring: setup wizard pages with simple architecture. --- client/src/components/App.js | 9 +- client/src/components/Dashboard/Dashboard.js | 18 +- .../Dashboard/DashboardLoadingIndicator.js | 5 +- .../Dashboard/EnsureOrganizationIsReady.js | 29 +- .../src/components/Dashboard/PrivatePages.js | 41 +++ client/src/containers/Authentication/Login.js | 6 +- .../src/containers/Authentication/Register.js | 284 ++++++++++++++++++ .../Authentication/Register/RegisterPage.js | 14 - .../Register/RegisterRightSection.js | 55 ---- .../Register/RegisterUserForm.js | 281 ----------------- .../Authentication/withAuthentication.js | 1 + .../withRegisterOrganizationActions.js | 12 - .../Organization/withOrganizationActions.js | 4 + .../Organization/withOrganizationByOrgId.js | 2 +- .../Organization/withOrganizationByTenId.js | 2 +- .../Setup/EnsureOrganizationIsNotReady.js | 25 ++ .../SetupLeftSection.js} | 11 +- .../SetupOrganizationForm.js} | 10 +- .../src/containers/Setup/SetupRightSection.js | 70 +++++ .../SetupSubscriptionForm.js} | 8 +- .../src/containers/Setup/WizardSetupPage.js | 19 ++ .../WizardSetupSteps.js} | 14 +- client/src/routes/authentication.js | 6 + .../organization/organization.actions.js | 28 -- .../organizations/organizations.actions.js | 40 ++- .../organizations/organizations.selectors.js | 14 +- .../src/style/pages/register-wizard-page.scss | 93 +++--- server/src/system/models/Tenant.js | 14 + .../system/repositories/TenantRepository.ts | 6 +- 29 files changed, 608 insertions(+), 513 deletions(-) create mode 100644 client/src/components/Dashboard/PrivatePages.js create mode 100644 client/src/containers/Authentication/Register.js delete mode 100644 client/src/containers/Authentication/Register/RegisterPage.js delete mode 100644 client/src/containers/Authentication/Register/RegisterRightSection.js delete mode 100644 client/src/containers/Authentication/Register/RegisterUserForm.js delete mode 100644 client/src/containers/Authentication/withRegisterOrganizationActions.js create mode 100644 client/src/containers/Setup/EnsureOrganizationIsNotReady.js rename client/src/containers/{Authentication/Register/RegisterLeftSection.js => Setup/SetupLeftSection.js} (92%) rename client/src/containers/{Authentication/Register/RegisterOrganizationForm.js => Setup/SetupOrganizationForm.js} (97%) create mode 100644 client/src/containers/Setup/SetupRightSection.js rename client/src/containers/{Authentication/Register/RegisterSubscriptionForm.js => Setup/SetupSubscriptionForm.js} (87%) create mode 100644 client/src/containers/Setup/WizardSetupPage.js rename client/src/containers/{Authentication/Register/RegisterWizardSteps.js => Setup/WizardSetupSteps.js} (69%) delete mode 100644 client/src/store/organization/organization.actions.js diff --git a/client/src/components/App.js b/client/src/components/App.js index aa7695535..bab88a549 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -7,9 +7,8 @@ import { ReactQueryDevtools } from 'react-query-devtools'; import PrivateRoute from 'components/PrivateRoute'; import Authentication from 'components/Authentication'; -import Dashboard from 'components/Dashboard/Dashboard'; +import DashboardPrivatePages from 'components/Dashboard/PrivatePages'; import GlobalErrors from 'containers/GlobalErrors/GlobalErrors'; -import RegisterWizardPage from 'containers/Authentication/Register/RegisterPage'; import messages from 'lang/en'; import 'style/App.scss'; @@ -32,12 +31,8 @@ function App({ locale }) { - - - - - + diff --git a/client/src/components/Dashboard/Dashboard.js b/client/src/components/Dashboard/Dashboard.js index 4b8dda00a..32d3ff487 100644 --- a/client/src/components/Dashboard/Dashboard.js +++ b/client/src/components/Dashboard/Dashboard.js @@ -14,7 +14,6 @@ import DashboardSplitPane from 'components/Dashboard/DashboardSplitePane'; import EnsureOrganizationIsReady from './EnsureOrganizationIsReady'; import withSettingsActions from 'containers/Settings/withSettingsActions'; -import withOrganizationsActions from 'containers/Organization/withOrganizationActions'; import { compose } from 'utils'; @@ -22,20 +21,14 @@ import { compose } from 'utils'; function Dashboard({ // #withSettings requestFetchOptions, - - // #withOrganizations - requestOrganizationsList, }) { - const fetchOrganizations = useQuery( - ['organizations'], - (key) => requestOrganizationsList(), - ); + // const fetchOptions = useQuery( + // ['options'], () => requestFetchOptions(), + // ); + return ( - + @@ -62,5 +55,4 @@ function Dashboard({ export default compose( withSettingsActions, - withOrganizationsActions, )(Dashboard); \ No newline at end of file diff --git a/client/src/components/Dashboard/DashboardLoadingIndicator.js b/client/src/components/Dashboard/DashboardLoadingIndicator.js index b07f92dd5..bdb9ac09a 100644 --- a/client/src/components/Dashboard/DashboardLoadingIndicator.js +++ b/client/src/components/Dashboard/DashboardLoadingIndicator.js @@ -2,12 +2,13 @@ import React from 'react'; import classNames from 'classnames'; import { Choose, Icon } from 'components'; -export default function Dashboard({ +export default function DashboardLoadingIndicator({ isLoading = false, + className, children, }) { return ( -
+
diff --git a/client/src/components/Dashboard/EnsureOrganizationIsReady.js b/client/src/components/Dashboard/EnsureOrganizationIsReady.js index a0b7e8929..a36d48896 100644 --- a/client/src/components/Dashboard/EnsureOrganizationIsReady.js +++ b/client/src/components/Dashboard/EnsureOrganizationIsReady.js @@ -1,16 +1,29 @@ import React from 'react'; +import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; +import { compose } from 'utils'; +import withAuthentication from 'containers/Authentication/withAuthentication'; +import withOrganizationByOrgId from 'containers/Organization/withOrganizationByOrgId'; -export default function EnsureOrganizationIsReady({ +function EnsureOrganizationIsReady({ + // #ownProps children, -}) { - const isOrganizationReady = false; + redirectTo = '/setup', - return (isOrganizationReady) ? children : ( + // #withOrganizationByOrgId + organization, +}) { + return (organization.is_ready) ? children : ( ); -} \ No newline at end of file +} + +export default compose( + withAuthentication(), + connect((state, props) => ({ + organizationId: props.currentOrganizationId, + })), + withOrganizationByOrgId(), +)(EnsureOrganizationIsReady); \ No newline at end of file diff --git a/client/src/components/Dashboard/PrivatePages.js b/client/src/components/Dashboard/PrivatePages.js new file mode 100644 index 000000000..54791a355 --- /dev/null +++ b/client/src/components/Dashboard/PrivatePages.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { Switch, Route } from 'react-router'; +import { useQuery } from 'react-query'; + +import Dashboard from 'components/Dashboard/Dashboard'; +import SetupWizardPage from 'containers/Setup/WizardSetupPage'; +import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator'; + +import withOrganizationActions from 'containers/Organization/withOrganizationActions'; + +import { compose } from 'utils'; + +/** + * Dashboard inner private pages. + */ +function DashboardPrivatePages({ + requestOrganizationsList, +}) { + const fetchOrganizations = useQuery( + ['organizations'], + () => requestOrganizationsList(), + ); + + return ( + + + + + + + + + + + + ); +} + +export default compose( + withOrganizationActions, +)(DashboardPrivatePages); \ No newline at end of file diff --git a/client/src/containers/Authentication/Login.js b/client/src/containers/Authentication/Login.js index fa73a5aab..6dabb1606 100644 --- a/client/src/containers/Authentication/Login.js +++ b/client/src/containers/Authentication/Login.js @@ -19,18 +19,15 @@ import Icon from 'components/Icon'; import { If } from 'components'; import withAuthenticationActions from './withAuthenticationActions'; -import withOrganizationsActions from 'containers/Organization/withOrganizationActions'; import { compose } from 'utils'; - const ERRORS_TYPES = { INVALID_DETAILS: 'INVALID_DETAILS', USER_INACTIVE: 'USER_INACTIVE', }; function Login({ requestLogin, - requestOrganizationsList, }) { const { formatMessage } = useIntl(); const history = useHistory(); @@ -105,7 +102,7 @@ function Login({

- +
@@ -170,5 +167,4 @@ function Login({ export default compose( withAuthenticationActions, - withOrganizationsActions, )(Login); \ No newline at end of file diff --git a/client/src/containers/Authentication/Register.js b/client/src/containers/Authentication/Register.js new file mode 100644 index 000000000..d88ec2a6e --- /dev/null +++ b/client/src/containers/Authentication/Register.js @@ -0,0 +1,284 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { Row, Col } from 'react-grid-system'; +import { Link, useHistory } from 'react-router-dom'; +import { + Button, + InputGroup, + Intent, + FormGroup, + Spinner, +} from '@blueprintjs/core'; +import { FormattedMessage as T, useIntl } from 'react-intl'; + +import AppToaster from 'components/AppToaster'; +import AuthInsider from 'containers/Authentication/AuthInsider'; + +import ErrorMessage from 'components/ErrorMessage'; +import Icon from 'components/Icon'; +import { If } from 'components'; +import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions'; + +import { compose } from 'utils'; + +function RegisterUserForm({ requestRegister, requestLogin }) { + const { formatMessage } = useIntl(); + const history = useHistory(); + const [shown, setShown] = useState(false); + const passwordRevealer = useCallback(() => { + setShown(!shown); + }, [shown]); + + const ValidationSchema = Yup.object().shape({ + first_name: Yup.string() + .required() + .label(formatMessage({ id: 'first_name_' })), + last_name: Yup.string() + .required() + .label(formatMessage({ id: 'last_name_' })), + email: Yup.string() + .email() + .required() + .label(formatMessage({ id: 'email' })), + phone_number: Yup.string() + .matches() + .required() + .label(formatMessage({ id: 'phone_number_' })), + password: Yup.string() + .min(4) + .required() + .label(formatMessage({ id: 'password' })), + }); + + const initialValues = useMemo( + () => ({ + first_name: '', + last_name: '', + email: '', + phone_number: '', + password: '', + }), + [], + ); + + const { + errors, + touched, + handleSubmit, + getFieldProps, + isSubmitting, + } = useFormik({ + enableReinitialize: true, + validationSchema: ValidationSchema, + initialValues: { + ...initialValues, + country: 'libya', + }, + onSubmit: (values, { setSubmitting, setErrors }) => { + requestRegister(values) + .then((response) => { + requestLogin({ + crediential: values.email, + password: values.password, + }) + .then(() => { + history.push('/register/subscription'); + setSubmitting(false); + }) + .catch((errors) => { + AppToaster.show({ + message: formatMessage({ id: 'something_wentwrong' }), + intent: Intent.SUCCESS, + }); + }); + }) + .catch((errors) => { + if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) { + setErrors({ + phone_number: formatMessage({ + id: 'the_phone_number_already_used_in_another_account', + }), + }); + } + if (errors.some((e) => e.type === 'EMAIL_EXISTS')) { + setErrors({ + email: formatMessage({ + id: 'the_email_already_used_in_another_account', + }), + }); + } + setSubmitting(false); + }); + }, + }); + + const passwordRevealerTmp = useMemo( + () => ( + passwordRevealer()}> + + <> + {' '} + + + + + + + <> + {' '} + + + + + + + ), + [shown, passwordRevealer], + ); + + return ( + +
+
+

+ +

+ + + {' '} + + +
+ + + + + + } + intent={ + errors.first_name && touched.first_name && Intent.DANGER + } + helperText={ + + } + className={'form-group--first-name'} + > + + + + + + } + intent={errors.last_name && touched.last_name && Intent.DANGER} + helperText={ + + } + className={'form-group--last-name'} + > + + + + + + } + intent={ + errors.phone_number && touched.phone_number && Intent.DANGER + } + helperText={ + + } + className={'form-group--phone-number'} + > + + + + } + intent={errors.email && touched.email && Intent.DANGER} + helperText={ + + } + className={'form-group--email'} + > + + + + } + labelInfo={passwordRevealerTmp} + intent={errors.password && touched.password && Intent.DANGER} + helperText={ + + } + className={'form-group--password has-password-revealer'} + > + + + +
+

+
+ + + {' '} + + + {' '} + + +

+
+ +
+ +
+ + + +
+ +
+
+
+
+ ); +} + +export default compose( + withAuthenticationActions, +)(RegisterUserForm); diff --git a/client/src/containers/Authentication/Register/RegisterPage.js b/client/src/containers/Authentication/Register/RegisterPage.js deleted file mode 100644 index f8a3f350c..000000000 --- a/client/src/containers/Authentication/Register/RegisterPage.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import RegisterRightSection from './RegisterRightSection'; -import RegisterLeftSection from './RegisterLeftSection'; - -function RegisterWizardPage() { - return ( -
- - -
- ); -} - -export default RegisterWizardPage; diff --git a/client/src/containers/Authentication/Register/RegisterRightSection.js b/client/src/containers/Authentication/Register/RegisterRightSection.js deleted file mode 100644 index ac628ac0b..000000000 --- a/client/src/containers/Authentication/Register/RegisterRightSection.js +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import { TransitionGroup, CSSTransition } from 'react-transition-group'; -import { Wizard, Steps, Step } from 'react-albus'; -import { useHistory } from "react-router-dom"; -import RegisterWizardSteps from './RegisterWizardSteps'; -import PrivateRoute from 'components/PrivateRoute'; - -import RegisterUserForm from 'containers/Authentication/Register/RegisterUserForm'; -import RegisterSubscriptionForm from 'containers/Authentication/Register/RegisterSubscriptionForm'; -import RegisterOrganizationForm from 'containers/Authentication/Register/RegisterOrganizationForm'; - -export default function RegisterRightSection () { - const history = useHistory(); - - return ( -
- ( -
- - - - -
- - - - - - - - - - - - - - -

Ice King

-
-
-
-
-
-
- )} /> -
- ) -} \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterUserForm.js b/client/src/containers/Authentication/Register/RegisterUserForm.js deleted file mode 100644 index 7dd592543..000000000 --- a/client/src/containers/Authentication/Register/RegisterUserForm.js +++ /dev/null @@ -1,281 +0,0 @@ -import React, { useMemo, useState, useCallback } from 'react'; -import * as Yup from 'yup'; -import { useFormik } from 'formik'; -import { Row, Col } from 'react-grid-system'; -import { Link, useHistory } from 'react-router-dom'; -import { - Button, - InputGroup, - Intent, - FormGroup, - Spinner, -} from '@blueprintjs/core'; -import { FormattedMessage as T, useIntl } from 'react-intl'; - -import AppToaster from 'components/AppToaster'; - -import ErrorMessage from 'components/ErrorMessage'; -import Icon from 'components/Icon'; -import { If } from 'components'; -import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions'; - -import { compose } from 'utils'; - -function RegisterUserForm({ requestRegister, requestLogin }) { - const { formatMessage } = useIntl(); - const history = useHistory(); - const [shown, setShown] = useState(false); - const passwordRevealer = useCallback(() => { - setShown(!shown); - }, [shown]); - - const ValidationSchema = Yup.object().shape({ - first_name: Yup.string() - .required() - .label(formatMessage({ id: 'first_name_' })), - last_name: Yup.string() - .required() - .label(formatMessage({ id: 'last_name_' })), - email: Yup.string() - .email() - .required() - .label(formatMessage({ id: 'email' })), - phone_number: Yup.string() - .matches() - .required() - .label(formatMessage({ id: 'phone_number_' })), - password: Yup.string() - .min(4) - .required() - .label(formatMessage({ id: 'password' })), - }); - - const initialValues = useMemo( - () => ({ - first_name: '', - last_name: '', - email: '', - phone_number: '', - password: '', - }), - [], - ); - - const { - errors, - touched, - handleSubmit, - getFieldProps, - isSubmitting, - } = useFormik({ - enableReinitialize: true, - validationSchema: ValidationSchema, - initialValues: { - ...initialValues, - country: 'libya', - }, - onSubmit: (values, { setSubmitting, setErrors }) => { - requestRegister(values) - .then((response) => { - requestLogin({ - crediential: values.email, - password: values.password, - }) - .then(() => { - history.push('/register/subscription'); - setSubmitting(false); - }) - .catch((errors) => { - AppToaster.show({ - message: formatMessage({ - id: 'something_wentwrong', - }), - intent: Intent.SUCCESS, - }); - }); - }) - .catch((errors) => { - if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) { - setErrors({ - phone_number: formatMessage({ - id: 'the_phone_number_already_used_in_another_account', - }), - }); - } - if (errors.some((e) => e.type === 'EMAIL_EXISTS')) { - setErrors({ - email: formatMessage({ - id: 'the_email_already_used_in_another_account', - }), - }); - } - setSubmitting(false); - }); - }, - }); - - const passwordRevealerTmp = useMemo( - () => ( - passwordRevealer()}> - - <> - {' '} - - - - - - - <> - {' '} - - - - - - - ), - [shown, passwordRevealer], - ); - - return ( -
-
-

- -

- - - {' '} - - -
- -
- - - - } - intent={ - errors.first_name && touched.first_name && Intent.DANGER - } - helperText={ - - } - className={'form-group--first-name'} - > - - - - - - } - intent={errors.last_name && touched.last_name && Intent.DANGER} - helperText={ - - } - className={'form-group--last-name'} - > - - - - - - } - intent={ - errors.phone_number && touched.phone_number && Intent.DANGER - } - helperText={ - - } - className={'form-group--phone-number'} - > - - - - } - intent={errors.email && touched.email && Intent.DANGER} - helperText={ - - } - className={'form-group--email'} - > - - - - } - labelInfo={passwordRevealerTmp} - intent={errors.password && touched.password && Intent.DANGER} - helperText={ - - } - className={'form-group--password has-password-revealer'} - > - - - -
-

-
- - - {' '} - - - {' '} - - -

-
- -
- -
-
- - -
- -
-
-
- ); -} - -export default compose(withAuthenticationActions)(RegisterUserForm); diff --git a/client/src/containers/Authentication/withAuthentication.js b/client/src/containers/Authentication/withAuthentication.js index 9ad30b97a..34a678227 100644 --- a/client/src/containers/Authentication/withAuthentication.js +++ b/client/src/containers/Authentication/withAuthentication.js @@ -6,6 +6,7 @@ export default (mapState) => { const mapped = { isAuthorized: isAuthenticated(state), user: state.authentication.user, + currentOrganizationId: state.authentication?.tenant?.organization_id, }; return mapState ? mapState(mapped, state, props) : mapped; }; diff --git a/client/src/containers/Authentication/withRegisterOrganizationActions.js b/client/src/containers/Authentication/withRegisterOrganizationActions.js deleted file mode 100644 index 35537fa47..000000000 --- a/client/src/containers/Authentication/withRegisterOrganizationActions.js +++ /dev/null @@ -1,12 +0,0 @@ -import { - buildTenant, - seedTenant, -} from 'store/organization/organization.actions'; -import { connect } from 'react-redux'; - -const mapDispatchToProps = (dispatch) => ({ - requestBuildTenant: (id, token) => dispatch(buildTenant({ id, token })), - requestSeedTenant: (id, token) => dispatch(seedTenant({ id, token })), -}); - -export default connect(null, mapDispatchToProps); diff --git a/client/src/containers/Organization/withOrganizationActions.js b/client/src/containers/Organization/withOrganizationActions.js index 2a23bb6bb..d2eeea2a6 100644 --- a/client/src/containers/Organization/withOrganizationActions.js +++ b/client/src/containers/Organization/withOrganizationActions.js @@ -1,10 +1,14 @@ import { connect } from 'react-redux'; import { fetchOrganizations, + buildTenant, + seedTenant, } from 'store/organizations/organizations.actions'; export const mapDispatchToProps = (dispatch) => ({ requestOrganizationsList: () => dispatch(fetchOrganizations()), + requestBuildTenant: () => dispatch(buildTenant()), + requestSeedTenant: () => dispatch(seedTenant()), }); export default connect(null, mapDispatchToProps); \ No newline at end of file diff --git a/client/src/containers/Organization/withOrganizationByOrgId.js b/client/src/containers/Organization/withOrganizationByOrgId.js index b07c89167..9d94d97f0 100644 --- a/client/src/containers/Organization/withOrganizationByOrgId.js +++ b/client/src/containers/Organization/withOrganizationByOrgId.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { getOrganizationByOrgIdFactory, -} from 'store/organizations/organizations.selector'; +} from 'store/organizations/organizations.selectors'; export default (mapState) => { const getOrganizationByOrgId = getOrganizationByOrgIdFactory(); diff --git a/client/src/containers/Organization/withOrganizationByTenId.js b/client/src/containers/Organization/withOrganizationByTenId.js index 998308914..a92fe1a8b 100644 --- a/client/src/containers/Organization/withOrganizationByTenId.js +++ b/client/src/containers/Organization/withOrganizationByTenId.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { getOrganizationByTenantIdFactory, -} from 'store/organizations/organizations.selector'; +} from 'store/organizations/organizations.selectors'; export default (mapState) => { const getOrgByTenId = getOrganizationByTenantIdFactory(); diff --git a/client/src/containers/Setup/EnsureOrganizationIsNotReady.js b/client/src/containers/Setup/EnsureOrganizationIsNotReady.js new file mode 100644 index 000000000..a31425b9b --- /dev/null +++ b/client/src/containers/Setup/EnsureOrganizationIsNotReady.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { Redirect } from 'react-router-dom'; +import { compose } from 'utils'; +import withAuthentication from 'containers/Authentication/withAuthentication'; +import withOrganizationByOrgId from 'containers/Organization/withOrganizationByOrgId'; + +function EnsureOrganizationIsNotReady({ + children, + + // #withOrganizationByOrgId + organization, +}) { + return (organization.is_ready) ? ( + + ) : children; +} + +export default compose( + withAuthentication(), + connect((state, props) => ({ + organizationId: props.currentOrganizationId, + })), + withOrganizationByOrgId(), +)(EnsureOrganizationIsNotReady); \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterLeftSection.js b/client/src/containers/Setup/SetupLeftSection.js similarity index 92% rename from client/src/containers/Authentication/Register/RegisterLeftSection.js rename to client/src/containers/Setup/SetupLeftSection.js index aeb8ad0a0..5dc327998 100644 --- a/client/src/containers/Authentication/Register/RegisterLeftSection.js +++ b/client/src/containers/Setup/SetupLeftSection.js @@ -7,8 +7,10 @@ import withAuthenticationActions from 'containers/Authentication/withAuthenticat import { compose } from 'utils'; - -function RegisterLeftSection({ +/** + * Wizard setup left section. + */ +function SetupLeftSection({ requestLogout, isAuthorized }) { @@ -19,7 +21,7 @@ function RegisterLeftSection({ }, [requestLogout]); return ( -
+

-
@@ -71,4 +72,4 @@ function RegisterLeftSection({ export default compose( withAuthentication(({ isAuthorized }) => ({ isAuthorized })), withAuthenticationActions, -)(RegisterLeftSection); +)(SetupLeftSection); diff --git a/client/src/containers/Authentication/Register/RegisterOrganizationForm.js b/client/src/containers/Setup/SetupOrganizationForm.js similarity index 97% rename from client/src/containers/Authentication/Register/RegisterOrganizationForm.js rename to client/src/containers/Setup/SetupOrganizationForm.js index 3f155608e..29c264bd0 100644 --- a/client/src/containers/Authentication/Register/RegisterOrganizationForm.js +++ b/client/src/containers/Setup/SetupOrganizationForm.js @@ -20,11 +20,13 @@ import { momentFormatter, tansformDateValue } from 'utils'; import AppToaster from 'components/AppToaster'; import { ListSelect, ErrorMessage, FieldRequiredHint } from 'components'; import { useHistory } from 'react-router-dom'; + import withSettingsActions from 'containers/Settings/withSettingsActions'; -import withRegisterOrganizationActions from 'containers/Authentication/withRegisterOrganizationActions'; +import withOrganizationActions from 'containers/Organization/withOrganizationActions'; + import { compose, optionsMapToArray } from 'utils'; -function RegisterOrganizationForm({ requestSubmitOptions, requestSeedTenant }) { +function SetupOrganizationForm({ requestSubmitOptions, requestSeedTenant }) { const { formatMessage } = useIntl(); const [selected, setSelected] = useState(); const history = useHistory(); @@ -414,5 +416,5 @@ function RegisterOrganizationForm({ requestSubmitOptions, requestSeedTenant }) { export default compose( withSettingsActions, - withRegisterOrganizationActions, -)(RegisterOrganizationForm); + withOrganizationActions, +)(SetupOrganizationForm); diff --git a/client/src/containers/Setup/SetupRightSection.js b/client/src/containers/Setup/SetupRightSection.js new file mode 100644 index 000000000..d00d38ccf --- /dev/null +++ b/client/src/containers/Setup/SetupRightSection.js @@ -0,0 +1,70 @@ +import React, { useCallback } from 'react'; +import { TransitionGroup, CSSTransition } from 'react-transition-group'; +import { Wizard, Steps, Step } from 'react-albus'; +import { useHistory } from "react-router-dom"; +import WizardSetupSteps from './WizardSetupSteps'; + +import SetupSubscriptionForm from './SetupSubscriptionForm'; +import SetupOrganizationForm from './SetupOrganizationForm'; + +import withAuthentication from 'containers/Authentication/withAuthentication'; + +import { compose } from 'utils'; + +/** + * Wizard setup right section. + */ +function SetupRightSection ({ + isTenantHasSubscriptions: hasSubscriptions = false, +}) { + const history = useHistory(); + const handleSkip = useCallback(({ step, push }) => { + const scenarios = [ + { condition: hasSubscriptions, redirectTo: 'organization' }, + { condition: !hasSubscriptions, redirectTo: 'subscription' }, + ]; + const scenario = scenarios.find((scenario) => scenario.condition); + + if (scenario) { + push(scenario.redirectTo); + } + }, [hasSubscriptions]); + + return ( +
+ ( +
+ + + + +
+ + + + + + + + + + +

Ice King

+
+
+
+
+
+
+ )} /> +
+ ) +} + +export default compose( + withAuthentication(({ isAuthorized }) => ({ isAuthorized })), +)(SetupRightSection); \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterSubscriptionForm.js b/client/src/containers/Setup/SetupSubscriptionForm.js similarity index 87% rename from client/src/containers/Authentication/Register/RegisterSubscriptionForm.js rename to client/src/containers/Setup/SetupSubscriptionForm.js index 6274ac3aa..17fab2a76 100644 --- a/client/src/containers/Authentication/Register/RegisterSubscriptionForm.js +++ b/client/src/containers/Setup/SetupSubscriptionForm.js @@ -1,11 +1,13 @@ import React, { useMemo } from 'react'; import * as Yup from 'yup'; import { useFormik } from 'formik'; -import { FormattedMessage as T, useIntl } from 'react-intl'; +import { FormattedMessage as T } from 'react-intl'; import { Button, Intent } from '@blueprintjs/core'; import BillingTab from 'containers/Subscriptions/BillingTab'; -function RegisterSubscriptionForm({}) { +export default function SetupSubscriptionForm({ + +}) { const ValidationSchema = Yup.object().shape({}); const initialValues = useMemo(() => ({}), []); @@ -36,5 +38,3 @@ function RegisterSubscriptionForm({}) {
); } - -export default RegisterSubscriptionForm; diff --git a/client/src/containers/Setup/WizardSetupPage.js b/client/src/containers/Setup/WizardSetupPage.js new file mode 100644 index 000000000..dcd4debb8 --- /dev/null +++ b/client/src/containers/Setup/WizardSetupPage.js @@ -0,0 +1,19 @@ +import React from 'react'; + +import EnsureOrganizationIsNotReady from './EnsureOrganizationIsNotReady'; +import SetupRightSection from './SetupRightSection'; +import SetupLeftSection from './SetupLeftSection'; + + +export default function WizardSetupPage({ + organizationId, +}) { + return ( + +
+ + +
+
+ ); +}; \ No newline at end of file diff --git a/client/src/containers/Authentication/Register/RegisterWizardSteps.js b/client/src/containers/Setup/WizardSetupSteps.js similarity index 69% rename from client/src/containers/Authentication/Register/RegisterWizardSteps.js rename to client/src/containers/Setup/WizardSetupSteps.js index b21a58a89..aaa39d6b7 100644 --- a/client/src/containers/Authentication/Register/RegisterWizardSteps.js +++ b/client/src/containers/Setup/WizardSetupSteps.js @@ -3,7 +3,7 @@ import classNames from 'classnames'; import { FormattedMessage as T } from 'react-intl'; import { registerWizardSteps } from 'common/registerWizard' -function RegisterWizardStep({ +function WizardSetupStep({ label, isActive = false }) { @@ -14,15 +14,15 @@ function RegisterWizardStep({ ); } -function RegisterWizardSteps({ +function WizardSetupSteps({ currentStep = 1, }) { return ( -
-
-