Skip to content

Commit

Permalink
🩹 Sign addOpening tx with role account (#4770)
Browse files Browse the repository at this point in the history
* Sign `addOpening` tx with role account

* Do not pick a member to create an opening or leave a role

* Fix some other minor bugs

* Fix tests

* Fix the "See my Opening" link

* Do not check memberships to create an opening

* Fix tests

* Fix the workers sidebar logic
  • Loading branch information
thesan authored Feb 14, 2024
1 parent bd7d173 commit 4319baf
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 165 deletions.
2 changes: 2 additions & 0 deletions packages/ui/src/app/GlobalModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ const GUEST_ACCESSIBLE_MODALS: ModalNames[] = [
'EmailConfirmationModal',
'VoteRationaleModal',
'NominatingRedirect',
'CreateOpening',
'LeaveRole',
]

export const MODAL_WITH_CLOSE_CONFIRMATION: ModalNames[] = [
Expand Down
14 changes: 0 additions & 14 deletions packages/ui/src/app/pages/WorkingGroups/WorkingGroup/AboutTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -86,14 +83,3 @@ export const AboutTab = ({ workingGroup }: Props) => {
</MainPanel>
)
}

export const AboutTabSidebar = ({ workingGroup }: Props) => {
const { workers } = useWorkers({ groupId: workingGroup.id ?? '', status: 'active' })
const lead = workers?.find((worker) => worker.member.id === workingGroup.leadId)

return (
<SidePanel scrollable>
<WorkersList lead={lead} workers={workers} />
</SidePanel>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 (
<SidePanel scrollable>
<WorkersList lead={lead} workers={workers} />
</SidePanel>
)
}

const OpeningsCategories = styled.div`
display: grid;
grid-row-gap: 24px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
GetGroupDebtDocument,
GetBudgetSpendingDocument,
GetPastWorkersDocument,
GetRoleAccountsDocument,
} from '@/working-groups/queries'

import { WorkingGroupsModule } from '../WorkingGroupsModule'
Expand All @@ -27,12 +28,12 @@ type Args = {
type Story = StoryObj<FC<Args>>

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',
Expand Down Expand Up @@ -62,7 +63,7 @@ export default {
component: WorkingGroupsModule,

argTypes: {
onCreateOpening: { action: 'MembershipWorkingGroup.OpeningCreated' },
onCreateOpening: { action: 'OperationsWorkingGroupAlpha.OpeningCreated' },
},

args: {
Expand All @@ -72,27 +73,40 @@ export default {
parameters: {
router: { path: '/working-groups/:name', href: `/working-groups/${WG_DATA.name}` },
mocks: ({ args, parameters }: StoryContext<Args>): 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,
Expand All @@ -101,7 +115,7 @@ export default {
},
},
consts: {
membershipWorkingGroup: {
operationsWorkingGroupAlpha: {
minimumApplicationStake: joy(10),
minUnstakingPeriodLimit: 100,
},
Expand All @@ -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 },
Expand All @@ -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: {
Expand Down Expand Up @@ -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')
Expand All @@ -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'), '🐁?')
Expand All @@ -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)
Expand All @@ -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` },
Expand All @@ -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/))

Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<PageLayout
header={<WorkingGroupPageHeader group={group} />}
header={<WorkingGroupPageHeader name={name} group={group} />}
main={isLoading || !group ? <Loading /> : <AboutTab workingGroup={group} />}
sidebar={!isLoading && group && <AboutTabSidebar workingGroup={group} />}
sidebar={!isLoading && group && <WorkerListSidebar workingGroup={group} />}
sidebarScrollable
lastBreadcrumb={nameMapping(group?.name ?? name)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<PageLayout
header={<WorkingGroupPageHeader group={group} />}
header={<WorkingGroupPageHeader name={name} group={group} />}
main={isLoading || !group ? <Loading /> : <HistoryTab workingGroup={group} />}
sidebar={!isLoading && group && <HistoryTabSidebar workingGroup={group} />}
sidebarScrollable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<PageLayout
header={<WorkingGroupPageHeader group={group} withButtons />}
header={<WorkingGroupPageHeader name={name} group={group} withButtons />}
main={isLoading || !group ? <Loading /> : <OpeningsTab workingGroup={group} />}
sidebar={!isLoading && group && <OpeningsTabSidebar workingGroup={group} />}
sidebar={!isLoading && group && <WorkerListSidebar workingGroup={group} />}
sidebarScrollable
lastBreadcrumb="Openings"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<SidePanel scrollable>
<WorkersList lead={lead} workers={workers} />
</SidePanel>
)
}
Loading

0 comments on commit 4319baf

Please sign in to comment.