diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.css b/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.css
index 8d7f0a7c83..11bc42277c 100644
--- a/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.css
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.css
@@ -14,13 +14,18 @@
align-items: center;
margin-bottom: 10px;
padding-bottom: 10px;
- border-bottom: 1px solid var(--temp-grey-blue-6);
+ border-bottom: 1px solid color-mod(var(--temp-grey-blue-7) alpha(15%));
}
.statusWrapper {
display: flex;
justify-content: space-between;
align-items: center;
+ margin-bottom: 12px;
+}
+
+.statusWrapper > span {
+ padding: 1px 10px 2px;
}
.line {
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.tsx b/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.tsx
index 87342ac2cc..d6e07b370e 100644
--- a/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.tsx
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/LinkedMotions/LinkedMotions.tsx
@@ -1,18 +1,27 @@
import React, { useMemo } from 'react';
-import { defineMessages, FormattedMessage } from 'react-intl';
+import {
+ defineMessages,
+ FormattedMessage,
+ MessageDescriptor,
+} from 'react-intl';
import Link from '~core/Link';
import Tag from '~core/Tag';
-import { MotionStatus } from '../constants';
+import { Motion, MotionStatus, MotionType } from '../constants';
import styles from './LinkedMotions.css';
+import { LANDING_PAGE_ROUTE } from '~routes/routeConstants';
const MSG = defineMessages({
- linkedMotions: {
+ linkedMotionExpenditure: {
id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.linkedMotions',
defaultMessage: 'Linked motions',
},
+ linkedMotionIncorporation: {
+ id: 'dashboard.Incorporation.LinkedMotions.linkedMotion',
+ defaultMessage: 'Relates to motion',
+ },
foundExp: {
id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.foundExp',
defaultMessage: '{motion} Exp - {id}',
@@ -29,30 +38,94 @@ const MSG = defineMessages({
id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.motion',
defaultMessage: 'Motion',
},
+ payment: {
+ id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.motion',
+ defaultMessage: 'Pay Incorporation Fee',
+ },
+ edit: {
+ id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.edit',
+ defaultMessage: 'Edit Incorporation',
+ },
+ cancel: {
+ id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.cancel',
+ defaultMessage: 'Cancel Incorporation',
+ },
+ motionText: {
+ id: 'dashboard.ExpenditurePage.Stages.LinkedMotions.motionText',
+ defaultMessage: '{text} {count}',
+ },
});
const displayName = 'dashboard.ExpenditurePage.Stages.LinkedMotions';
+export enum ViewFor {
+ INCORPORATION = 'incorporation',
+ EXPENDITURE = 'expenditure',
+}
+
+type MotionSettins = {
+ text?: MessageDescriptor;
+ tagText?: MessageDescriptor;
+ tagColor?: string;
+};
+
interface Props {
- status: MotionStatus;
- motionLink?: string;
- motion: string;
- id: string;
+ motion: Motion | Motion[];
+ viewFor?: ViewFor;
}
-const LinkedMotions = ({ status, motionLink, motion, id }: Props) => {
- const tagOptions = useMemo(() => {
- switch (status) {
- case MotionStatus.Pending:
- return { text: MSG.motion, color: undefined };
- case MotionStatus.Passed:
- return { text: MSG.passed, color: styles.passedColor };
- case MotionStatus.Failed:
- return { text: MSG.failed, color: styles.failedColor };
- default:
- return { text: '', color: undefined };
- }
- }, [status]);
+const LinkedMotions = ({ motion, viewFor = ViewFor.EXPENDITURE }: Props) => {
+ const motionSettings = useMemo(
+ () => (motionItem: Motion) => {
+ const settings: MotionSettins = {
+ text: undefined,
+ tagText: undefined,
+ tagColor: undefined,
+ };
+
+ switch (motionItem.type) {
+ case MotionType.Payment:
+ settings.text = MSG.payment;
+ break;
+ case MotionType.Edit:
+ settings.text = MSG.edit;
+ break;
+ case MotionType.Cancel:
+ settings.text = MSG.cancel;
+ break;
+ default:
+ break;
+ }
+
+ switch (motionItem.status) {
+ case MotionStatus.Pending:
+ settings.tagText = MSG.motion;
+ settings.tagColor = undefined;
+ break;
+ case MotionStatus.Passed:
+ settings.tagText = MSG.passed;
+ settings.tagColor = styles.passedColor;
+ break;
+ case MotionStatus.Failed:
+ settings.tagText = MSG.failed;
+ settings.tagColor = styles.failedColor;
+ break;
+ default:
+ break;
+ }
+
+ return settings;
+ },
+ [],
+ );
+
+ // mocks, needed to display correct UI, must be adjusted to display data from the backend
+ const multiplePayments =
+ Array.isArray(motion) &&
+ motion.filter((motionItem) => motionItem.type === MotionType.Payment)
+ ?.length > 1;
+ let paymentCount = 0;
+ const motionsArr = Array.isArray(motion) ? motion : [motion];
return (
@@ -61,27 +134,52 @@ const LinkedMotions = ({ status, motionLink, motion, id }: Props) => {
-
+
-
- {motionLink ? (
-
-
-
- ) : (
-
- )}
-
-
+ {/* The link is hardcoded. Link should redirect to the motion page */}
+ {motionsArr.map((motionItem) => {
+ const motionData = motionSettings(motionItem);
+ if (motionItem.type === MotionType.Payment) paymentCount += 1;
+
+ return (
+
+
+ {motionData.text && (
+ ,
+ count: (
+
+ {multiplePayments &&
+ paymentCount > 1 &&
+ `#${paymentCount}`}
+
+ ),
+ }}
+ />
+ )}
+
+
+
+ );
+ })}
);
};
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/LockedStages.tsx b/src/modules/dashboard/components/ExpenditurePage/Stages/LockedStages.tsx
index c15191c380..7a1cf072cf 100644
--- a/src/modules/dashboard/components/ExpenditurePage/Stages/LockedStages.tsx
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/LockedStages.tsx
@@ -1,6 +1,7 @@
import React, { useCallback, useMemo } from 'react';
import { defineMessages, MessageDescriptor, useIntl } from 'react-intl';
+import { isEmpty } from 'lodash';
import Tag from '~core/Tag';
import { Colony } from '~data/index';
import {
@@ -8,12 +9,12 @@ import {
StageObject,
ValuesType,
} from '~pages/ExpenditurePage/types';
-import { LANDING_PAGE_ROUTE } from '~routes/routeConstants';
import { Motion, MotionStatus, MotionType, Status } from './constants';
import LinkedMotions from './LinkedMotions';
-import Stages from './Stages';
+import Stages, { Appearance } from './Stages';
import StreamingStagesLocked from './StreamingStages/StreamingStagesLocked';
+import { ViewFor } from './LinkedMotions/LinkedMotions';
import styles from './Stages.css';
const MSG = defineMessages({
@@ -33,12 +34,14 @@ interface Props {
stages: StageObject[];
setActiveStageId?: React.Dispatch>;
activeStageId?: string;
- motion?: Motion;
+ motion?: Motion[] | Motion;
status?: Status;
handleCancel?: () => void;
colony: Colony;
expenditureType?: ExpenditureTypes;
formValues?: ValuesType;
+ appearance?: Appearance;
+ viewFor?: ViewFor;
}
const LockedStages = ({
@@ -51,6 +54,7 @@ const LockedStages = ({
formValues,
colony,
expenditureType,
+ viewFor,
}: Props) => {
const activeStage = stages.find((stage) => stage.id === activeStageId);
const { formatMessage } = useIntl();
@@ -77,17 +81,29 @@ const LockedStages = ({
[formatMessage],
);
+ // searching for motion in pending state is a mock. Will be replaced with call to backend
+ const pendingMotion = useMemo(
+ () =>
+ Array.isArray(motion)
+ ? motion?.find(
+ (motionItem) => motionItem.status === MotionStatus.Pending,
+ )
+ : motion?.status === MotionStatus.Pending,
+ [motion],
+ );
+
return (
- {motion?.status === MotionStatus.Pending && !isStreamingPaymentType && (
+ {pendingMotion && !isStreamingPaymentType && (
- {motion.type === MotionType.Edit &&
- motion.status === MotionStatus.Pending
+ {typeof pendingMotion === 'object' &&
+ 'type' in pendingMotion &&
+ pendingMotion.type === MotionType.Edit
? formatMessage(MSG.activeMotion)
: formatMessage(MSG.motion, {
action: formattedLabel(activeStage?.buttonText),
@@ -97,7 +113,7 @@ const LockedStages = ({
{isStreamingPaymentType ? (
)}
- {motion && (
-
+ {motion && !isEmpty(motion) && (
+
)}
);
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/Stages.tsx b/src/modules/dashboard/components/ExpenditurePage/Stages/Stages.tsx
index 12b62c87f5..b7127dfdfb 100644
--- a/src/modules/dashboard/components/ExpenditurePage/Stages/Stages.tsx
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/Stages.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
defineMessages,
FormattedMessage,
@@ -19,10 +19,12 @@ import { Colony } from '~data/index';
import { Stages as StagesEnum } from '~pages/IncorporationPage/constants';
import StageItem from './StageItem';
-import { Motion, MotionStatus, Stage, Status } from './constants';
+import { Motion, MotionStatus, MotionType, Stage, Status } from './constants';
import Label from './StageItem/Label';
import StagesButton from './StagesButton';
+import StagesButtonIncorporation from './StagesButtonIncorporation';
import { ClaimFundsOther, ClaimFundsRecipients } from './ClaimFunds';
+import { ViewFor } from './LinkedMotions/LinkedMotions';
import styles from './Stages.css';
const MSG = defineMessages({
@@ -90,7 +92,7 @@ export interface Props {
handleSaveDraft?: () => void;
handleButtonClick: () => void;
status?: Status;
- motion?: Motion;
+ motion?: Motion | Motion[];
handleCancel?: () => void;
colony: Colony;
buttonDisabled?: boolean;
@@ -133,14 +135,37 @@ const Stages = ({
const activeStage = stages.find((stage) => stage.id === activeStageId);
const isLogedIn = true;
- const isCancelled =
- status === Status.Cancelled || status === Status.ForceCancelled;
-
const claimFundsVisible =
isLogedIn &&
activeStageId === Stage.Released &&
status !== Status.Cancelled;
+ const isCancelled = useMemo(
+ () =>
+ Array.isArray(motion)
+ ? motion?.find(
+ (motionItem) =>
+ motionItem.type === MotionType.Cancel &&
+ motionItem.status === MotionStatus.Passed,
+ )
+ : (motion?.type === MotionType.Cancel &&
+ motion?.status === MotionStatus.Passed) ||
+ status === Status.Cancelled ||
+ status === Status.ForceCancelled,
+ [motion, status],
+ );
+
+ // searching for motion in pending state is a mock. Will be replaced with call to backend
+ const pendingMotion = useMemo(
+ () =>
+ Array.isArray(motion)
+ ? motion?.find(
+ (motionItem) => motionItem.status === MotionStatus.Pending,
+ )
+ : motion?.status === MotionStatus.Pending,
+ [motion],
+ );
+
return (
{claimFundsVisible && formValues && (
@@ -151,7 +176,7 @@ const Stages = ({
colony={colony}
buttonAction={activeStage?.buttonAction}
buttonText={activeStage?.buttonText}
- isDisabled={motion?.status === MotionStatus.Pending}
+ isDisabled={!!pendingMotion}
/>
) : (
)}
>
)}
@@ -296,17 +321,13 @@ const Stages = ({
activeStageId !== Stage.Claimed && (
- {motion?.status === MotionStatus.Pending ? (
+ {pendingMotion ? (
)}
-
+ {viewFor === ViewFor.EXPENDITURE ? (
+
+ ) : (
+
+ )}
>
)}
@@ -367,7 +396,7 @@ const Stages = ({
description={description}
isLast={stages.length === index + 1}
isActive={activeStage ? index <= activeIndex : false}
- isCancelled={isCancelled && status === Status.ForceCancelled}
+ isCancelled={!!isCancelled}
labelComponent={
status === Status.ForceEdited &&
index === activeIndex &&
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css
new file mode 100644
index 0000000000..0ebc71b8ed
--- /dev/null
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css
@@ -0,0 +1,6 @@
+.button {
+ composes: themePrimary main from '~core/Button/Button.css';
+ padding: 0;
+ height: 29px;
+ width: 100px;
+}
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css.d.ts b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css.d.ts
new file mode 100644
index 0000000000..347105caea
--- /dev/null
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.css.d.ts
@@ -0,0 +1 @@
+export const button: string;
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.tsx b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.tsx
new file mode 100644
index 0000000000..2b34da7c8d
--- /dev/null
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/StagesButtonIncorporation.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { useIntl } from 'react-intl';
+
+import Button from '~core/Button';
+import { Stages } from '~pages/IncorporationPage/constants';
+
+import styles from './StagesButtonIncorporation.css';
+import { StageType } from '../Stages';
+
+const displayName = 'dashboard.Incorporation.Stages.StagesButtonIncorporation';
+
+interface Props {
+ activeStage?: StageType;
+ buttonDisabled?: boolean;
+ buttonAction?: VoidFunction;
+}
+
+const StagesButton = ({ activeStage, buttonDisabled, buttonAction }: Props) => {
+ const { formatMessage } = useIntl();
+ const buttonText =
+ typeof activeStage?.buttonText === 'string'
+ ? activeStage.buttonText
+ : activeStage?.buttonText && formatMessage(activeStage.buttonText);
+
+ if (!activeStage) return null;
+
+ if (
+ activeStage.id === Stages.Processing ||
+ activeStage.id === Stages.Complete
+ ) {
+ return null;
+ }
+
+ return (
+
+ {buttonText}
+
+ );
+};
+
+StagesButton.displayName = displayName;
+
+export default StagesButton;
diff --git a/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/index.ts b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/index.ts
new file mode 100644
index 0000000000..3e7a39e768
--- /dev/null
+++ b/src/modules/dashboard/components/ExpenditurePage/Stages/StagesButtonIncorporation/index.ts
@@ -0,0 +1 @@
+export { default } from './StagesButtonIncorporation';
diff --git a/src/modules/dashboard/components/Incorporation/IncorporationForm/Protectors/Protectors.tsx b/src/modules/dashboard/components/Incorporation/IncorporationForm/Protectors/Protectors.tsx
index 2f5e47a80b..4fa7617d57 100644
--- a/src/modules/dashboard/components/Incorporation/IncorporationForm/Protectors/Protectors.tsx
+++ b/src/modules/dashboard/components/Incorporation/IncorporationForm/Protectors/Protectors.tsx
@@ -17,6 +17,7 @@ import Button from '~core/Button';
import Link from '~core/Link';
import SingleUserPicker from '../SingleUserPicker';
+import { SignOption } from '../constants';
import styles from './Protectors.css';
diff --git a/src/modules/dashboard/components/Incorporation/Stages/FormStages.tsx b/src/modules/dashboard/components/Incorporation/Stages/FormStages.tsx
deleted file mode 100644
index 606ef0e586..0000000000
--- a/src/modules/dashboard/components/Incorporation/Stages/FormStages.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { useFormikContext, setNestedObjectValues, FormikTouched } from 'formik';
-import React, { useCallback, useMemo } from 'react';
-import { defineMessages, FormattedMessage } from 'react-intl';
-
-import { flattenObject } from '~dashboard/ExpenditurePage/Stages/utils';
-import { FIX_TRIGGER_EVENT_NAME } from '~pages/ExpenditurePage/constants';
-import { StageObject, ValuesType } from '~pages/IncorporationPage/types';
-import { Stages as StagesEnum } from '~pages/IncorporationPage/constants';
-
-import Stages from './Stages';
-import styles from './Stages.css';
-
-const displayName = 'dashboard.Incorporation.Stages.FormStages';
-
-const MSG = defineMessages({
- singleErrorMessage: {
- id: 'dashboard.Incorporation.Stages.FormStages.singleErrorMessage',
- defaultMessage: '{number} required field has an error.',
- },
- mulitpleErrorMessage: {
- id: 'dashboard.Incorporation.Stages.FormStages.mulitpleErrorMessage',
- defaultMessage: '{number} required fields have an error.',
- },
- errorMessageAction: {
- id: 'dashboard.Incorporation.Stages.FormStages.errorMessageAction',
- defaultMessage: 'Fix it!',
- },
-});
-
-export interface Props {
- stages: StageObject[];
- activeStageId: StagesEnum;
-}
-
-const FormStages = ({ stages, activeStageId }: Props) => {
- const { values, handleSubmit, validateForm, setTouched, errors: formikErr } =
- useFormikContext
() || {};
-
- const formikErrors = useMemo(() => {
- const errorsFlat = flattenObject(formikErr);
- return Object.keys(errorsFlat);
- }, [formikErr]);
-
- const handleSaveDraft = useCallback(async () => {
- const errors = await validateForm(values);
- const errorsLength = Object.keys(errors)?.length;
- setTouched(setNestedObjectValues>(errors, true));
-
- return !errorsLength && handleSubmit(values as any);
- }, [handleSubmit, setTouched, validateForm, values]);
-
- const handleFixButtonClick = useCallback(() => {
- setTouched(
- setNestedObjectValues>(formikErr, true),
- );
- if (!formikErrors.length) return;
-
- const firstError = document.getElementsByName(formikErrors[0])?.[0];
-
- if (['textarea', 'input'].includes(firstError?.tagName.toLowerCase())) {
- (firstError as HTMLElement).focus();
- } else {
- const customEvent = new CustomEvent(FIX_TRIGGER_EVENT_NAME, {
- detail: {
- name: formikErrors[0],
- },
- });
-
- window.dispatchEvent(customEvent);
- }
- }, [setTouched, formikErr, formikErrors]);
-
- return (
-
- {!!formikErrors.length && (
-
-
- 1
- ? { ...MSG.mulitpleErrorMessage }
- : { ...MSG.singleErrorMessage })}
- values={{ number: formikErrors.length }}
- />
-
-
-
-
-
- )}
-
-
-
- );
-};
-
-FormStages.displayName = displayName;
-
-export default FormStages;
diff --git a/src/modules/pages/components/IncorporationPage/IncorporationPage.tsx b/src/modules/pages/components/IncorporationPage/IncorporationPage.tsx
index 632fc0a7b3..3bd76343fb 100644
--- a/src/modules/pages/components/IncorporationPage/IncorporationPage.tsx
+++ b/src/modules/pages/components/IncorporationPage/IncorporationPage.tsx
@@ -7,18 +7,18 @@ import { useColonyFromNameQuery } from '~data/generated';
import { getMainClasses } from '~utils/css';
import { SpinnerLoader } from '~core/Preloaders';
import IncorporationForm from '~dashboard/Incorporation/IncorporationForm';
-import Stages, { FormStages } from '~dashboard/ExpenditurePage/Stages';
+import { FormStages, LockedStages } from '~dashboard/ExpenditurePage/Stages';
import LockedIncorporationForm from '~dashboard/Incorporation/IncorporationForm/LockedIncorporationForm';
import VerificationBanner from '~dashboard/Incorporation/VerificationBanner';
import {
Motion,
MotionStatus,
MotionType,
- Status,
} from '~dashboard/ExpenditurePage/Stages/constants';
import { useDialog } from '~core/Dialog';
import CancelIncorporationDialog from '~dashboard/Dialogs/CancelIncorporationDialog';
import IncorporationPaymentDialog from '~dashboard/Dialogs/IncorporationPaymentDialog';
+import { ViewFor } from '~dashboard/ExpenditurePage/Stages/LinkedMotions/LinkedMotions';
import {
initialValues,
@@ -26,9 +26,6 @@ import {
validationSchema,
Stages as StagesEnum,
formValuesMock,
- Motion,
- MotionType,
- MotionStatus,
userMock,
} from './constants';
import { ValuesType } from './types';
@@ -56,8 +53,7 @@ const IncorporationPage = () => {
const notVerified = true; // temporary valule
const openPayDialog = useDialog(IncorporationPaymentDialog);
- const [motions, setMotions] = useState([]);
- const [status, setStatus] = useState();
+ const [motion, setMotions] = useState([]);
const handleSubmit = useCallback((values) => {
setFormValues(values);
@@ -94,9 +90,13 @@ const IncorporationPage = () => {
return motionItem;
}),
);
+ if (passed) {
+ setActiveStageId(StagesEnum.Processing);
+ }
},
isVotingExtensionEnabled: true,
- colony: colonyData?.processedColony,});
+ colony: colonyData?.processedColony,
+ });
}, [colonyData, openPayDialog]);
const buttonAction = useMemo(() => {
@@ -244,19 +244,20 @@ const IncorporationPage = () => {
{colonyData && (
- ({
...stage,
id: stage.id.toString(),
label: stage.title,
- buttonAction,
+ buttonAction: buttonAction || (() => {}),
+ buttonText: stage.buttonText || '',
}))}
appearance={{ size: 'medium' }}
- handleButtonClick={buttonAction || (() => {})}
colony={colonyData?.processedColony}
- viewFor="incorporation"
+ viewFor={ViewFor.INCORPORATION}
handleCancel={handleCancelIncorporation}
+ motion={motion}
/>
)}