Instant Sale Create Flow
This commit is contained in:
parent
0b8feaed9a
commit
f684a0490c
|
@ -2,11 +2,6 @@ import {
|
||||||
findProgramAddress,
|
findProgramAddress,
|
||||||
getAuctionExtended,
|
getAuctionExtended,
|
||||||
programIds,
|
programIds,
|
||||||
VAULT_PREFIX,
|
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
|
||||||
findProgramAddress,
|
|
||||||
programIds,
|
|
||||||
StringPublicKey,
|
StringPublicKey,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
VAULT_PREFIX,
|
VAULT_PREFIX,
|
||||||
|
|
|
@ -36,7 +36,6 @@ import {
|
||||||
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
||||||
import { MintLayout } from '@solana/spl-token';
|
import { MintLayout } from '@solana/spl-token';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { capitalize } from 'lodash';
|
|
||||||
import { WinningConfigType, AmountRange } from '../../models/metaplex';
|
import { WinningConfigType, AmountRange } from '../../models/metaplex';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
|
@ -57,6 +56,7 @@ const { Step } = Steps;
|
||||||
const { ZERO } = constants;
|
const { ZERO } = constants;
|
||||||
|
|
||||||
export enum AuctionCategory {
|
export enum AuctionCategory {
|
||||||
|
InstantSale,
|
||||||
Limited,
|
Limited,
|
||||||
Single,
|
Single,
|
||||||
Open,
|
Open,
|
||||||
|
@ -98,7 +98,6 @@ export interface AuctionState {
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
category: AuctionCategory;
|
category: AuctionCategory;
|
||||||
saleType?: 'auction' | 'sale';
|
|
||||||
|
|
||||||
price?: number;
|
price?: number;
|
||||||
priceFloor?: number;
|
priceFloor?: number;
|
||||||
|
@ -133,19 +132,19 @@ export const AuctionCreateView = () => {
|
||||||
|
|
||||||
const [step, setStep] = useState<number>(0);
|
const [step, setStep] = useState<number>(0);
|
||||||
const [stepsVisible, setStepsVisible] = useState<boolean>(true);
|
const [stepsVisible, setStepsVisible] = useState<boolean>(true);
|
||||||
const [auctionObj, setAuctionObj] = useState<
|
const [auctionObj, setAuctionObj] =
|
||||||
| {
|
useState<
|
||||||
vault: StringPublicKey;
|
| {
|
||||||
auction: StringPublicKey;
|
vault: StringPublicKey;
|
||||||
auctionManager: StringPublicKey;
|
auction: StringPublicKey;
|
||||||
}
|
auctionManager: StringPublicKey;
|
||||||
| undefined
|
}
|
||||||
>(undefined);
|
| undefined
|
||||||
|
>(undefined);
|
||||||
const [attributes, setAttributes] = useState<AuctionState>({
|
const [attributes, setAttributes] = useState<AuctionState>({
|
||||||
reservationPrice: 0,
|
reservationPrice: 0,
|
||||||
items: [],
|
items: [],
|
||||||
category: AuctionCategory.Open,
|
category: AuctionCategory.Open,
|
||||||
saleType: 'auction',
|
|
||||||
auctionDurationType: 'minutes',
|
auctionDurationType: 'minutes',
|
||||||
gapTimeType: 'minutes',
|
gapTimeType: 'minutes',
|
||||||
winnersCount: 1,
|
winnersCount: 1,
|
||||||
|
@ -445,6 +444,14 @@ export const AuctionCreateView = () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const instantSaleStep = (
|
||||||
|
<InstantSaleStep
|
||||||
|
attributes={attributes}
|
||||||
|
setAttributes={setAttributes}
|
||||||
|
confirm={() => gotoNextStep()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
const copiesStep = (
|
const copiesStep = (
|
||||||
<CopiesStep
|
<CopiesStep
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
|
@ -461,16 +468,8 @@ export const AuctionCreateView = () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const typeStep = (
|
const priceAuction = (
|
||||||
<SaleTypeStep
|
<PriceAuction
|
||||||
attributes={attributes}
|
|
||||||
setAttributes={setAttributes}
|
|
||||||
confirm={() => gotoNextStep()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const priceStep = (
|
|
||||||
<PriceStep
|
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
setAttributes={setAttributes}
|
setAttributes={setAttributes}
|
||||||
confirm={() => gotoNextStep()}
|
confirm={() => gotoNextStep()}
|
||||||
|
@ -486,7 +485,7 @@ export const AuctionCreateView = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const endingStep = (
|
const endingStep = (
|
||||||
<EndingPhaseStep
|
<EndingPhaseAuction
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
setAttributes={setAttributes}
|
setAttributes={setAttributes}
|
||||||
confirm={() => gotoNextStep()}
|
confirm={() => gotoNextStep()}
|
||||||
|
@ -529,11 +528,17 @@ export const AuctionCreateView = () => {
|
||||||
const congratsStep = <Congrats auction={auctionObj} />;
|
const congratsStep = <Congrats auction={auctionObj} />;
|
||||||
|
|
||||||
const stepsByCategory = {
|
const stepsByCategory = {
|
||||||
|
[AuctionCategory.InstantSale]: [
|
||||||
|
['Category', categoryStep],
|
||||||
|
['Instant Sale', instantSaleStep],
|
||||||
|
['Review', reviewStep], // Update for clarity
|
||||||
|
['Publish', waitStep], // Just the loading screen
|
||||||
|
[undefined, congratsStep],
|
||||||
|
],
|
||||||
[AuctionCategory.Limited]: [
|
[AuctionCategory.Limited]: [
|
||||||
['Category', categoryStep],
|
['Category', categoryStep],
|
||||||
['Copies', copiesStep],
|
['Copies', copiesStep],
|
||||||
['Sale Type', typeStep],
|
['Price', priceAuction],
|
||||||
['Price', priceStep],
|
|
||||||
['Initial Phase', initialStep],
|
['Initial Phase', initialStep],
|
||||||
['Ending Phase', endingStep],
|
['Ending Phase', endingStep],
|
||||||
['Participation NFT', participationStep],
|
['Participation NFT', participationStep],
|
||||||
|
@ -544,7 +549,7 @@ export const AuctionCreateView = () => {
|
||||||
[AuctionCategory.Single]: [
|
[AuctionCategory.Single]: [
|
||||||
['Category', categoryStep],
|
['Category', categoryStep],
|
||||||
['Copies', copiesStep],
|
['Copies', copiesStep],
|
||||||
['Price', priceStep],
|
['Price', priceAuction],
|
||||||
['Initial Phase', initialStep],
|
['Initial Phase', initialStep],
|
||||||
['Ending Phase', endingStep],
|
['Ending Phase', endingStep],
|
||||||
['Participation NFT', participationStep],
|
['Participation NFT', participationStep],
|
||||||
|
@ -555,7 +560,7 @@ export const AuctionCreateView = () => {
|
||||||
[AuctionCategory.Open]: [
|
[AuctionCategory.Open]: [
|
||||||
['Category', categoryStep],
|
['Category', categoryStep],
|
||||||
['Copies', copiesStep],
|
['Copies', copiesStep],
|
||||||
['Price', priceStep],
|
['Price', priceAuction],
|
||||||
['Initial Phase', initialStep],
|
['Initial Phase', initialStep],
|
||||||
['Ending Phase', endingStep],
|
['Ending Phase', endingStep],
|
||||||
['Review', reviewStep],
|
['Review', reviewStep],
|
||||||
|
@ -566,7 +571,7 @@ export const AuctionCreateView = () => {
|
||||||
['Category', categoryStep],
|
['Category', categoryStep],
|
||||||
['Winners', winnersStep],
|
['Winners', winnersStep],
|
||||||
['Tiers', tierTableStep],
|
['Tiers', tierTableStep],
|
||||||
['Price', priceStep],
|
['Price', priceAuction],
|
||||||
['Initial Phase', initialStep],
|
['Initial Phase', initialStep],
|
||||||
['Ending Phase', endingStep],
|
['Ending Phase', endingStep],
|
||||||
['Participation NFT', participationStep],
|
['Participation NFT', participationStep],
|
||||||
|
@ -627,6 +632,20 @@ const CategoryStep = (props: {
|
||||||
</Row>
|
</Row>
|
||||||
<Row justify={width < 768 ? 'center' : 'start'}>
|
<Row justify={width < 768 ? 'center' : 'start'}>
|
||||||
<Col>
|
<Col>
|
||||||
|
<Row>
|
||||||
|
<Button
|
||||||
|
className="type-btn"
|
||||||
|
size="large"
|
||||||
|
onClick={() => props.confirm(AuctionCategory.InstantSale)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div>Instant Sale</div>
|
||||||
|
<div className="type-btn-description">
|
||||||
|
At a fixed price, sell a single Master NFT or copies of it
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Button
|
<Button
|
||||||
className="type-btn"
|
className="type-btn"
|
||||||
|
@ -691,6 +710,145 @@ const CategoryStep = (props: {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const InstantSaleStep = (props: {
|
||||||
|
attributes: AuctionState;
|
||||||
|
setAttributes: (attr: AuctionState) => void;
|
||||||
|
confirm: () => void;
|
||||||
|
}) => {
|
||||||
|
const [copiesChecked, setCopiesChecked] = useState(false);
|
||||||
|
const copiesEnabled = React.useMemo(
|
||||||
|
() => !!props.attributes?.items?.[0]?.masterEdition?.info?.maxSupply,
|
||||||
|
[props.attributes?.items?.[0]],
|
||||||
|
);
|
||||||
|
|
||||||
|
let artistFilter = (i: SafetyDepositDraft) =>
|
||||||
|
!(i.metadata.info.data.creators || []).find((c: Creator) => !c.verified);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row className="call-to-action" style={{ marginBottom: 0 }}>
|
||||||
|
<h2>Select which item to sell:</h2>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Row className="content-action">
|
||||||
|
<Col xl={24}>
|
||||||
|
<ArtSelector
|
||||||
|
filter={artistFilter}
|
||||||
|
selected={props.attributes.items}
|
||||||
|
setSelected={items => {
|
||||||
|
props.setAttributes({ ...props.attributes, items });
|
||||||
|
}}
|
||||||
|
allowMultiple={false}
|
||||||
|
>
|
||||||
|
Select NFT
|
||||||
|
</ArtSelector>
|
||||||
|
|
||||||
|
<label className="action-field">
|
||||||
|
<Checkbox
|
||||||
|
defaultChecked={false}
|
||||||
|
checked={copiesChecked}
|
||||||
|
disabled={!copiesEnabled}
|
||||||
|
onChange={e => setCopiesChecked(e.target.checked)}
|
||||||
|
>
|
||||||
|
<span className="field-title">
|
||||||
|
Create copies of a Master Edition NFT?
|
||||||
|
</span>
|
||||||
|
</Checkbox>
|
||||||
|
{copiesChecked && copiesEnabled && (
|
||||||
|
<>
|
||||||
|
<span className="field-info">
|
||||||
|
Each copy will be given unique edition number e.g. 1 of 30
|
||||||
|
</span>
|
||||||
|
<Input
|
||||||
|
autoFocus
|
||||||
|
className="input"
|
||||||
|
placeholder="Enter number of copies sold"
|
||||||
|
allowClear
|
||||||
|
onChange={info =>
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
editions: parseInt(info.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="action-field">
|
||||||
|
<span className="field-title">Price</span>
|
||||||
|
<span className="field-info">
|
||||||
|
This is the instant sale price for your item.
|
||||||
|
</span>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
autoFocus
|
||||||
|
className="input"
|
||||||
|
placeholder="Price"
|
||||||
|
prefix="◎"
|
||||||
|
suffix="SOL"
|
||||||
|
onChange={info =>
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
priceFloor: parseFloat(info.target.value),
|
||||||
|
instantSalePrice: parseFloat(info.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="action-field">
|
||||||
|
<span className="field-title">Sale Duration</span>
|
||||||
|
<span className="field-info">
|
||||||
|
This is how long the sale will last for.
|
||||||
|
</span>
|
||||||
|
<Input
|
||||||
|
addonAfter={
|
||||||
|
<Select
|
||||||
|
defaultValue={props.attributes.auctionDurationType}
|
||||||
|
onChange={value =>
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
auctionDurationType: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Option value="minutes">Minutes</Option>
|
||||||
|
<Option value="hours">Hours</Option>
|
||||||
|
<Option value="days">Days</Option>
|
||||||
|
</Select>
|
||||||
|
}
|
||||||
|
autoFocus
|
||||||
|
type="number"
|
||||||
|
className="input"
|
||||||
|
placeholder="Set the sale duration"
|
||||||
|
onChange={info =>
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
auctionDuration: parseInt(info.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={() => {
|
||||||
|
props.confirm();
|
||||||
|
}}
|
||||||
|
className="action-btn"
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const CopiesStep = (props: {
|
const CopiesStep = (props: {
|
||||||
attributes: AuctionState;
|
attributes: AuctionState;
|
||||||
setAttributes: (attr: AuctionState) => void;
|
setAttributes: (attr: AuctionState) => void;
|
||||||
|
@ -823,137 +981,6 @@ const NumberOfWinnersStep = (props: {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SaleTypeStep = (props: {
|
|
||||||
attributes: AuctionState;
|
|
||||||
setAttributes: (attr: AuctionState) => void;
|
|
||||||
confirm: () => void;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row className="call-to-action">
|
|
||||||
<h2>Sale Type</h2>
|
|
||||||
<p>Sell a limited copy or copies of a single Master NFT.</p>
|
|
||||||
</Row>
|
|
||||||
<Row className="content-action">
|
|
||||||
<Col className="section" xl={24}>
|
|
||||||
<label className="action-field">
|
|
||||||
<span className="field-title">
|
|
||||||
How do you want to sell your NFT(s)?
|
|
||||||
</span>
|
|
||||||
<Radio.Group
|
|
||||||
defaultValue={props.attributes.saleType}
|
|
||||||
onChange={info =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
saleType: info.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Radio className="radio-field" value="auction">
|
|
||||||
Auction
|
|
||||||
</Radio>
|
|
||||||
<div className="radio-subtitle">
|
|
||||||
Allow bidding on your NFT(s).
|
|
||||||
</div>
|
|
||||||
</Radio.Group>
|
|
||||||
<Radio.Group
|
|
||||||
defaultValue={props.attributes.saleType}
|
|
||||||
onChange={info =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
saleType: info.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Radio className="radio-field" value="auction">
|
|
||||||
Instant Sale
|
|
||||||
</Radio>
|
|
||||||
<div className="radio-subtitle">
|
|
||||||
Instant purchase and redemption of your NFT.
|
|
||||||
</div>
|
|
||||||
</Radio.Group>
|
|
||||||
</label>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
size="large"
|
|
||||||
onClick={props.confirm}
|
|
||||||
className="action-btn"
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
</Row>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PriceStep = (props: {
|
|
||||||
attributes: AuctionState;
|
|
||||||
setAttributes: (attr: AuctionState) => void;
|
|
||||||
confirm: () => void;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{props.attributes.saleType === 'auction' ? (
|
|
||||||
<PriceAuction {...props} />
|
|
||||||
) : (
|
|
||||||
<PriceSale {...props} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PriceSale = (props: {
|
|
||||||
attributes: AuctionState;
|
|
||||||
setAttributes: (attr: AuctionState) => void;
|
|
||||||
confirm: () => void;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row className="call-to-action">
|
|
||||||
<h2>Price</h2>
|
|
||||||
<p>Set the fixed price for your instant sale.</p>
|
|
||||||
</Row>
|
|
||||||
<Row className="content-action">
|
|
||||||
<label className="action-field">
|
|
||||||
<span className="field-title">Sale price</span>
|
|
||||||
<span className="field-info">
|
|
||||||
This is the price of purchasing the item(s).
|
|
||||||
</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
min={0}
|
|
||||||
autoFocus
|
|
||||||
className="input"
|
|
||||||
placeholder="Price"
|
|
||||||
prefix="◎"
|
|
||||||
suffix="SOL"
|
|
||||||
onChange={info =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
priceFloor: parseFloat(info.target.value),
|
|
||||||
instantSalePrice: parseFloat(info.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
size="large"
|
|
||||||
onClick={props.confirm}
|
|
||||||
className="action-btn"
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
</Row>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PriceAuction = (props: {
|
const PriceAuction = (props: {
|
||||||
attributes: AuctionState;
|
attributes: AuctionState;
|
||||||
setAttributes: (attr: AuctionState) => void;
|
setAttributes: (attr: AuctionState) => void;
|
||||||
|
@ -1103,13 +1130,13 @@ const InitialPhaseStep = (props: {
|
||||||
<>
|
<>
|
||||||
<Row className="call-to-action">
|
<Row className="call-to-action">
|
||||||
<h2>Initial Phase</h2>
|
<h2>Initial Phase</h2>
|
||||||
<p>Set the terms for your {props.attributes.saleType}.</p>
|
<p>Set the terms for your auction.</p>
|
||||||
</Row>
|
</Row>
|
||||||
<Row className="content-action">
|
<Row className="content-action">
|
||||||
<Col className="section" xl={24}>
|
<Col className="section" xl={24}>
|
||||||
<label className="action-field">
|
<label className="action-field">
|
||||||
<span className="field-title">
|
<span className="field-title">
|
||||||
When do you want the {props.attributes.saleType} to begin?
|
When do you want the auction to begin?
|
||||||
</span>
|
</span>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
defaultValue="now"
|
defaultValue="now"
|
||||||
|
@ -1134,9 +1161,7 @@ const InitialPhaseStep = (props: {
|
||||||
{!startNow && (
|
{!startNow && (
|
||||||
<>
|
<>
|
||||||
<label className="action-field">
|
<label className="action-field">
|
||||||
<span className="field-title">
|
<span className="field-title">Auction Start Date</span>
|
||||||
{capitalize(props.attributes.saleType)} Start Date
|
|
||||||
</span>
|
|
||||||
{saleMoment && (
|
{saleMoment && (
|
||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
momentObj={saleMoment}
|
momentObj={saleMoment}
|
||||||
|
@ -1215,22 +1240,6 @@ const InitialPhaseStep = (props: {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const EndingPhaseStep = (props: {
|
|
||||||
attributes: AuctionState;
|
|
||||||
setAttributes: (attr: AuctionState) => void;
|
|
||||||
confirm: () => void;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{props.attributes.saleType === 'auction' ? (
|
|
||||||
<EndingPhaseAuction {...props} />
|
|
||||||
) : (
|
|
||||||
<EndingPhaseSale {...props} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const EndingPhaseAuction = (props: {
|
const EndingPhaseAuction = (props: {
|
||||||
attributes: AuctionState;
|
attributes: AuctionState;
|
||||||
setAttributes: (attr: AuctionState) => void;
|
setAttributes: (attr: AuctionState) => void;
|
||||||
|
@ -1245,10 +1254,7 @@ const EndingPhaseAuction = (props: {
|
||||||
<Row className="content-action">
|
<Row className="content-action">
|
||||||
<Col className="section" xl={24}>
|
<Col className="section" xl={24}>
|
||||||
<div className="action-field">
|
<div className="action-field">
|
||||||
<span className="field-title">
|
<span className="field-title">Auction Duration</span>
|
||||||
{props.attributes.saleType == 'auction' ? 'Auction' : 'Sale'}{' '}
|
|
||||||
Duration
|
|
||||||
</span>
|
|
||||||
<span className="field-info">
|
<span className="field-info">
|
||||||
This is how long the auction will last for.
|
This is how long the auction will last for.
|
||||||
</span>
|
</span>
|
||||||
|
@ -1281,153 +1287,61 @@ const EndingPhaseAuction = (props: {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{props.attributes.saleType == 'auction' && (
|
<div className="action-field">
|
||||||
<>
|
<span className="field-title">Gap Time</span>
|
||||||
<div className="action-field">
|
<span className="field-info">
|
||||||
<span className="field-title">Gap Time</span>
|
The final phase of the auction will begin when there is this much
|
||||||
<span className="field-info">
|
time left on the countdown. Any bids placed during the final phase
|
||||||
The final phase of the auction will begin when there is this
|
will extend the end time by this same duration.
|
||||||
much time left on the countdown. Any bids placed during the
|
|
||||||
final phase will extend the end time by this same duration.
|
|
||||||
</span>
|
|
||||||
<Input
|
|
||||||
addonAfter={
|
|
||||||
<Select
|
|
||||||
defaultValue={props.attributes.gapTimeType}
|
|
||||||
onChange={value =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
gapTimeType: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Option value="minutes">Minutes</Option>
|
|
||||||
<Option value="hours">Hours</Option>
|
|
||||||
<Option value="days">Days</Option>
|
|
||||||
</Select>
|
|
||||||
}
|
|
||||||
type="number"
|
|
||||||
className="input"
|
|
||||||
placeholder="Set the gap time"
|
|
||||||
onChange={info =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
gapTime: parseInt(info.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label className="action-field">
|
|
||||||
<span className="field-title">Tick Size for Ending Phase</span>
|
|
||||||
<span className="field-info">
|
|
||||||
In order for winners to move up in the auction, they must
|
|
||||||
place a bid that’s at least this percentage higher than the
|
|
||||||
next highest bid.
|
|
||||||
</span>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
className="input"
|
|
||||||
placeholder="Percentage"
|
|
||||||
suffix="%"
|
|
||||||
onChange={info =>
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
tickSizeEndingPhase: parseInt(info.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
size="large"
|
|
||||||
onClick={props.confirm}
|
|
||||||
className="action-btn"
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
</Row>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const EndingPhaseSale = (props: {
|
|
||||||
attributes: AuctionState;
|
|
||||||
setAttributes: (attr: AuctionState) => void;
|
|
||||||
confirm: () => void;
|
|
||||||
}) => {
|
|
||||||
const startMoment = props.attributes.startSaleTS
|
|
||||||
? moment.unix(props.attributes.startSaleTS)
|
|
||||||
: moment();
|
|
||||||
const [untilSold, setUntilSold] = useState<boolean>(true);
|
|
||||||
const [endMoment, setEndMoment] = useState<moment.Moment | undefined>(
|
|
||||||
props.attributes.endTS ? moment.unix(props.attributes.endTS) : undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
props.setAttributes({
|
|
||||||
...props.attributes,
|
|
||||||
endTS: endMoment && endMoment.unix(),
|
|
||||||
});
|
|
||||||
}, [endMoment]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (untilSold) setEndMoment(undefined);
|
|
||||||
else setEndMoment(startMoment);
|
|
||||||
}, [untilSold]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row className="call-to-action">
|
|
||||||
<h2>Ending Phase</h2>
|
|
||||||
<p>Set the terms for your sale.</p>
|
|
||||||
</Row>
|
|
||||||
<Row className="content-action">
|
|
||||||
<Col className="section" xl={24}>
|
|
||||||
<label className="action-field">
|
|
||||||
<span className="field-title">
|
|
||||||
When do you want the sale to end?
|
|
||||||
</span>
|
</span>
|
||||||
<Radio.Group
|
<Input
|
||||||
defaultValue="now"
|
addonAfter={
|
||||||
onChange={info => setUntilSold(info.target.value === 'now')}
|
<Select
|
||||||
>
|
defaultValue={props.attributes.gapTimeType}
|
||||||
<Radio className="radio-field" value="now">
|
onChange={value =>
|
||||||
Until sold
|
props.setAttributes({
|
||||||
</Radio>
|
...props.attributes,
|
||||||
<div className="radio-subtitle">
|
gapTimeType: value,
|
||||||
The sale will end once the supply goes to zero.
|
})
|
||||||
</div>
|
}
|
||||||
<Radio className="radio-field" value="later">
|
>
|
||||||
At a specified date
|
<Option value="minutes">Minutes</Option>
|
||||||
</Radio>
|
<Option value="hours">Hours</Option>
|
||||||
<div className="radio-subtitle">
|
<Option value="days">Days</Option>
|
||||||
The sale will end at this date, regardless if there is remaining
|
</Select>
|
||||||
supply.
|
}
|
||||||
</div>
|
type="number"
|
||||||
</Radio.Group>
|
className="input"
|
||||||
</label>
|
placeholder="Set the gap time"
|
||||||
|
onChange={info =>
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
gapTime: parseInt(info.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{!untilSold && (
|
<label className="action-field">
|
||||||
<label className="action-field">
|
<span className="field-title">Tick Size for Ending Phase</span>
|
||||||
<span className="field-title">End Date</span>
|
<span className="field-info">
|
||||||
{endMoment && (
|
In order for winners to move up in the auction, they must place a
|
||||||
<DateTimePicker
|
bid that’s at least this percentage higher than the next highest
|
||||||
momentObj={endMoment}
|
bid.
|
||||||
setMomentObj={setEndMoment}
|
</span>
|
||||||
datePickerProps={{
|
<Input
|
||||||
disabledDate: (current: moment.Moment) =>
|
type="number"
|
||||||
current && current < startMoment,
|
className="input"
|
||||||
}}
|
placeholder="Percentage"
|
||||||
/>
|
suffix="%"
|
||||||
)}
|
onChange={info =>
|
||||||
</label>
|
props.setAttributes({
|
||||||
)}
|
...props.attributes,
|
||||||
|
tickSizeEndingPhase: parseInt(info.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -1892,6 +1806,7 @@ const ReviewStep = (props: {
|
||||||
startListTS: props.attributes.startListTS || moment().unix(),
|
startListTS: props.attributes.startListTS || moment().unix(),
|
||||||
startSaleTS: props.attributes.startSaleTS || moment().unix(),
|
startSaleTS: props.attributes.startSaleTS || moment().unix(),
|
||||||
});
|
});
|
||||||
|
console.log('attributes', props.attributes);
|
||||||
props.confirm();
|
props.confirm();
|
||||||
}}
|
}}
|
||||||
className="action-btn"
|
className="action-btn"
|
||||||
|
|
Loading…
Reference in New Issue