diff --git a/packages/ui/src/app/GlobalModals.tsx b/packages/ui/src/app/GlobalModals.tsx index f3bfdc6a4d..9f69df316e 100644 --- a/packages/ui/src/app/GlobalModals.tsx +++ b/packages/ui/src/app/GlobalModals.tsx @@ -209,6 +209,8 @@ const GUEST_ACCESSIBLE_MODALS: ModalNames[] = [ 'EmailConfirmationModal', 'VoteRationaleModal', 'NominatingRedirect', + 'CreateOpening', + 'LeaveRole', ] export const MODAL_WITH_CLOSE_CONFIRMATION: ModalNames[] = [ diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/AboutTab.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/AboutTab.tsx index fecb0dad73..f91398be67 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/AboutTab.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/AboutTab.tsx @@ -5,14 +5,11 @@ import { LinkSymbol } from '@/common/components/icons/symbols' import { Loading } from '@/common/components/Loading' import { MarkdownPreview } from '@/common/components/MarkdownPreview' import { MainPanel, RowGapBlock } from '@/common/components/page/PageContent' -import { SidePanel } from '@/common/components/page/SidePanel' import { StatisticItem, Statistics, TokenValueStat } from '@/common/components/statistics' import { NumericValueStat } from '@/common/components/statistics/NumericValueStat' import { wgListItemMappings } from '@/common/helpers' import { isDefined } from '@/common/utils' -import { WorkersList } from '@/working-groups/components/WorkersList' import { useGroupStatistics } from '@/working-groups/hooks/useGroupStatistics' -import { useWorkers } from '@/working-groups/hooks/useWorkers' import { WorkingGroup } from '@/working-groups/types' import { StatusBadge, StatusGroup, StatusTitleGroup } from '../components/StatusBadges' @@ -86,14 +83,3 @@ export const AboutTab = ({ workingGroup }: Props) => { ) } - -export const AboutTabSidebar = ({ workingGroup }: Props) => { - const { workers } = useWorkers({ groupId: workingGroup.id ?? '', status: 'active' }) - const lead = workers?.find((worker) => worker.member.id === workingGroup.leadId) - - return ( - - - - ) -} diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/OpeningsTab.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/OpeningsTab.tsx index 68f1daedc2..fc9a5ecc6f 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/OpeningsTab.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/OpeningsTab.tsx @@ -3,15 +3,12 @@ import styled from 'styled-components' import { CountBadge } from '@/common/components/CountBadge' import { MainPanel } from '@/common/components/page/PageContent' -import { SidePanel } from '@/common/components/page/SidePanel' import { Statistics, TokenValueStat } from '@/common/components/statistics' import { Label } from '@/common/components/typography' import { LoadingOpenings } from '@/working-groups/components/OpeningsList' -import { WorkersList } from '@/working-groups/components/WorkersList' import { useGroupDebt } from '@/working-groups/hooks/useGroupDebt' import { useOpenings } from '@/working-groups/hooks/useOpenings' import { useUpcomingOpenings } from '@/working-groups/hooks/useUpcomingOpenings' -import { useWorkers } from '@/working-groups/hooks/useWorkers' import { WorkingGroup } from '@/working-groups/types' interface Props { @@ -67,17 +64,6 @@ export const OpeningsTab = ({ workingGroup }: Props) => { ) } -export const OpeningsTabSidebar = ({ workingGroup }: Props) => { - const { workers } = useWorkers({ groupId: workingGroup.id ?? '', status: 'active' }) - const lead = workers?.find((worker) => worker.member.id === workingGroup.leadId) - - return ( - - - - ) -} - const OpeningsCategories = styled.div` display: grid; grid-row-gap: 24px; diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx index 6f89572030..cde3e6f577 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx @@ -15,6 +15,7 @@ import { GetGroupDebtDocument, GetBudgetSpendingDocument, GetPastWorkersDocument, + GetRoleAccountsDocument, } from '@/working-groups/queries' import { WorkingGroupsModule } from '../WorkingGroupsModule' @@ -27,12 +28,12 @@ type Args = { type Story = StoryObj> const WG_DATA = { - id: 'membershipWorkingGroup', - name: 'membership', + id: 'operationsWorkingGroupAlpha', + name: 'operationsWorkingGroupAlpha', } const WG_OPENING_METADATA = { - title: 'Membership worker role', + title: 'Builder worker role', shortDescription: 'Lorem Ipsum...', description: 'Bigger Lorem ipsum...', applicationDetails: 'Application process default', @@ -62,7 +63,7 @@ export default { component: WorkingGroupsModule, argTypes: { - onCreateOpening: { action: 'MembershipWorkingGroup.OpeningCreated' }, + onCreateOpening: { action: 'OperationsWorkingGroupAlpha.OpeningCreated' }, }, args: { @@ -72,27 +73,40 @@ export default { parameters: { router: { path: '/working-groups/:name', href: `/working-groups/${WG_DATA.name}` }, mocks: ({ args, parameters }: StoryContext): MocksParameters => { - const alice = member('alice', { - roles: [ - { - __typename: 'Worker', - id: `${WG_DATA.id}-0`, - createdAt: '2021', - isLead: args.isLead, - isActive: true, - group: { - __typename: 'WorkingGroup', - name: WG_DATA.name, - }, + const alice = member('alice') + const charlie = member('charlie') + + const role = { + __typename: 'Worker', + id: `${WG_DATA.id}-0`, + application: { opening: {} }, + entry: {}, + createdAt: '2021', + isLead: args.isLead, + isActive: true, + status: 'WorkerStatusActive', + group: { __typename: 'WorkingGroup', ...WG_DATA }, + } as const + + const workers = [ + { ...role, membership: alice }, + { + id: `${WG_DATA.id}-1`, + group: { + id: WG_DATA.id, + name: WG_DATA.name, }, - ], - }) + status: 'WorkerStatusActive', + membership: charlie, + }, + ] + return { - accounts: { active: { member: alice } }, + accounts: { active: { member: { ...alice, roles: [role] } } }, chain: { tx: { - membershipWorkingGroup: { + operationsWorkingGroupAlpha: { addOpening: { event: 'OpeningCreated', onSend: args.onCreateOpening, @@ -101,7 +115,7 @@ export default { }, }, consts: { - membershipWorkingGroup: { + operationsWorkingGroupAlpha: { minimumApplicationStake: joy(10), minUnstakingPeriodLimit: 100, }, @@ -115,8 +129,7 @@ export default { query: GetWorkingGroupDocument, data: { workingGroupByUniqueInput: { - id: WG_DATA.id, - name: WG_DATA.name, + ...WG_DATA, budget: joy(200), workers: [], leader: { membershipId: alice.id, isActive: args.isLead }, @@ -125,31 +138,16 @@ export default { }, { query: GetWorkersDocument, - data: { - workers: [ - { - id: `${WG_DATA.id}-0`, - group: { - id: WG_DATA.id, - name: WG_DATA.name, - }, - status: 'WorkerStatusActive', - membership: alice, - }, - { - id: `${WG_DATA.id}-1`, - group: { - id: WG_DATA.id, - name: WG_DATA.name, - }, - status: 'WorkerStatusActive', - membership: member('charlie'), - }, - ], - }, + data: { workers }, }, // Opening tab + { + query: GetRoleAccountsDocument, + data: { + workers: args.isLead ? [{ roleAccount: alice.controllerAccount }] : [], + }, + }, { query: GetGroupDebtDocument, data: { @@ -255,12 +253,13 @@ export const CreateOpening: Story = { await waitFor(() => expect(nextButton).toBeDisabled()) - await userEvent.type(openingTitleField, 'Membership worker role') + await userEvent.type(openingTitleField, 'Builder worker role') await userEvent.type(shortDescriptionField, 'Lorem Ipsum...') ;(await getEditorByLabel(modal, 'Description')).setData('Bigger Lorem ipsum...') await waitFor(() => expect(nextButton).toBeEnabled()) await userEvent.click(nextButton) }) + await step('Duration & Process', async () => { await waitFor(() => expect(nextButton).toBeDisabled()) ;(await getEditorByLabel(modal, 'Application process')).setData('Application process default') @@ -275,6 +274,7 @@ export const CreateOpening: Story = { await waitFor(() => expect(nextButton).toBeEnabled()) await userEvent.click(nextButton) }) + await step('Application Form', async () => { await waitFor(() => expect(nextButton).toBeDisabled()) await userEvent.type(modal.getByRole('textbox'), '🐁?') @@ -286,12 +286,19 @@ export const CreateOpening: Story = { await waitFor(() => expect(nextButton).toBeEnabled()) await userEvent.click(nextButton) }) + await step('Staking Policy & Reward', async () => { const createButton = getButtonByText(modal, 'Create Opening') expect(createButton).toBeDisabled() - await userEvent.type(modal.getByLabelText('Staking amount *'), '100') - await userEvent.clear(modal.getByLabelText('Role cooldown period')) - await userEvent.type(modal.getByLabelText('Role cooldown period'), '1000') + + const stakeAmountField = modal.getByLabelText('Staking amount *') + await userEvent.clear(stakeAmountField) + await userEvent.type(stakeAmountField, '100') + + const coolDownField = modal.getByLabelText('Role cooldown period') + await userEvent.clear(coolDownField) + await userEvent.type(coolDownField, '1000') + await userEvent.type(modal.getByLabelText('Reward amount per Block'), '0.1') await waitFor(() => expect(createButton).toBeEnabled()) await userEvent.click(createButton) @@ -314,8 +321,14 @@ export const CreateOpening: Story = { expect(openingType).toEqual('Regular') expect(metadataFromBytes(OpeningMetadata, description)).toEqual(WG_OPENING_METADATA) }) + + await step('Link to new Opening', async () => { + const openingLink = (await modal.findByText('See my Opening')).parentElement as Element + expect(openingLink.getAttribute('href')).toBe('/working-groups/openings/builders-1') + }) }, } + export const CreateOpeningImport: Story = { parameters: { router: { path: '/working-groups/:name/openings', href: `/working-groups/${WG_DATA.name}/openings` }, @@ -342,6 +355,7 @@ export const CreateOpeningImport: Story = { new File([JSON.stringify(WG_JSON_OPENING)], 'file.json', { type: 'application/json' }) ) }) + await step('Check imported data', async () => { expect(await modal.findByText(/File imported successfully, preview your input/)) @@ -366,10 +380,12 @@ export const CreateOpeningImport: Story = { const createButton = getButtonByText(modal, 'Create Opening') await userEvent.click(createButton) }) + await step('Sign transaction and Create', async () => { expect(await modal.findByText('You intend to create an Opening.')) await userEvent.click(modal.getByText('Sign transaction and Create')) }) + step('Transaction parameters', () => { const [description, openingType, stakePolicy, rewardPerBlock] = args.onCreateOpening.mock.calls.at(-1) diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx index ca8c5e3369..e0927a7e71 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx @@ -7,18 +7,21 @@ import { nameMapping } from '@/common/helpers' import { useWorkingGroup } from '@/working-groups/hooks/useWorkingGroup' import { urlParamToWorkingGroupId } from '@/working-groups/model/workingGroupName' -import { AboutTab, AboutTabSidebar } from './AboutTab' +import { AboutTab } from './AboutTab' +import { WorkerListSidebar } from './components/WorkerListSidebar' import { WorkingGroupPageHeader } from './components/WorkingGroupPageHeader' export const WorkingGroup = () => { - const { name } = useParams<{ name: string }>() - const { isLoading, group } = useWorkingGroup({ name: urlParamToWorkingGroupId(name) }) + const params = useParams<{ name: string }>() + const name = urlParamToWorkingGroupId(params.name) + + const { isLoading, group } = useWorkingGroup({ name }) return ( } + header={} main={isLoading || !group ? : } - sidebar={!isLoading && group && } + sidebar={!isLoading && group && } sidebarScrollable lastBreadcrumb={nameMapping(group?.name ?? name)} /> diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupHistory.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupHistory.tsx index 3ff8e9a872..b1107a1804 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupHistory.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupHistory.tsx @@ -10,12 +10,14 @@ import { WorkingGroupPageHeader } from './components/WorkingGroupPageHeader' import { HistoryTab, HistoryTabSidebar } from './HistoryTab' export function WorkingGroupHistory() { - const { name } = useParams<{ name: string }>() - const { isLoading, group } = useWorkingGroup({ name: urlParamToWorkingGroupId(name) }) + const params = useParams<{ name: string }>() + const name = urlParamToWorkingGroupId(params.name) + + const { isLoading, group } = useWorkingGroup({ name }) return ( } + header={} main={isLoading || !group ? : } sidebar={!isLoading && group && } sidebarScrollable diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx index e1d9b73b0a..ac5ab79a47 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx @@ -6,18 +6,21 @@ import { Loading } from '@/common/components/Loading' import { useWorkingGroup } from '@/working-groups/hooks/useWorkingGroup' import { urlParamToWorkingGroupId } from '@/working-groups/model/workingGroupName' +import { WorkerListSidebar } from './components/WorkerListSidebar' import { WorkingGroupPageHeader } from './components/WorkingGroupPageHeader' -import { OpeningsTab, OpeningsTabSidebar } from './OpeningsTab' +import { OpeningsTab } from './OpeningsTab' export function WorkingGroupOpenings() { - const { name } = useParams<{ name: string }>() - const { isLoading, group } = useWorkingGroup({ name: urlParamToWorkingGroupId(name) }) + const params = useParams<{ name: string }>() + const name = urlParamToWorkingGroupId(params.name) + + const { isLoading, group } = useWorkingGroup({ name }) return ( } + header={} main={isLoading || !group ? : } - sidebar={!isLoading && group && } + sidebar={!isLoading && group && } sidebarScrollable lastBreadcrumb="Openings" /> diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkerListSidebar.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkerListSidebar.tsx new file mode 100644 index 0000000000..6338db4e98 --- /dev/null +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkerListSidebar.tsx @@ -0,0 +1,21 @@ +import React from 'react' + +import { SidePanel } from '@/common/components/page/SidePanel' +import { WorkersList } from '@/working-groups/components/WorkersList' +import { useWorkers } from '@/working-groups/hooks/useWorkers' +import { WorkingGroup } from '@/working-groups/types' + +interface Props { + workingGroup: WorkingGroup +} + +export const WorkerListSidebar = ({ workingGroup }: Props) => { + const { workers } = useWorkers({ groupId: workingGroup.id ?? '', status: 'active' }) + const lead = workingGroup.isActive ? workers?.find((worker) => worker.member.id === workingGroup.leadId) : undefined + + return ( + + + + ) +} diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkingGroupPageHeader.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkingGroupPageHeader.tsx index fbbb94e841..56496e6d22 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkingGroupPageHeader.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkingGroupPageHeader.tsx @@ -1,10 +1,8 @@ -import React, { useMemo } from 'react' -import { useParams } from 'react-router-dom' +import React from 'react' import { PageHeader } from '@/app/components/PageHeader' import { nameMapping } from '@/common/helpers' import { CreateOpeningButton } from '@/working-groups/components/CreateOpeningButton' -import { useMyWorkers } from '@/working-groups/hooks/useMyWorkers' import { WorkingGroup } from '@/working-groups/types' import { StatusBadge, StatusGroup } from '../../components/StatusBadges' @@ -12,21 +10,15 @@ import { StatusBadge, StatusGroup } from '../../components/StatusBadges' import { WorkingGroupTabs } from './WorkingGroupTabs' interface WorkingGroupPageHeaderProps { + name: string group: WorkingGroup | undefined withButtons?: boolean } -export const WorkingGroupPageHeader = React.memo(({ group, withButtons = false }: WorkingGroupPageHeaderProps) => { - const { name } = useParams<{ name: string }>() - const { workers } = useMyWorkers() - const isLead = useMemo( - () => group?.isActive && workers.find((w) => w.membership.id === group?.leadId), - [workers, group?.isActive, group?.leadId] - ) - +export const WorkingGroupPageHeader = React.memo(({ name, group, withButtons }: WorkingGroupPageHeaderProps) => { return ( ) } - buttons={withButtons && group && isLead && } + buttons={withButtons && } tabs={} /> ) diff --git a/packages/ui/src/common/components/buttons/LinkButtons.tsx b/packages/ui/src/common/components/buttons/LinkButtons.tsx index 6ee611ee24..2854ba176f 100644 --- a/packages/ui/src/common/components/buttons/LinkButtons.tsx +++ b/packages/ui/src/common/components/buttons/LinkButtons.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { MouseEventHandler } from 'react' import { Link } from 'react-router-dom' import styled, { css } from 'styled-components' @@ -12,6 +12,7 @@ export interface LinkButtonProps extends LinkButtonSizingProps { children?: React.ReactNode disabled?: boolean to: string + onClick?: MouseEventHandler } interface LinkButtonSizingProps { @@ -39,41 +40,41 @@ const getPadding = (props: BasicLinkButtonStylesProps) => { return props.$square ? '8px' : '8px 16px' } -export function LinkButtonPrimary({ className, children, size, square, disabled, to }: LinkButtonProps) { +export function LinkButtonPrimary({ children, size, square, ...props }: LinkButtonProps) { return ( - + {children} ) } -export function LinkButtonSecondary({ className, children, size, square, disabled, to }: LinkButtonProps) { +export function LinkButtonSecondary({ children, size, square, ...props }: LinkButtonProps) { return ( - + {children} ) } -export function LinkButtonGhost({ className, children, size, square, disabled, to }: LinkButtonProps) { +export function LinkButtonGhost({ children, size, square, ...props }: LinkButtonProps) { return ( - + {children} ) } -export function LinkButtonBareGhost({ className, children, size, square, disabled, to }: LinkButtonProps) { +export function LinkButtonBareGhost({ children, size, square, ...props }: LinkButtonProps) { return ( - + {children} ) } -export function LinkButtonLink({ className, children, square, disabled, to }: LinkButtonProps) { +export function LinkButtonLink({ children, square, ...props }: LinkButtonProps) { return ( - + {children} ) diff --git a/packages/ui/src/working-groups/components/CreateOpeningButton.tsx b/packages/ui/src/working-groups/components/CreateOpeningButton.tsx index fe11aa10eb..878c81e0b4 100644 --- a/packages/ui/src/working-groups/components/CreateOpeningButton.tsx +++ b/packages/ui/src/working-groups/components/CreateOpeningButton.tsx @@ -1,24 +1,35 @@ -import React, { useCallback } from 'react' +import React from 'react' +import { useMyAccounts } from '@/accounts/hooks/useMyAccounts' import { TransactionButton } from '@/common/components/buttons/TransactionButton' import { PlusIcon } from '@/common/components/icons/PlusIcon' import { useModal } from '@/common/hooks/useModal' +import { useRoleAccount } from '@/working-groups/hooks/useRoleAccount' import { CreateOpeningModalCall } from '@/working-groups/modals/CreateOpening/types' -import { GroupIdName } from '@/working-groups/types' +import { WorkingGroup } from '@/working-groups/types' -export interface CreateOpeningButtonProps { - group: GroupIdName +type CreateOpeningButtonProps = { + name: string + group: WorkingGroup | undefined } -export const CreateOpeningButton = ({ group }: CreateOpeningButtonProps) => { +export const CreateOpeningButton = ({ name, group }: CreateOpeningButtonProps) => { const { showModal } = useModal() - const createOpening = useCallback( - () => showModal({ modal: 'CreateOpening', data: { group } }), - [group] - ) + + const { roleAccount: leadAccount } = useRoleAccount({ isLead_eq: true, isActive_eq: true, group: { name_eq: name } }) + const { allAccounts } = useMyAccounts() + const isLead = allAccounts.some((account) => account.address === leadAccount) + + if (!group || !leadAccount || !isLead) { + return null + } return ( - + showModal({ modal: 'CreateOpening', data: { group, leadAccount } })} + > Add opening diff --git a/packages/ui/src/working-groups/hooks/useRoleAccount.ts b/packages/ui/src/working-groups/hooks/useRoleAccount.ts new file mode 100644 index 0000000000..c7a0287faa --- /dev/null +++ b/packages/ui/src/working-groups/hooks/useRoleAccount.ts @@ -0,0 +1,13 @@ +import { useMemo } from 'react' + +import { WorkerWhereInput } from '@/common/api/queries' +import { useGetRoleAccountsQuery } from '@/working-groups/queries' + +type UseRoleAccount = { roleAccount: string | undefined; isLoading: boolean } + +export const useRoleAccount = (where: WorkerWhereInput): UseRoleAccount => { + const { data, loading } = useGetRoleAccountsQuery({ variables: { where } }) + const roleAccount = useMemo(() => data?.workers.at(0)?.roleAccount, [data, loading]) + + return { roleAccount, isLoading: loading } +} diff --git a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx index d18860a977..964222b69e 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx @@ -1,3 +1,4 @@ +import { merge } from 'lodash' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { FormProvider, useForm } from 'react-hook-form' import styled from 'styled-components' @@ -16,39 +17,58 @@ import { isLastStepActive } from '@/common/modals/utils' import { getSteps } from '@/common/model/machines/getSteps' import { useYupValidationResolver } from '@/common/utils/validation' import { machineStateConverter } from '@/council/modals/AnnounceCandidacy/helpers' -import { useMyMemberships } from '@/memberships/hooks/useMyMemberships' import { StyledStepperBody } from '@/proposals/modals/AddNewProposal' import { SuccessModal, CreateOpeningSteps as Steps, ImportOpening } from './components' import { createOpeningMachine, CreateOpeningMachineState } from './machine' -import { OpeningConditions, CreateOpeningForm, CreateOpeningModalCall, OpeningSchema, defaultValues } from './types' +import { + OpeningConditions, + CreateOpeningForm, + CreateOpeningModalCall, + OpeningSchema, + defaultValues as _defaultValue, +} from './types' import { getTxParams } from './utils' export const CreateOpeningModal = () => { const [showImport, setShowImport] = useState(false) const { api } = useApi() - const { active: activeMember } = useMyMemberships() const [state, send, service] = useMachine(createOpeningMachine) const { hideModal, modalData } = useModal() - const { group } = modalData - const workingGroupConsts = api?.consts[group] - - const context = { - group, - minUnstakingPeriodLimit: workingGroupConsts?.minUnstakingPeriodLimit, - minimumApplicationStake: workingGroupConsts?.minimumApplicationStake, - } as OpeningConditions + const { id: group, name: groupName } = modalData.group + const signer = modalData.leadAccount + + const context = useMemo( + () => + ({ + group, + minUnstakingPeriodLimit: api?.consts[group].minUnstakingPeriodLimit, + minimumApplicationStake: api?.consts[group].minimumApplicationStake, + } as OpeningConditions), + [api?.isConnected] + ) const path = useMemo(() => machineStateConverter(state.value), [state.value]) const resolver = useYupValidationResolver(OpeningSchema, path) + const defaultValues = useMemo( + () => + merge({}, _defaultValue, { + stakingPolicyAndReward: { + stakingAmount: context.minimumApplicationStake, + leavingUnstakingPeriod: context.minUnstakingPeriodLimit?.toNumber(), + }, + }), + [context] + ) + const form = useForm({ resolver, mode: 'onChange', defaultValues, context }) useEffect(() => { form.trigger(machineStateConverter(state.value) as keyof CreateOpeningForm) }, [machineStateConverter(state.value)]) const { transaction, feeInfo } = useTransactionFee( - activeMember?.controllerAccount, + signer, () => { if (api && group) { const { ...specifics } = form.getValues() as CreateOpeningForm @@ -56,8 +76,9 @@ export const CreateOpeningModal = () => { return api.tx[group].addOpening(description, 'Regular', stakePolicy, String(rewardPerBlock)) } }, - [api?.isConnected, activeMember?.id, group, form.formState.isValidating] + [api?.isConnected, signer, group, form.formState.isValidating] ) + const exportedJsonValue = useMemo(() => { const { ...specifics } = form.getValues() as CreateOpeningForm const exportValue = { @@ -85,24 +106,21 @@ export const CreateOpeningModal = () => { }, [send]) useEffect((): any => { - if (state.matches('requirementsVerification')) { - return feeInfo && send(feeInfo.canAfford ? 'NEXT' : 'FAIL') - } if (state.matches('beforeTransaction')) { return feeInfo?.canAfford ? send('NEXT') : send('FAIL') } - }, [state, activeMember?.id, feeInfo]) + }, [state, feeInfo]) - if (!activeMember || state.matches('requirementsFailed')) { + if (state.matches('requirementsFailed')) { return null } - if (state.matches('transaction') && transaction && group) { + if (state.matches('transaction') && transaction) { return ( You intend to create an Opening. @@ -111,7 +129,7 @@ export const CreateOpeningModal = () => { } if (state.matches('success') && group) { - return + return } return ( diff --git a/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx b/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx index c2de543f41..c62991b4e4 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx @@ -1,26 +1,23 @@ import React from 'react' -import { useHistory } from 'react-router-dom' +import { generatePath } from 'react-router-dom' -import { ButtonGhost } from '@/common/components/buttons' +import { LinkButtonGhost } from '@/common/components/buttons/LinkButtons' import { SuccessSymbol } from '@/common/components/icons/symbols' import { Info } from '@/common/components/Info' import { Modal, ModalBody, ModalFooter, ModalHeader } from '@/common/components/Modal' import { TextMedium } from '@/common/components/typography' -import { GroupIdToGroupParam } from '@/working-groups/constants' -import { GroupIdName } from '@/working-groups/types' +import { nameMapping } from '@/common/helpers' +import { WorkingGroupsRoutes } from '@/working-groups/constants' +import { groupNameToURLParam } from '@/working-groups/model/workingGroupName' interface SuccessModalProps { onClose: () => void - groupId: GroupIdName + groupName: string + openingRuntimeId: number } -export const SuccessModal = ({ onClose, groupId }: SuccessModalProps) => { - const history = useHistory() - - const redirect = () => { - onClose() - history.push(`/working-groups/${GroupIdToGroupParam[groupId].toLowerCase()}`) - } +export const SuccessModal = ({ onClose, groupName, openingRuntimeId }: SuccessModalProps) => { + const openingId = `${groupNameToURLParam(nameMapping(groupName))}${openingRuntimeId}` return ( @@ -31,9 +28,13 @@ export const SuccessModal = ({ onClose, groupId }: SuccessModalProps) => { - - Back to Working Group - + + See my Opening + ) diff --git a/packages/ui/src/working-groups/modals/CreateOpening/machine.ts b/packages/ui/src/working-groups/modals/CreateOpening/machine.ts index 8a94cbdc2f..2186734804 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/machine.ts +++ b/packages/ui/src/working-groups/modals/CreateOpening/machine.ts @@ -14,7 +14,6 @@ import { EmptyObject } from '@/common/types' import { CreateOpeningForm, TransactionContext } from './types' export type CreateOpeningState = - | { value: 'requirementsVerification'; context: EmptyObject } | { value: 'requirementsFailed'; context: EmptyObject } | { value: 'workingGroupAndDescription'; context: Required } | { value: 'durationAndProcess'; context: Required } @@ -37,9 +36,8 @@ export type CreateOpeningMachineState = State< type Context = CreateOpeningForm & TransactionContext export const createOpeningMachine = createMachine, CreateOpeningEvent, CreateOpeningState>({ - initial: 'requirementsVerification', + initial: 'workingGroupAndDescription', states: { - requirementsVerification: { on: { FAIL: 'requirementsFailed', NEXT: 'workingGroupAndDescription' } }, requirementsFailed: { type: 'final' }, workingGroupAndDescription: { meta: { diff --git a/packages/ui/src/working-groups/modals/CreateOpening/types.tsx b/packages/ui/src/working-groups/modals/CreateOpening/types.tsx index a60c0d3ee3..4cfc63a95d 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/types.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/types.tsx @@ -5,10 +5,11 @@ import * as Yup from 'yup' import { QuestionValueProps } from '@/common/components/EditableInputList/EditableInputList' import { ModalWithDataCall } from '@/common/providers/modal/types' import { BNSchema, minContext, minMixed } from '@/common/utils/validation' -import { GroupIdName } from '@/working-groups/types' +import { GroupIdName, WorkingGroup } from '@/working-groups/types' export interface OpeningModalData { - group: GroupIdName + group: WorkingGroup + leadAccount: string } export type CreateOpeningModalCall = ModalWithDataCall<'CreateOpening', OpeningModalData> diff --git a/packages/ui/src/working-groups/queries/__generated__/workingGroups.generated.tsx b/packages/ui/src/working-groups/queries/__generated__/workingGroups.generated.tsx index 112006efcb..693ea4dd0a 100644 --- a/packages/ui/src/working-groups/queries/__generated__/workingGroups.generated.tsx +++ b/packages/ui/src/working-groups/queries/__generated__/workingGroups.generated.tsx @@ -285,6 +285,15 @@ export type GetWorkingGroupsQuery = { }> } +export type GetRoleAccountsQueryVariables = Types.Exact<{ + where?: Types.InputMaybe +}> + +export type GetRoleAccountsQuery = { + __typename: 'Query' + workers: Array<{ __typename: 'Worker'; roleAccount: string }> +} + export type GetWorkersQueryVariables = Types.Exact<{ where?: Types.InputMaybe offset?: Types.InputMaybe @@ -1870,6 +1879,45 @@ export function useGetWorkingGroupsLazyQuery( export type GetWorkingGroupsQueryHookResult = ReturnType export type GetWorkingGroupsLazyQueryHookResult = ReturnType export type GetWorkingGroupsQueryResult = Apollo.QueryResult +export const GetRoleAccountsDocument = gql` + query GetRoleAccounts($where: WorkerWhereInput) { + workers(where: $where) { + roleAccount + } + } +` + +/** + * __useGetRoleAccountsQuery__ + * + * To run a query within a React component, call `useGetRoleAccountsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetRoleAccountsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetRoleAccountsQuery({ + * variables: { + * where: // value for 'where' + * }, + * }); + */ +export function useGetRoleAccountsQuery( + baseOptions?: Apollo.QueryHookOptions +) { + const options = { ...defaultOptions, ...baseOptions } + return Apollo.useQuery(GetRoleAccountsDocument, options) +} +export function useGetRoleAccountsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions +) { + const options = { ...defaultOptions, ...baseOptions } + return Apollo.useLazyQuery(GetRoleAccountsDocument, options) +} +export type GetRoleAccountsQueryHookResult = ReturnType +export type GetRoleAccountsLazyQueryHookResult = ReturnType +export type GetRoleAccountsQueryResult = Apollo.QueryResult export const GetWorkersDocument = gql` query GetWorkers($where: WorkerWhereInput, $offset: Int, $limit: Int) { workers(where: $where, offset: $offset, limit: $limit) { diff --git a/packages/ui/src/working-groups/queries/workingGroups.graphql b/packages/ui/src/working-groups/queries/workingGroups.graphql index 377fef292e..7b60ce6c51 100644 --- a/packages/ui/src/working-groups/queries/workingGroups.graphql +++ b/packages/ui/src/working-groups/queries/workingGroups.graphql @@ -129,6 +129,12 @@ query GetWorkingGroups { } } +query GetRoleAccounts($where: WorkerWhereInput) { + workers(where: $where) { + roleAccount + } +} + query GetWorkers($where: WorkerWhereInput, $offset: Int, $limit: Int) { workers(where: $where, offset: $offset, limit: $limit) { ...WorkerFields