mirror of
https://github.com/penxio/penx.git
synced 2026-05-12 03:03:12 -04:00
feat: can add contributor
This commit is contained in:
@@ -17,7 +17,9 @@ export function AddContributorDialog() {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={(v) => setIsOpen(v)}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={() => setIsOpen(true)}>Add Contributor</Button>
|
||||
<Button className="rounded-xl" onClick={() => setIsOpen(true)}>
|
||||
Add Contributor
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import LoadingDots from '@/components/icons/loading-dots'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -12,13 +13,13 @@ import {
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { channelsAtom } from '@/hooks/useChannels'
|
||||
import { useContributors } from '@/hooks/useContributors'
|
||||
import { useSpace } from '@/hooks/useSpace'
|
||||
import { spaceAbi } from '@/lib/abi'
|
||||
import { checkChain } from '@/lib/checkChain'
|
||||
import { extractErrorMessage } from '@/lib/extractErrorMessage'
|
||||
import { api, trpc } from '@/lib/trpc'
|
||||
import { wagmiConfig } from '@/lib/wagmi'
|
||||
import { store } from '@/store'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { readContract, writeContract } from '@wagmi/core'
|
||||
import { toast } from 'sonner'
|
||||
@@ -33,53 +34,53 @@ const FormSchema = z.object({
|
||||
.string()
|
||||
.min(1, { message: 'Address is required' })
|
||||
.regex(ethAddressRegex, { message: 'Invalid Ethereum address' }),
|
||||
share: z
|
||||
.string()
|
||||
.min(1, { message: 'Share cannot be empty' })
|
||||
.regex(/^\d+$/, { message: 'Share must be a numeric string' }),
|
||||
})
|
||||
|
||||
export function AddContributorForm() {
|
||||
const [isLoading, setLoading] = useState(false)
|
||||
const { isPending, mutateAsync } = trpc.contributor.create.useMutation()
|
||||
const { setIsOpen } = useAddContributorDialog()
|
||||
const { space } = useSpace()
|
||||
const { contributors = [], refetch } = useContributors()
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
address: '',
|
||||
share: '',
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
try {
|
||||
setLoading(true)
|
||||
await checkChain()
|
||||
const some = contributors.find((c) => c.user.address === data.address)
|
||||
|
||||
if (some) {
|
||||
setLoading(false)
|
||||
return toast.error('This address has already been added.')
|
||||
}
|
||||
|
||||
await writeContract(wagmiConfig, {
|
||||
address: space.spaceAddress as Address,
|
||||
abi: spaceAbi,
|
||||
functionName: 'addContributor',
|
||||
args: [data.address as Address],
|
||||
})
|
||||
|
||||
await mutateAsync({
|
||||
spaceId: space.id,
|
||||
address: data.address,
|
||||
share: data.share,
|
||||
})
|
||||
refetch()
|
||||
|
||||
// await writeContract(wagmiConfig, {
|
||||
// address: space.spaceAddress as Address,
|
||||
// abi: spaceAbi,
|
||||
// functionName: 'addContributor',
|
||||
// args: [
|
||||
// {
|
||||
// account: data.address as Address,
|
||||
// shares: BigInt(data.share),
|
||||
// },
|
||||
// ],
|
||||
// })
|
||||
|
||||
// const channels = await api.channel.listBySpaceId.query(space.id)
|
||||
// store.set(channelsAtom, channels)
|
||||
setIsOpen(false)
|
||||
toast.success('Add Contributor successfully!')
|
||||
} catch (error) {
|
||||
const msg = extractErrorMessage(error)
|
||||
toast.error(msg || 'Failed to add Contributor. Please try again.')
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -99,22 +100,12 @@ export function AddContributorForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="share"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Share</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="" {...field} className="w-full" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="submit" className="w-full">
|
||||
{isPending ? <LoadingDots color="#808080" /> : <p>Add</p>}
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={isLoading || !form.formState.isValid}
|
||||
>
|
||||
{isLoading ? <LoadingDots color="#808080" /> : <p>Add</p>}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { UserAvatar } from '@/components/UserAvatar'
|
||||
import { useContributors } from '@/hooks/useContributors'
|
||||
import { useSpace } from '@/hooks/useSpace'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { cn, shortenAddress } from '@/lib/utils'
|
||||
|
||||
export function ContributorList() {
|
||||
const { space } = useSpace()
|
||||
const { data = [], isLoading } = trpc.contributor.listBySpaceId.useQuery(
|
||||
space.id,
|
||||
)
|
||||
const { contributors = [], isLoading } = useContributors()
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="grid gap-4 mx-auto w-[800x] md:w-[800px] sm:w-full mt-6">
|
||||
<div className="grid gap-4 mt-6">
|
||||
{Array(5)
|
||||
.fill('')
|
||||
.map((_, i) => (
|
||||
@@ -24,7 +24,7 @@ export function ContributorList() {
|
||||
)
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
if (!contributors.length) {
|
||||
return (
|
||||
<div className="grid gap-4 mx-auto sm:w-full mt-6 text-neutral-400">
|
||||
No contributors yet.
|
||||
@@ -34,7 +34,7 @@ export function ContributorList() {
|
||||
|
||||
return (
|
||||
<div className="space-y-3 mt-8">
|
||||
{data.map((item) => (
|
||||
{contributors.map((item) => (
|
||||
<div key={item.id} className="flex justify-between">
|
||||
<div className="flex gap-2 items-center">
|
||||
<UserAvatar user={item.user} />
|
||||
@@ -44,9 +44,13 @@ export function ContributorList() {
|
||||
? item.user.ensName
|
||||
: shortenAddress(item.user.address)}
|
||||
</div>
|
||||
{space.userId === item.user.id && <Badge>Founder</Badge>}
|
||||
{space.userId !== item.user.id && (
|
||||
<Badge variant="outline">Shareholder</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-bold">{item.shares}</span> share
|
||||
<span className="font-bold">{item.shares}</span> shares
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
11
hooks/useContributors.ts
Normal file
11
hooks/useContributors.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { useSpace } from './useSpace'
|
||||
|
||||
export function useContributors() {
|
||||
const { space } = useSpace()
|
||||
const { data: contributors = [], ...rest } =
|
||||
trpc.contributor.listBySpaceId.useQuery(space.id, {
|
||||
enabled: !!space?.id,
|
||||
})
|
||||
return { contributors, ...rest }
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export const contributorRouter = router({
|
||||
.query(async ({ input }) => {
|
||||
return [] as (Contributor & {
|
||||
user: {
|
||||
id: string
|
||||
name: string | null
|
||||
ensName: string | null
|
||||
email: string | null
|
||||
@@ -25,7 +26,6 @@ export const contributorRouter = router({
|
||||
z.object({
|
||||
spaceId: z.string(),
|
||||
address: z.string(),
|
||||
share: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
|
||||
Reference in New Issue
Block a user