Skip to content

Commit cf849b7

Browse files
committed
feat(#1444): align profile purchase UI with standard forms
Clean up Buy Space Tokens profile UI by removing debug details, reusing standard recipient sections, and placing the Space Token purchase action above Buy HYPHA in profile assets. Made-with: Cursor
1 parent 6223b8b commit cf849b7

2 files changed

Lines changed: 64 additions & 103 deletions

File tree

packages/epics/src/people/components/people-buy-space-tokens.tsx

Lines changed: 60 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ import {
3131
} from '@hypha-platform/core/client';
3232
import { useScrollToErrors } from '../../hooks';
3333
import { useDbTokens } from '../../hooks/use-db-tokens';
34+
import { useDbSpaces } from '../../hooks';
3435
import { formatCurrencyValue } from '@hypha-platform/ui-utils';
3536
import { Loader2 } from 'lucide-react';
3637
import { formatUnits } from 'viem';
38+
import { RecipientField } from '../../agreements/plugins/components/common/recipient-field';
39+
import { useEffect } from 'react';
3740

3841
type PurchasableToken = {
3942
id: number;
@@ -55,6 +58,8 @@ const buySpaceTokensSchema = z.object({
5558
.refine((v) => parseFloat(v) > 0, {
5659
message: 'Amount must be greater than 0',
5760
}),
61+
buyerAddress: z.string().optional(),
62+
paymentRecipient: z.string().optional(),
5863
});
5964

6065
type FormValues = z.infer<typeof buySpaceTokensSchema>;
@@ -170,6 +175,8 @@ export const PeopleBuySpaceTokens = ({
170175
defaultValues: {
171176
tokenAddress: '',
172177
amount: '',
178+
buyerAddress: person?.address ?? '',
179+
paymentRecipient: '',
173180
},
174181
});
175182

@@ -229,6 +236,35 @@ export const PeopleBuySpaceTokens = ({
229236
formatUnits(sale.salePricePerToken, sale.paymentTokenDecimals),
230237
);
231238
}, [sale]);
239+
const { spaces } = useDbSpaces({
240+
parentOnly: false,
241+
});
242+
const buyerMembers = useMemo(
243+
() => (person?.address ? [person] : []),
244+
[person],
245+
);
246+
const recipientSpaces = useMemo(
247+
() =>
248+
spaces?.filter(
249+
(space) =>
250+
sale?.executor &&
251+
space.address?.toLowerCase() === sale.executor.toLowerCase(),
252+
) ?? [],
253+
[sale?.executor, spaces],
254+
);
255+
256+
useEffect(() => {
257+
if (buyerAddress || person?.address) {
258+
form.setValue('buyerAddress', buyerAddress ?? person?.address ?? '', {
259+
shouldDirty: false,
260+
});
261+
}
262+
if (sale?.executor) {
263+
form.setValue('paymentRecipient', sale.executor, {
264+
shouldDirty: false,
265+
});
266+
}
267+
}, [buyerAddress, person?.address, sale?.executor, form]);
232268

233269
const handleApprove = async () => {
234270
form.clearErrors('root');
@@ -386,7 +422,7 @@ export const PeopleBuySpaceTokens = ({
386422
</div>
387423
</div>
388424

389-
{selectedToken && !isNaN(parsedAmount) && parsedAmount > 0 && (
425+
{selectedToken && !isNaN(parsedAmount) && parsedAmount > 0 && sale && (
390426
<div className="text-sm text-neutral-11">
391427
<div>
392428
Price:{' '}
@@ -405,68 +441,6 @@ export const PeopleBuySpaceTokens = ({
405441
<div>
406442
Remaining in sale: <strong>{remainingForSale}</strong>
407443
</div>
408-
<div>
409-
Buyer eligibility:{' '}
410-
<strong>
411-
{sale?.canPurchase ? 'Eligible' : 'Not eligible for this sale'}
412-
</strong>
413-
</div>
414-
<div>
415-
Purchase mode:{' '}
416-
<strong>{sale?.purchaseEligibilityMode ?? '-'}</strong>
417-
</div>
418-
<div>
419-
Allowance:{' '}
420-
<strong>
421-
{sale
422-
? `${formatCurrencyValue(
423-
Number(
424-
formatUnits(sale.allowance, sale.paymentTokenDecimals),
425-
),
426-
)} ${paymentTokenMeta?.symbol ?? ''}`
427-
: '-'}
428-
</strong>
429-
</div>
430-
<div>
431-
Balance:{' '}
432-
<strong>
433-
{sale
434-
? `${formatCurrencyValue(
435-
Number(
436-
formatUnits(sale.balance, sale.paymentTokenDecimals),
437-
),
438-
)} ${paymentTokenMeta?.symbol ?? ''}`
439-
: '-'}
440-
</strong>
441-
</div>
442-
<div>
443-
Payment token treasury recipient:{' '}
444-
<strong className="break-all">{sale?.executor ?? '-'}</strong>
445-
</div>
446-
<div>
447-
Wallet used for tx:{' '}
448-
<strong className="break-all">
449-
{buyerAddress ?? person?.address ?? '-'}
450-
</strong>
451-
</div>
452-
{needsApproval && (
453-
<div>
454-
<strong>Approval required before buying.</strong>
455-
</div>
456-
)}
457-
{!hasEnoughBalance && (
458-
<div>
459-
<strong>Insufficient payment token balance.</strong>
460-
</div>
461-
)}
462-
{(approveError || buyError) && (
463-
<div>
464-
<strong>
465-
{(approveError || buyError)?.message ??
466-
'Transaction failed. Please retry.'}
467-
</strong>
468-
</div>
469-
)}
470444
</div>
471445
)}
472446

@@ -494,45 +468,26 @@ export const PeopleBuySpaceTokens = ({
494468

495469
<Separator />
496470

497-
{/* Token sent to (buyer) */}
498-
<div className="flex flex-col gap-2">
499-
<span className="text-2 text-neutral-11">Token sent to</span>
500-
<div className="flex items-center gap-2 p-2 rounded-md bg-neutral-2 border border-neutral-6">
501-
<span className="text-2 text-neutral-11 font-medium truncate">
502-
{person?.name
503-
? `${person.name}${person.surname ? ` ${person.surname}` : ''}`
504-
: person?.address ?? '—'}
505-
</span>
506-
{(buyerAddress || person?.address) && (
507-
<span className="text-1 text-neutral-10 truncate">
508-
{buyerAddress ?? person?.address}
509-
</span>
510-
)}
511-
</div>
512-
</div>
471+
<RecipientField
472+
label={`${selectedToken?.symbol ?? 'Token'} sent to`}
473+
members={buyerMembers}
474+
defaultRecipientType="member"
475+
readOnly={true}
476+
showTabs={false}
477+
name="buyerAddress"
478+
/>
513479

514480
<Separator />
515481

516-
{/* Payment sent to (space treasury) */}
517-
<div className="flex flex-col gap-2">
518-
<span className="text-2 text-neutral-11">Payment sent to</span>
519-
<div className="flex items-center gap-2 p-2 rounded-md bg-neutral-2 border border-neutral-6">
520-
{selectedToken ? (
521-
<div className="flex flex-col">
522-
<span className="text-2 text-neutral-11 font-medium">
523-
{selectedToken.name} Space Treasury
524-
</span>
525-
<span className="text-1 text-neutral-10 break-all">
526-
{sale?.executor ?? 'Loading...'}
527-
</span>
528-
</div>
529-
) : (
530-
<span className="text-2 text-neutral-10 italic">
531-
Select a token to see the treasury address
532-
</span>
533-
)}
534-
</div>
535-
</div>
482+
<RecipientField
483+
label={`${paymentTokenMeta?.symbol ?? 'Payment'} paid to`}
484+
members={[]}
485+
spaces={recipientSpaces}
486+
defaultRecipientType="space"
487+
readOnly={true}
488+
showTabs={false}
489+
name="paymentRecipient"
490+
/>
536491

537492
<Separator />
538493

@@ -594,6 +549,12 @@ export const PeopleBuySpaceTokens = ({
594549
{form.formState.errors.root.message}
595550
</div>
596551
)}
552+
{(approveError || buyError) && (
553+
<div className="text-2 text-foreground">
554+
{(approveError || buyError)?.message ??
555+
'Transaction failed. Please retry.'}
556+
</div>
557+
)}
597558
</form>
598559
</Form>
599560
);

packages/epics/src/treasury/components/assets/user-assets-section.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,17 @@ export const UserAssetsSection: FC<UserAssetsSectionProps> = ({
5858
<div className="flex gap-2 justify-end">
5959
<Link
6060
className={!isMyProfile ? 'cursor-not-allowed' : ''}
61-
href={isMyProfile ? `${basePath}/actions/purchase-hypha-tokens` : {}}
61+
href={isMyProfile ? `${basePath}/actions/buy-space-tokens` : {}}
6262
scroll={false}
6363
>
64-
<Button disabled={!isMyProfile}>{tProfile('buyHypha')}</Button>
64+
<Button disabled={!isMyProfile}>Buy Space tokens</Button>
6565
</Link>
6666
<Link
6767
className={!isMyProfile ? 'cursor-not-allowed' : ''}
68-
href={isMyProfile ? `${basePath}/actions/buy-space-tokens` : {}}
68+
href={isMyProfile ? `${basePath}/actions/purchase-hypha-tokens` : {}}
6969
scroll={false}
7070
>
71-
<Button disabled={!isMyProfile}>Buy Space tokens</Button>
71+
<Button disabled={!isMyProfile}>{tProfile('buyHypha')}</Button>
7272
</Link>
7373
<Link href={isMyProfile ? `${basePath}/actions` : {}} scroll={false}>
7474
<Button disabled={!isMyProfile}>{tProfile('actions')}</Button>

0 commit comments

Comments
 (0)