From a59742ce19581aacf2f6d8184042a8775046fcb2 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Fri, 9 Feb 2024 12:22:50 +0100 Subject: [PATCH 1/8] Sign `addOpening` tx with role account --- .../WorkingGroup/WorkingGroup.tsx | 6 +++--- .../components/CreateOpeningButton.tsx | 10 +++++----- .../modals/CreateOpening/CreateOpening.tsx | 19 ++++++++++--------- .../modals/CreateOpening/types.tsx | 4 ++-- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx index 22616437e1..7d96d9fa9f 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx @@ -26,7 +26,7 @@ export function WorkingGroup() { const { name } = useParams<{ name: string }>() const { isLoading, group } = useWorkingGroup({ name: urlParamToWorkingGroupId(name) }) const { workers } = useMyWorkers() - const isLead = useMemo( + const lead = useMemo( () => group?.isActive && workers.find((w) => w.membership.id === group?.leadId), [workers, group?.isActive, group?.leadId] ) @@ -75,9 +75,9 @@ export function WorkingGroup() { )} - {group && isLead && currentTab === 'OPENINGS' && ( + {group && lead && currentTab === 'OPENINGS' && ( - + )} diff --git a/packages/ui/src/working-groups/components/CreateOpeningButton.tsx b/packages/ui/src/working-groups/components/CreateOpeningButton.tsx index fe11aa10eb..55821d3a4e 100644 --- a/packages/ui/src/working-groups/components/CreateOpeningButton.tsx +++ b/packages/ui/src/working-groups/components/CreateOpeningButton.tsx @@ -4,17 +4,17 @@ import { TransactionButton } from '@/common/components/buttons/TransactionButton import { PlusIcon } from '@/common/components/icons/PlusIcon' import { useModal } from '@/common/hooks/useModal' import { CreateOpeningModalCall } from '@/working-groups/modals/CreateOpening/types' -import { GroupIdName } from '@/working-groups/types' +import { Worker } from '@/working-groups/types' export interface CreateOpeningButtonProps { - group: GroupIdName + worker: Worker } -export const CreateOpeningButton = ({ group }: CreateOpeningButtonProps) => { +export const CreateOpeningButton = ({ worker }: CreateOpeningButtonProps) => { const { showModal } = useModal() const createOpening = useCallback( - () => showModal({ modal: 'CreateOpening', data: { group } }), - [group] + () => showModal({ modal: 'CreateOpening', data: { worker } }), + [worker] ) return ( diff --git a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx index d18860a977..3c5555d9ea 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx @@ -16,8 +16,8 @@ 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 { useWorker } from '@/working-groups/hooks/useWorker' import { SuccessModal, CreateOpeningSteps as Steps, ImportOpening } from './components' import { createOpeningMachine, CreateOpeningMachineState } from './machine' @@ -28,11 +28,11 @@ 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 group = modalData.worker.group.id + const { worker } = useWorker(modalData.worker.id) const workingGroupConsts = api?.consts[group] const context = { @@ -48,7 +48,7 @@ export const CreateOpeningModal = () => { }, [machineStateConverter(state.value)]) const { transaction, feeInfo } = useTransactionFee( - activeMember?.controllerAccount, + worker?.roleAccount, () => { if (api && group) { const { ...specifics } = form.getValues() as CreateOpeningForm @@ -56,8 +56,9 @@ export const CreateOpeningModal = () => { return api.tx[group].addOpening(description, 'Regular', stakePolicy, String(rewardPerBlock)) } }, - [api?.isConnected, activeMember?.id, group, form.formState.isValidating] + [api?.isConnected, worker?.roleAccount, group, form.formState.isValidating] ) + const exportedJsonValue = useMemo(() => { const { ...specifics } = form.getValues() as CreateOpeningForm const exportValue = { @@ -91,18 +92,18 @@ export const CreateOpeningModal = () => { 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 && worker) { return ( You intend to create an Opening. diff --git a/packages/ui/src/working-groups/modals/CreateOpening/types.tsx b/packages/ui/src/working-groups/modals/CreateOpening/types.tsx index a60c0d3ee3..03ecd61aee 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/types.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/types.tsx @@ -5,10 +5,10 @@ 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, Worker } from '@/working-groups/types' export interface OpeningModalData { - group: GroupIdName + worker: Worker } export type CreateOpeningModalCall = ModalWithDataCall<'CreateOpening', OpeningModalData> From a6f7f961d832620668dcdea6f4266773bae369e9 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Fri, 9 Feb 2024 12:37:05 +0100 Subject: [PATCH 2/8] Do not pick a member to create an opening or leave a role --- packages/ui/src/app/GlobalModals.tsx | 2 ++ 1 file changed, 2 insertions(+) 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[] = [ From e3e0a6e990079599a80bc3b701b4188652039f14 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Fri, 9 Feb 2024 20:58:44 +0100 Subject: [PATCH 3/8] Fix some other minor bugs --- .../modals/CreateOpening/CreateOpening.tsx | 41 ++++++++++++++----- .../CreateOpening/components/Success.tsx | 17 ++++---- .../modals/CreateOpening/machine.ts | 4 +- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx index 3c5555d9ea..10181d25ae 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' @@ -21,7 +22,13 @@ import { useWorker } from '@/working-groups/hooks/useWorker' 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 = () => { @@ -32,16 +39,31 @@ export const CreateOpeningModal = () => { const { hideModal, modalData } = useModal() const group = modalData.worker.group.id + const groupName = modalData.worker.group.name const { worker } = useWorker(modalData.worker.id) - const workingGroupConsts = api?.consts[group] - const context = { - group, - minUnstakingPeriodLimit: workingGroupConsts?.minUnstakingPeriodLimit, - minimumApplicationStake: workingGroupConsts?.minimumApplicationStake, - } as OpeningConditions + 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) @@ -86,9 +108,6 @@ 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') } @@ -112,7 +131,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..819a8e27f9 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx @@ -1,25 +1,28 @@ import React from 'react' -import { useHistory } from 'react-router-dom' +import { generatePath, useHistory } from 'react-router-dom' import { ButtonGhost } from '@/common/components/buttons' 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) => { +export const SuccessModal = ({ onClose, groupName, openingRuntimeId }: SuccessModalProps) => { const history = useHistory() + const openingId = `${groupNameToURLParam(nameMapping(groupName))}-${openingRuntimeId}` const redirect = () => { onClose() - history.push(`/working-groups/${GroupIdToGroupParam[groupId].toLowerCase()}`) + history.push(generatePath(WorkingGroupsRoutes.openingById, { id: openingId })) } return ( @@ -32,7 +35,7 @@ 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: { From f8cfff6f8780e5e05b1454517eeba4a793fde111 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Fri, 9 Feb 2024 21:42:03 +0100 Subject: [PATCH 4/8] Fix tests --- .../WorkingGroup/WorkingGroup.stories.tsx | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) 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 86ab1bd6d4..00a6594f7e 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx @@ -9,7 +9,7 @@ import { metadataFromBytes } from '@/common/model/JoystreamNode' import { member } from '@/mocks/data/members' import { getButtonByText, getEditorByLabel, joy, withinModal } from '@/mocks/helpers' import { MocksParameters } from '@/mocks/providers' -import { GetWorkersDocument, GetWorkingGroupDocument } from '@/working-groups/queries' +import { GetWorkerDocument, GetWorkersDocument, GetWorkingGroupDocument } from '@/working-groups/queries' import { WorkingGroup } from './WorkingGroup' @@ -66,23 +66,28 @@ export default { parameters: { router: { path: '/:name', href: `/${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 role = { + __typename: 'Worker', + id: `${WG_DATA.id}-0`, + application: { opening: {} }, + entry: {}, + createdAt: '2021', + isLead: args.isLead, + isActive: true, + status: 'WorkerStatusActive', + roleAccount: alice.controllerAccount, + group: { + __typename: 'WorkingGroup', + ...WG_DATA, + }, + } as const + + const worker = { ...role, membership: alice } + return { - accounts: { active: { member: alice } }, + accounts: { active: { member: { ...alice, roles: [role] } } }, chain: { tx: { @@ -108,8 +113,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 }, @@ -120,15 +124,7 @@ export default { query: GetWorkersDocument, data: { workers: [ - { - id: `${WG_DATA.id}-0`, - group: { - id: WG_DATA.id, - name: WG_DATA.name, - }, - status: 'WorkerStatusActive', - membership: alice, - }, + worker, { id: `${WG_DATA.id}-1`, group: { @@ -141,6 +137,10 @@ export default { ], }, }, + { + query: GetWorkerDocument, + data: { workerByUniqueInput: worker }, + }, ], }, } @@ -199,9 +199,15 @@ export const CreateOpening: Story = { 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) From 7d6e654f3d7d333e69caf046814c74a742e000e0 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Tue, 13 Feb 2024 13:46:32 +0100 Subject: [PATCH 5/8] Fix the "See my Opening" link --- .../WorkingGroup/WorkingGroup.stories.tsx | 26 ++++++++++++++----- .../common/components/buttons/LinkButtons.tsx | 23 ++++++++-------- .../CreateOpening/components/Success.tsx | 20 +++++++------- 3 files changed, 40 insertions(+), 29 deletions(-) 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 00a6594f7e..903a466896 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx @@ -21,12 +21,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', @@ -56,7 +56,7 @@ export default { component: WorkingGroup, argTypes: { - onCreateOpening: { action: 'MembershipWorkingGroup.OpeningCreated' }, + onCreateOpening: { action: 'OperationsWorkingGroupAlpha.OpeningCreated' }, }, args: { @@ -91,7 +91,7 @@ export default { chain: { tx: { - membershipWorkingGroup: { + operationsWorkingGroupAlpha: { addOpening: { event: 'OpeningCreated', onSend: args.onCreateOpening, @@ -100,7 +100,7 @@ export default { }, }, consts: { - membershipWorkingGroup: { + operationsWorkingGroupAlpha: { minimumApplicationStake: joy(10), minUnstakingPeriodLimit: 100, }, @@ -165,12 +165,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') @@ -185,6 +186,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'), '🐁?') @@ -196,6 +198,7 @@ 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() @@ -230,8 +233,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 = { play: async ({ args, canvasElement, step }) => { const screen = within(canvasElement) @@ -254,6 +263,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/)) @@ -278,10 +288,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/common/components/buttons/LinkButtons.tsx b/packages/ui/src/common/components/buttons/LinkButtons.tsx index 56d79b1d1d..be59e48b16 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/modals/CreateOpening/components/Success.tsx b/packages/ui/src/working-groups/modals/CreateOpening/components/Success.tsx index 819a8e27f9..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,7 +1,7 @@ import React from 'react' -import { generatePath, 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' @@ -17,13 +17,7 @@ interface SuccessModalProps { } export const SuccessModal = ({ onClose, groupName, openingRuntimeId }: SuccessModalProps) => { - const history = useHistory() - - const openingId = `${groupNameToURLParam(nameMapping(groupName))}-${openingRuntimeId}` - const redirect = () => { - onClose() - history.push(generatePath(WorkingGroupsRoutes.openingById, { id: openingId })) - } + const openingId = `${groupNameToURLParam(nameMapping(groupName))}${openingRuntimeId}` return ( @@ -34,9 +28,13 @@ export const SuccessModal = ({ onClose, groupName, openingRuntimeId }: SuccessMo - + See my Opening - + ) From 0501dce332fc2aac8709dd539ac3f142c14ce6fe Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Wed, 14 Feb 2024 20:47:11 +0100 Subject: [PATCH 6/8] Do not check memberships to create an opening --- .../WorkingGroup/WorkingGroup.tsx | 8 ++-- .../WorkingGroup/WorkingGroupHistory.tsx | 8 ++-- .../WorkingGroup/WorkingGroupOpenings.tsx | 8 ++-- .../components/WorkingGroupPageHeader.tsx | 18 ++----- .../components/CreateOpeningButton.tsx | 31 ++++++++---- .../working-groups/hooks/useRoleAccount.ts | 13 +++++ .../modals/CreateOpening/CreateOpening.tsx | 14 +++--- .../modals/CreateOpening/types.tsx | 5 +- .../__generated__/workingGroups.generated.tsx | 48 +++++++++++++++++++ .../queries/workingGroups.graphql | 6 +++ 10 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 packages/ui/src/working-groups/hooks/useRoleAccount.ts diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx index ca8c5e3369..b18938b135 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx @@ -11,12 +11,14 @@ import { AboutTab, AboutTabSidebar } from './AboutTab' 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 && } sidebarScrollable 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..42bdb27e3f 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx @@ -10,12 +10,14 @@ import { WorkingGroupPageHeader } from './components/WorkingGroupPageHeader' import { OpeningsTab, OpeningsTabSidebar } 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 && } sidebarScrollable 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 690f381516..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 lead = 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 && lead && } + buttons={withButtons && } tabs={} /> ) diff --git a/packages/ui/src/working-groups/components/CreateOpeningButton.tsx b/packages/ui/src/working-groups/components/CreateOpeningButton.tsx index 55821d3a4e..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 { Worker } from '@/working-groups/types' +import { WorkingGroup } from '@/working-groups/types' -export interface CreateOpeningButtonProps { - worker: Worker +type CreateOpeningButtonProps = { + name: string + group: WorkingGroup | undefined } -export const CreateOpeningButton = ({ worker }: CreateOpeningButtonProps) => { +export const CreateOpeningButton = ({ name, group }: CreateOpeningButtonProps) => { const { showModal } = useModal() - const createOpening = useCallback( - () => showModal({ modal: 'CreateOpening', data: { worker } }), - [worker] - ) + + 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 10181d25ae..964222b69e 100644 --- a/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx +++ b/packages/ui/src/working-groups/modals/CreateOpening/CreateOpening.tsx @@ -18,7 +18,6 @@ import { getSteps } from '@/common/model/machines/getSteps' import { useYupValidationResolver } from '@/common/utils/validation' import { machineStateConverter } from '@/council/modals/AnnounceCandidacy/helpers' import { StyledStepperBody } from '@/proposals/modals/AddNewProposal' -import { useWorker } from '@/working-groups/hooks/useWorker' import { SuccessModal, CreateOpeningSteps as Steps, ImportOpening } from './components' import { createOpeningMachine, CreateOpeningMachineState } from './machine' @@ -38,9 +37,8 @@ export const CreateOpeningModal = () => { const [state, send, service] = useMachine(createOpeningMachine) const { hideModal, modalData } = useModal() - const group = modalData.worker.group.id - const groupName = modalData.worker.group.name - const { worker } = useWorker(modalData.worker.id) + const { id: group, name: groupName } = modalData.group + const signer = modalData.leadAccount const context = useMemo( () => @@ -70,7 +68,7 @@ export const CreateOpeningModal = () => { }, [machineStateConverter(state.value)]) const { transaction, feeInfo } = useTransactionFee( - worker?.roleAccount, + signer, () => { if (api && group) { const { ...specifics } = form.getValues() as CreateOpeningForm @@ -78,7 +76,7 @@ export const CreateOpeningModal = () => { return api.tx[group].addOpening(description, 'Regular', stakePolicy, String(rewardPerBlock)) } }, - [api?.isConnected, worker?.roleAccount, group, form.formState.isValidating] + [api?.isConnected, signer, group, form.formState.isValidating] ) const exportedJsonValue = useMemo(() => { @@ -117,12 +115,12 @@ export const CreateOpeningModal = () => { return null } - if (state.matches('transaction') && transaction && worker) { + if (state.matches('transaction') && transaction) { return ( You intend to create an Opening. diff --git a/packages/ui/src/working-groups/modals/CreateOpening/types.tsx b/packages/ui/src/working-groups/modals/CreateOpening/types.tsx index 03ecd61aee..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, Worker } from '@/working-groups/types' +import { GroupIdName, WorkingGroup } from '@/working-groups/types' export interface OpeningModalData { - worker: Worker + 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 From 1932ef9b64efdbaee0f247f0ff95dc5e4b1f8a72 Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Wed, 14 Feb 2024 20:48:25 +0100 Subject: [PATCH 7/8] Fix tests --- .../WorkingGroup/WorkingGroup.stories.tsx | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) 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 e0f1fbf5c1..cde3e6f577 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.stories.tsx @@ -10,12 +10,12 @@ import { member } from '@/mocks/data/members' import { getButtonByText, getEditorByLabel, joy, withinModal } from '@/mocks/helpers' import { MocksParameters } from '@/mocks/providers' import { - GetWorkerDocument, GetWorkersDocument, GetWorkingGroupDocument, GetGroupDebtDocument, GetBudgetSpendingDocument, GetPastWorkersDocument, + GetRoleAccountsDocument, } from '@/working-groups/queries' import { WorkingGroupsModule } from '../WorkingGroupsModule' @@ -74,6 +74,7 @@ export default { router: { path: '/working-groups/:name', href: `/working-groups/${WG_DATA.name}` }, mocks: ({ args, parameters }: StoryContext): MocksParameters => { const alice = member('alice') + const charlie = member('charlie') const role = { __typename: 'Worker', @@ -84,14 +85,21 @@ export default { isLead: args.isLead, isActive: true, status: 'WorkerStatusActive', - roleAccount: alice.controllerAccount, - group: { - __typename: 'WorkingGroup', - ...WG_DATA, - }, + group: { __typename: 'WorkingGroup', ...WG_DATA }, } as const - const worker = { ...role, membership: alice } + 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, roles: [role] } } }, @@ -130,26 +138,15 @@ export default { }, { query: GetWorkersDocument, - data: { - workers: [ - worker, - { - id: `${WG_DATA.id}-1`, - group: { - id: WG_DATA.id, - name: WG_DATA.name, - }, - status: 'WorkerStatusActive', - membership: member('charlie'), - }, - ], - }, + data: { workers }, }, // Opening tab { - query: GetWorkerDocument, - data: { workerByUniqueInput: worker }, + query: GetRoleAccountsDocument, + data: { + workers: args.isLead ? [{ roleAccount: alice.controllerAccount }] : [], + }, }, { query: GetGroupDebtDocument, From df105f15d59add204f348b9caa07e96481028cbb Mon Sep 17 00:00:00 2001 From: Theophile Sandoz Date: Wed, 14 Feb 2024 20:49:23 +0100 Subject: [PATCH 8/8] Fix the workers sidebar logic --- .../WorkingGroups/WorkingGroup/AboutTab.tsx | 14 ------------- .../WorkingGroup/OpeningsTab.tsx | 14 ------------- .../WorkingGroup/WorkingGroup.tsx | 5 +++-- .../WorkingGroup/WorkingGroupOpenings.tsx | 5 +++-- .../components/WorkerListSidebar.tsx | 21 +++++++++++++++++++ 5 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 packages/ui/src/app/pages/WorkingGroups/WorkingGroup/components/WorkerListSidebar.tsx 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.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx index b18938b135..e0927a7e71 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroup.tsx @@ -7,7 +7,8 @@ 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 = () => { @@ -20,7 +21,7 @@ export const WorkingGroup = () => { } 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/WorkingGroupOpenings.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx index 42bdb27e3f..ac5ab79a47 100644 --- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx +++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroup/WorkingGroupOpenings.tsx @@ -6,8 +6,9 @@ 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 params = useParams<{ name: string }>() @@ -19,7 +20,7 @@ export function WorkingGroupOpenings() { } 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 ( + + + + ) +}