mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
Initial layout for buzz marketplace
This commit is contained in:
@@ -155,3 +155,7 @@ NOW_PAYMENTS_IPN_KEY=key
|
||||
COINBASE_API_URL=http://localhost
|
||||
COINBASE_API_KEY=key
|
||||
COINBASE_WEBHOOK_SECRET=secret
|
||||
|
||||
EMERCHANTPAY_WPF_URL=
|
||||
EMERCHANTPAY_USERNAME=
|
||||
EMERCHANTPAY_PASSWORD=
|
||||
|
||||
Generated
+8715
-6349
File diff suppressed because it is too large
Load Diff
+1
-4
@@ -72,7 +72,7 @@
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@essentials/one-key-map": "^1.2.0",
|
||||
"@google-cloud/recaptcha-enterprise": "^5.1.1",
|
||||
"@headlessui/react": "2.2",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@mantine/core": "^7.17.7",
|
||||
"@mantine/dates": "^7.17.7",
|
||||
@@ -283,8 +283,5 @@
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "6.2.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@react-aria/interactions": "3.16.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Anchor, Button, Card, Paper, Text, Title } from '@mantine/core';
|
||||
import { z } from 'zod';
|
||||
import { useMarketplaceContext } from '~/components/Marketplace/MarketplaceProvider';
|
||||
import { Form, InputNumber, InputSelect, useForm } from '~/libs/form';
|
||||
import { MarketplacePaymentMethod } from '~/server/common/enums';
|
||||
import { formatCurrencyForDisplay } from '~/utils/number-helpers';
|
||||
|
||||
const minBuzzAmount = 1000; // Minimum amount of Buzz to list for sale
|
||||
const availablePaymentMethods = Object.values(MarketplacePaymentMethod);
|
||||
|
||||
const schema = z.object({
|
||||
amount: z.number().min(minBuzzAmount, 'Minimum amount is 1000 Buzz'),
|
||||
paymentMethod: z.nativeEnum(MarketplacePaymentMethod),
|
||||
currency: z.enum(['USD', 'EUR', 'GBP']),
|
||||
});
|
||||
|
||||
export function BuyBuzz() {
|
||||
const { currency } = useMarketplaceContext();
|
||||
const form = useForm({ schema });
|
||||
|
||||
const handleSubmit = (data: z.input<typeof schema>) => {
|
||||
console.log(data, currency);
|
||||
};
|
||||
|
||||
const [amount = 0] = form.watch(['amount']);
|
||||
// const totalValue = (amount / 10) * price; // Total value in the selected currency
|
||||
// const totalPrice = formatCurrencyForDisplay(totalValue, currency, { decimals: true });
|
||||
|
||||
return (
|
||||
<Card className="flex flex-col" radius="md" withBorder>
|
||||
<Title order={2} className="mb-2 text-lg font-semibold">
|
||||
Buy Buzz
|
||||
</Title>
|
||||
<Form className="flex flex-col gap-4" form={form} onSubmit={handleSubmit}>
|
||||
<InputNumber
|
||||
name="amount"
|
||||
label="Amount to purchase"
|
||||
step={minBuzzAmount}
|
||||
min={minBuzzAmount}
|
||||
/>
|
||||
<InputSelect name="paymentMethod" label="Payment Method" data={availablePaymentMethods} />
|
||||
<Paper className="flex h-12 items-center justify-center" withBorder>
|
||||
Best match goes here
|
||||
</Paper>
|
||||
<Button type="submit" disabled={!amount} fullWidth>
|
||||
Proceed to Purchase
|
||||
</Button>
|
||||
<Text size="xs" c="dimmed">
|
||||
Payments handled via{' '}
|
||||
<Anchor href="https://zkp2p.xyz" target="_blank" rel="nofollow noreferrer">
|
||||
ZKP2P
|
||||
</Anchor>{' '}
|
||||
secure escrow. Learn More
|
||||
</Text>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Button, Card, Text, Title } from '@mantine/core';
|
||||
import { z } from 'zod';
|
||||
import { useMarketplaceContext } from '~/components/Marketplace/MarketplaceProvider';
|
||||
import { Form, InputNumber, useForm } from '~/libs/form';
|
||||
import { formatCurrencyForDisplay } from '~/utils/number-helpers';
|
||||
|
||||
const minBuzzAmount = 1000; // Minimum amount of Buzz to list for sale
|
||||
|
||||
const schema = z.object({
|
||||
amount: z.number().min(minBuzzAmount, 'Minimum amount is 1000 Buzz'),
|
||||
price: z.number().min(0.01, 'Price must be greater than 0'),
|
||||
currency: z.enum(['USD', 'EUR', 'GBP']),
|
||||
});
|
||||
|
||||
export function ListBuzzSale() {
|
||||
const { currency } = useMarketplaceContext();
|
||||
const form = useForm({ schema });
|
||||
|
||||
const handleSubmit = (data: z.input<typeof schema>) => {
|
||||
console.log(data);
|
||||
};
|
||||
|
||||
const [amount = 0, price = 0] = form.watch(['amount', 'price']);
|
||||
const totalValue = (amount / 10) * price; // Total value in the selected currency
|
||||
const totalPrice = formatCurrencyForDisplay(totalValue, currency, { decimals: true });
|
||||
|
||||
return (
|
||||
<Card className="flex flex-col" aria-label="List Buzz for Sale" radius="md" withBorder>
|
||||
<Title order={2} className="mb-2 text-lg font-semibold">
|
||||
List Buzz for Sale
|
||||
</Title>
|
||||
<Form className="flex flex-col gap-4" form={form} onSubmit={handleSubmit}>
|
||||
<InputNumber name="amount" label="Amount" step={minBuzzAmount} min={minBuzzAmount} />
|
||||
<InputNumber name="price" label="Price per thousand" step={0.01} min={0.01} />
|
||||
<Text size="sm">
|
||||
Total value:{' '}
|
||||
<Text fw="bold" span>
|
||||
${totalPrice}
|
||||
</Text>
|
||||
</Text>
|
||||
<Button type="submit" disabled={!totalValue} fullWidth>
|
||||
Post Listing - ${totalPrice}
|
||||
</Button>
|
||||
<Text size="xs" c="dimmed">
|
||||
Buzz is deducted immediately. Canceling listings costs 100 Buzz
|
||||
</Text>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Card, Stack, Group, Button, Title, Select, Paper, Text, Table } from '@mantine/core';
|
||||
import { useMarketplaceContext } from '~/components/Marketplace/MarketplaceProvider';
|
||||
import { useCurrentUser } from '~/hooks/useCurrentUser';
|
||||
import { Currency } from '~/shared/utils/prisma/enums';
|
||||
|
||||
const currencies = Object.values(Currency);
|
||||
|
||||
export function MarketplaceOverview() {
|
||||
const { currency, setCurrency } = useMarketplaceContext();
|
||||
const currentUser = useCurrentUser();
|
||||
|
||||
return (
|
||||
<Card className="flex flex-col gap-5" radius="md" withBorder>
|
||||
<Group gap="xs" justify="space-between" align="center">
|
||||
<Stack gap={0}>
|
||||
<Title>Market Overview</Title>
|
||||
<Text>Buy Red Buzz privately through secure escrow</Text>
|
||||
</Stack>
|
||||
<Group gap="sm">
|
||||
<Select
|
||||
w={100}
|
||||
data={currencies}
|
||||
defaultValue={currency}
|
||||
onChange={(value) => setCurrency((value as Currency | null) ?? Currency.USD)}
|
||||
/>
|
||||
{currentUser ? <Button variant="default">Seller Settings</Button> : null}
|
||||
</Group>
|
||||
</Group>
|
||||
<Paper radius="md">
|
||||
<div className="flex h-96 items-center justify-center">
|
||||
<Title order={3} c="dimmed">
|
||||
Coming Soon! Marketplace overview is under development.
|
||||
</Title>
|
||||
</div>
|
||||
</Paper>
|
||||
<Stack>
|
||||
<Title order={2}>Current Listings</Title>
|
||||
<Table>
|
||||
<Table.Tbody>
|
||||
<Table.Tr>
|
||||
<Table.Td>$0.85 per 1K</Table.Td>
|
||||
<Table.Td>1,000,000 Buzz</Table.Td>
|
||||
</Table.Tr>
|
||||
<Table.Tr>
|
||||
<Table.Td>$0.85 per 1K</Table.Td>
|
||||
<Table.Td>1,000,000 Buzz</Table.Td>
|
||||
</Table.Tr>
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
import { Currency } from '~/shared/utils/prisma/enums';
|
||||
|
||||
type ProviderState = {
|
||||
currency: Currency;
|
||||
setCurrency: (currency: Currency) => void;
|
||||
};
|
||||
|
||||
const MarketplaceContext = createContext<ProviderState | undefined>(undefined);
|
||||
export const useMarketplaceContext = () => {
|
||||
const context = useContext(MarketplaceContext);
|
||||
if (!context) {
|
||||
throw new Error('useMarketplaceContext must be used within a MarketPlaceProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export function MarketplaceProvider({ children }: { children: React.ReactNode }) {
|
||||
const [currency, setCurrency] = useState<Currency>(Currency.USD);
|
||||
|
||||
return (
|
||||
<MarketplaceContext.Provider value={{ currency, setCurrency }}>
|
||||
{children}
|
||||
</MarketplaceContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Card, Tabs, Text, Title } from '@mantine/core';
|
||||
|
||||
export function SellerListings() {
|
||||
return (
|
||||
<Card className="flex flex-col" aria-label="Your Listings" radius="md" withBorder>
|
||||
<Title order={2} className="mb-2 text-lg font-semibold">
|
||||
Your Listings
|
||||
</Title>
|
||||
<Tabs>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="open">Open</Tabs.Tab>
|
||||
<Tabs.Tab value="closed">Closed</Tabs.Tab>
|
||||
<Tabs.Tab value="canceled">Canceled</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="open">
|
||||
<Text>View and manage your active Buzz listings.</Text>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="closed">
|
||||
<Text>View your completed listings.</Text>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="canceled">
|
||||
<Text>View your canceled listings.</Text>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Card, Container, Stack, Text, Title } from '@mantine/core';
|
||||
import { BuyBuzz } from '~/components/Marketplace/BuyBuzz';
|
||||
import { ListBuzzSale } from '~/components/Marketplace/ListBuzzSale';
|
||||
import { MarketplaceOverview } from '~/components/Marketplace/MarketplaceOverview';
|
||||
import { MarketplaceProvider } from '~/components/Marketplace/MarketplaceProvider';
|
||||
import { SellerListings } from '~/components/Marketplace/SellerListings';
|
||||
import { Meta } from '~/components/Meta/Meta';
|
||||
import { env } from '~/env/client';
|
||||
import { createServerSideProps } from '~/server/utils/server-side-helpers';
|
||||
|
||||
export const getServerSideProps = createServerSideProps({
|
||||
resolver: async () => {
|
||||
// Do something server-side if needed
|
||||
return { props: {} };
|
||||
},
|
||||
});
|
||||
|
||||
export default function MarketplacePage() {
|
||||
return (
|
||||
<MarketplaceProvider>
|
||||
<Meta
|
||||
title="Buzz Marketplace | Civitai"
|
||||
description="Explore the latest marketplace trends and insights on Civitai."
|
||||
links={
|
||||
env.NEXT_PUBLIC_BASE_URL
|
||||
? [{ rel: 'canonical', href: `${env.NEXT_PUBLIC_BASE_URL}/buzz/marketplace` }]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<Container size="xl" py="lg">
|
||||
<Stack gap="xl">
|
||||
<MarketplaceOverview />
|
||||
<div
|
||||
className="grid grid-cols-1 gap-6 md:grid-cols-3"
|
||||
role="region"
|
||||
aria-label="Marketplace actions"
|
||||
>
|
||||
<ListBuzzSale />
|
||||
<BuyBuzz />
|
||||
<SellerListings />
|
||||
</div>
|
||||
</Stack>
|
||||
</Container>
|
||||
</MarketplaceProvider>
|
||||
);
|
||||
}
|
||||
@@ -393,3 +393,11 @@ export enum ModReviewType {
|
||||
Appeals = 'appeal',
|
||||
RuleViolations = 'modRule',
|
||||
}
|
||||
|
||||
export enum MarketplacePaymentMethod {
|
||||
CashApp = 'CashApp',
|
||||
Revolut = 'Revolut',
|
||||
Venmo = 'Venmo',
|
||||
Wise = 'Wise',
|
||||
Zelle = 'Zelle',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user