Skip to content

Commit

Permalink
Merge pull request #1674 from DA0-DA0/development
Browse files Browse the repository at this point in the history
Deploy manage member action cleanup
  • Loading branch information
NoahSaso authored Jan 27, 2024
2 parents 637d648 + 1ce0aa5 commit 494338d
Show file tree
Hide file tree
Showing 24 changed files with 409 additions and 343 deletions.
2 changes: 1 addition & 1 deletion apps/dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"next-i18next": "^11.0.0",
"postcss": "^8.4.5",
"postcss-loader": "^7.0.1",
"tailwindcss": "^3.0.7",
"tailwindcss": "^3.4.1",
"typescript": "5.3.3"
},
"prettier": "@dao-dao/config/prettier",
Expand Down
2 changes: 1 addition & 1 deletion apps/sda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"next-i18next": "^11.0.0",
"postcss": "^8.4.5",
"postcss-loader": "^7.0.1",
"tailwindcss": "^3.0.7",
"tailwindcss": "^3.4.1",
"typescript": "5.3.3"
},
"prettier": "@dao-dao/config/prettier",
Expand Down
3 changes: 1 addition & 2 deletions packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"version": "2.2.0",
"license": "BSD-3-Clause-Clear",
"dependencies": {
"@tailwindcss/line-clamp": "^0.3.1",
"@tailwindcss/typography": "^0.5.1",
"@typescript-eslint/eslint-plugin": "6.14.0",
"@typescript-eslint/parser": "6.14.0",
Expand All @@ -21,7 +20,7 @@
"postcss": "^8.4.16",
"postcss-loader": "^7.0.1",
"prettier": "^2.6.2",
"tailwindcss": "^3.0.7",
"tailwindcss": "^3.4.1",
"tailwindcss-safe-area": "^0.4.1",
"typescript": "5.3.3"
},
Expand Down
1 change: 0 additions & 1 deletion packages/config/tailwind/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const tailwindConfig = {
safelist: [/data-theme$/],
},
plugins: [
require('@tailwindcss/line-clamp'),
require('./button'),
require('@tailwindcss/typography'),
require('tailwindcss-safe-area'),
Expand Down
5 changes: 2 additions & 3 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -609,10 +609,9 @@
"limits": "Limits",
"maxCharacters": "Max of {{max}} characters",
"membersAddress": "Member's address",
"membersToAddOrUpdate": "Members to add/update",
"membersToAddOrUpdateDescription": "Set the voting power of each address individually. DAO DAO will calculate the voting weight percentage for you.",
"membersToAddOrUpdate": "Members to add or update",
"membersToAddOrUpdateDescription": "Set the voting power of each member individually. DAO DAO will calculate the voting weight percentage for you.",
"membersToRemove": "Members to remove",
"membersToRemoveDescription": "These addresses will be removed from the DAO.",
"message": "Message",
"messageType": "Message type",
"migrateDescription": "This will <1>migrate</1> the selected contract to a new code ID.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'
import { AddressInput } from '@dao-dao/stateless'
import { makeReactHookFormDecorator } from '@dao-dao/storybook'

import {
ManageMembersComponent,
ManageMembersData,
} from './ManageMembersComponent'
import { EntityDisplay } from '../../../../../components'
import { ManageMembersComponent, ManageMembersData } from './Component'

export default {
title:
Expand Down Expand Up @@ -36,7 +34,11 @@ Default.args = {
isCreating: true,
errors: {},
options: {
currentMembers: ['member1', 'member2'],
currentMembers: {
loading: false,
data: ['member1', 'member2'],
},
AddressInput,
EntityDisplay,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import {
Add,
ArrowRightAltRounded,
Close,
SubdirectoryArrowRightRounded,
} from '@mui/icons-material'
import clsx from 'clsx'
import { ComponentType } from 'react'
import { useFieldArray, useFormContext } from 'react-hook-form'
import { useTranslation } from 'react-i18next'

import {
Button,
Checkbox,
IconButton,
InputErrorMessage,
InputLabel,
Loader,
NumberInput,
useDetectWrap,
} from '@dao-dao/stateless'
import {
ActionComponent,
AddressInputProps,
LoadingData,
StatefulEntityDisplayProps,
} from '@dao-dao/types'
import { Member } from '@dao-dao/types/contracts/Cw4Group'
import {
makeValidateAddress,
validateNonNegative,
validateRequired,
} from '@dao-dao/utils'

import { useActionOptions } from '../../../../../actions'

export interface ManageMembersData {
toAdd: Member[]
toRemove: string[]
}

export interface ManageMembersOptions {
currentMembers: LoadingData<string[]>
// Used to show the profiles of the members being updated.
AddressInput: ComponentType<AddressInputProps<any>>
EntityDisplay: ComponentType<StatefulEntityDisplayProps>
}

export const ManageMembersComponent: ActionComponent<
ManageMembersOptions,
ManageMembersData
> = ({
fieldNamePrefix,
errors,
isCreating,
options: { currentMembers, AddressInput, EntityDisplay },
}) => {
const { t } = useTranslation()
const {
chain: { bech32_prefix: bech32Prefix },
} = useActionOptions()
const { register, setValue, watch, control } =
useFormContext<ManageMembersData>()

const toRemove = watch((fieldNamePrefix + 'toRemove') as 'toRemove')

const {
fields: toAddFields,
append: toAddAppend,
remove: toAddRemove,
} = useFieldArray({
control,
name: (fieldNamePrefix + 'toAdd') as 'toAdd',
})

const { containerRef, childRef, wrapped } = useDetectWrap()
const Icon = wrapped ? SubdirectoryArrowRightRounded : ArrowRightAltRounded

return (
<>
{(isCreating || toAddFields.length > 0) && (
<div className="flex flex-col gap-1">
<InputLabel
name={t('form.membersToAddOrUpdate')}
tooltip={t('form.membersToAddOrUpdateDescription')}
/>

<div className="flex flex-col items-stretch gap-1">
{toAddFields.map(({ id }, index) => {
const addrFieldName = (fieldNamePrefix +
`toAdd.${index}.addr`) as `toAdd.${number}.addr`
const weightFieldName = (fieldNamePrefix +
`toAdd.${index}.weight`) as `toAdd.${number}.weight`

return (
<div
key={id}
className="flex flex-row items-center gap-3 rounded-md bg-background-tertiary p-3"
>
<div
className="flex grow flex-row flex-wrap items-stretch gap-x-3 gap-y-2"
ref={containerRef}
>
<div className="flex flex-col gap-1">
<InputLabel name={t('form.votingWeightPlaceholder')} />
<NumberInput
disabled={!isCreating}
error={errors?.toAdd?.[index]?.weight}
fieldName={weightFieldName}
min={0}
placeholder={t('form.votingWeightPlaceholder')}
register={register}
setValue={setValue}
sizing="fill"
validation={[validateRequired, validateNonNegative]}
watch={watch}
/>
<InputErrorMessage
error={errors?.toAdd?.[index]?.weight}
/>
</div>

<div
className="flex grow flex-row items-stretch justify-center gap-2 sm:gap-3"
ref={childRef}
>
<div
className={clsx(
'flex flex-row items-center pt-4',
wrapped && 'pl-1'
)}
>
<Icon className="!h-6 !w-6 text-text-secondary" />
</div>

<div className="flex grow flex-col gap-1">
<InputLabel name={t('form.address')} />
<AddressInput
containerClassName="h-full"
disabled={!isCreating}
error={errors?.toAdd?.[index]?.addr}
fieldName={addrFieldName}
register={register}
validation={[
validateRequired,
makeValidateAddress(bech32Prefix),
(value) =>
toRemove.every((addr) => addr !== value) ||
t('error.invalidDuplicateFound'),
]}
/>
<InputErrorMessage
error={errors?.toAdd?.[index]?.addr}
/>
</div>
</div>
</div>

{isCreating && (
<IconButton
Icon={Close}
onClick={() => toAddRemove(index)}
size="sm"
variant="ghost"
/>
)}
</div>
)
})}
{isCreating && (
<Button
className="self-start"
onClick={() => toAddAppend({ weight: NaN, addr: '' })}
variant="secondary"
>
<Add className="!h-5 !w-5" />
{t('button.add')}
</Button>
)}
</div>
</div>
)}

{(isCreating || toRemove.length > 0) && (
<div className="flex flex-col gap-2">
<InputLabel name={t('form.membersToRemove')} />

<div
className={clsx(
'flex flex-col items-start',
isCreating ? 'gap-1' : 'gap-2'
)}
>
{isCreating ? (
currentMembers.loading ? (
<Loader fill={false} size={24} />
) : (
currentMembers.data.map((addr) => (
<Button
key={addr}
className="!p-3"
contentContainerClassName="!gap-2.5"
onClick={() =>
setValue(
(fieldNamePrefix + 'toRemove') as 'toRemove',
toRemove.includes(addr)
? toRemove.filter((a) => a !== addr)
: [...toRemove, addr]
)
}
variant="secondary"
>
<Checkbox checked={toRemove.includes(addr)} size="sm" />
<EntityDisplay address={addr} noCopy noLink />
</Button>
))
)
) : (
toRemove.map((addr) => (
<EntityDisplay key={addr} address={addr} />
))
)}
</div>
</div>
)}
</>
)
}
Loading

0 comments on commit 494338d

Please sign in to comment.