refactor: remove dropdown component and use combobox instead (#242)
This commit is contained in:
parent
fd4b7c953c
commit
9f48213e28
|
|
@ -35,7 +35,7 @@
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^18.15.11",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.0.4",
|
||||||
"web3": "^1.9.0"
|
"web3": "^1.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,16 @@ const querystring = require('querystring');
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { nfaContract } from '@libs/nfa-contract';
|
import { nfaContract } from '@libs/nfa-contract';
|
||||||
|
|
||||||
export const submitBuildInfo = async (event: APIGatewayEvent): Promise<APIGatewayProxyResult> => {
|
export const submitBuildInfo = async (
|
||||||
|
event: APIGatewayEvent
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
try {
|
try {
|
||||||
const eventData = querystring.parse(event.body);
|
const eventData = querystring.parse(event.body);
|
||||||
const id = v4();
|
const id = v4();
|
||||||
const buildInfo = {
|
const buildInfo = {
|
||||||
buildId: id,
|
buildId: id,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
submittedData: eventData
|
submittedData: eventData,
|
||||||
};
|
};
|
||||||
|
|
||||||
// place holder call
|
// place holder call
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,44 @@
|
||||||
import {
|
import {
|
||||||
APIGatewayProxyResult,
|
APIGatewayProxyResult,
|
||||||
APIGatewayEvent,
|
APIGatewayEvent,
|
||||||
///APIGatewayEventRequestContext,
|
///APIGatewayEventRequestContext,
|
||||||
} from 'aws-lambda';
|
} from 'aws-lambda';
|
||||||
import { formatJSONResponse } from '@libs/api-gateway';
|
import { formatJSONResponse } from '@libs/api-gateway';
|
||||||
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
export const submitMintInfo = async (
|
export const submitMintInfo = async (
|
||||||
event: APIGatewayEvent,
|
event: APIGatewayEvent
|
||||||
///context: APIGatewayEventRequestContext
|
///context: APIGatewayEventRequestContext
|
||||||
): Promise<APIGatewayProxyResult> => {
|
): Promise<APIGatewayProxyResult> => {
|
||||||
try {
|
try {
|
||||||
const id = v4();
|
const id = v4();
|
||||||
|
|
||||||
/**if (!verifyAlchemySig(event.headers.xalchemywork)) {
|
/**if (!verifyAlchemySig(event.headers.xalchemywork)) {
|
||||||
throw new Error('Invalid sig');
|
throw new Error('Invalid sig');
|
||||||
}**/
|
}**/
|
||||||
|
|
||||||
if (event.body == undefined) {
|
if (event.body == undefined) {
|
||||||
throw new Error('Undefined data');
|
throw new Error('Undefined data');
|
||||||
}
|
|
||||||
|
|
||||||
const mintInfo = {
|
|
||||||
buildId: id,
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
body: JSON.parse(event.body),
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if we have it in mongo
|
|
||||||
// if so, trigger verification call
|
|
||||||
// if not, add to mongo
|
|
||||||
|
|
||||||
return formatJSONResponse({
|
|
||||||
mintInfo,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return formatJSONResponse({
|
|
||||||
status: 500,
|
|
||||||
message: e,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
const mintInfo = {
|
||||||
|
buildId: id,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
body: JSON.parse(event.body),
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if we have it in mongo
|
||||||
|
// if so, trigger verification call
|
||||||
|
// if not, add to mongo
|
||||||
|
|
||||||
|
return formatJSONResponse({
|
||||||
|
mintInfo,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return formatJSONResponse({
|
||||||
|
status: 500,
|
||||||
|
message: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
var Web3 = require('web3');
|
var Web3 = require('web3');
|
||||||
|
|
||||||
var web3 = new Web3(Web3.givenProvider || "ws://localhost:17895");
|
var web3 = new Web3(Web3.givenProvider || 'ws://localhost:17895');
|
||||||
|
|
||||||
export const logDecoder = (eventFieldsABI: { indexed: boolean; internalType: string; name: string; type: string; }[], data: string, topics: string[]) => {
|
export const logDecoder = (
|
||||||
return web3.eth.abi.decodeLog(eventFieldsABI, data, topics);
|
eventFieldsABI: {
|
||||||
|
indexed: boolean;
|
||||||
|
internalType: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}[],
|
||||||
|
data: string,
|
||||||
|
topics: string[]
|
||||||
|
) => {
|
||||||
|
return web3.eth.abi.decodeLog(eventFieldsABI, data, topics);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import { Listbox, Transition } from '@headlessui/react';
|
|
||||||
import { Fragment } from 'react';
|
|
||||||
|
|
||||||
import { Flex } from '@/components';
|
|
||||||
import { Icon } from '@/components/core/icon';
|
|
||||||
|
|
||||||
type DropdownOptionProps = {
|
|
||||||
option: DropdownItem;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DropdownOption: React.FC<DropdownOptionProps> = ({
|
|
||||||
option,
|
|
||||||
}: DropdownOptionProps) => (
|
|
||||||
<Listbox.Option
|
|
||||||
className={({ active }) =>
|
|
||||||
`relative cursor-default select-none py-2 px-3.5 text-slate11 rounded-xl mb-2 text-sm max-w-full ${
|
|
||||||
active ? 'bg-slate5 text-slate12' : 'bg-transparent'
|
|
||||||
}`
|
|
||||||
}
|
|
||||||
value={option}
|
|
||||||
>
|
|
||||||
{({ selected, active }) => (
|
|
||||||
<Flex css={{ justifyContent: 'space-between' }}>
|
|
||||||
<span
|
|
||||||
className={`${
|
|
||||||
active ? 'text-slate12' : 'text-slate11'
|
|
||||||
} max-w-full break-words pr-5`}
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</span>
|
|
||||||
{selected && (
|
|
||||||
<Icon
|
|
||||||
name="check"
|
|
||||||
color="white"
|
|
||||||
css={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '$0',
|
|
||||||
bottom: '$0',
|
|
||||||
right: '$0',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
pr: '$4',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Listbox.Option>
|
|
||||||
);
|
|
||||||
|
|
||||||
type DropdownButtonProps = {
|
|
||||||
/**
|
|
||||||
* The selected value of the dropdown.
|
|
||||||
*/
|
|
||||||
selectedValue: DropdownItem | undefined;
|
|
||||||
/**
|
|
||||||
* If it's true, the list of options will be displayed
|
|
||||||
*/
|
|
||||||
open: boolean;
|
|
||||||
/**
|
|
||||||
* Background color of the dropdown. Should be on tailwind palette.
|
|
||||||
*/
|
|
||||||
backgroundColor?: string;
|
|
||||||
/**
|
|
||||||
* Text color of the dropdown. Should be on tailwind palette.
|
|
||||||
*/
|
|
||||||
textColor?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DropdownButton: React.FC<DropdownButtonProps> = ({
|
|
||||||
selectedValue,
|
|
||||||
backgroundColor,
|
|
||||||
textColor,
|
|
||||||
}: DropdownButtonProps) => {
|
|
||||||
const textColorCss = textColor ? `text-${textColor}` : 'text-slate12';
|
|
||||||
const borderColor = backgroundColor
|
|
||||||
? `border-${backgroundColor}`
|
|
||||||
: 'border-slate7';
|
|
||||||
const backgroundColorClass = backgroundColor
|
|
||||||
? `bg-${backgroundColor}`
|
|
||||||
: 'bg-transparent';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Listbox.Button
|
|
||||||
className={`relative w-full cursor-default ${borderColor} border-solid border rounded-xl py-3 pl-3.5 pr-10 h-11 text-left focus:outline-none sm:text-sm
|
|
||||||
${backgroundColorClass}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`block truncate ${
|
|
||||||
selectedValue && selectedValue.label
|
|
||||||
? `${textColorCss}`
|
|
||||||
: 'text-slate11'
|
|
||||||
} break-words`}
|
|
||||||
>
|
|
||||||
{selectedValue && selectedValue.label ? selectedValue.label : 'Select'}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`pointer-events-none absolute top-1 bottom-0 right-0 flex items-center pr-4 ${textColorCss}`}
|
|
||||||
>
|
|
||||||
<Icon name="chevron-down" />
|
|
||||||
</span>
|
|
||||||
</Listbox.Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DropdownItem = {
|
|
||||||
/**
|
|
||||||
* The key of the item.
|
|
||||||
*/
|
|
||||||
value: string;
|
|
||||||
/**
|
|
||||||
* The label to display of the item.
|
|
||||||
*/
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DropdownProps = {
|
|
||||||
/**
|
|
||||||
* List of items to be displayed in the dropdown.
|
|
||||||
*/
|
|
||||||
items: DropdownItem[];
|
|
||||||
/**
|
|
||||||
* The selected value of the dropdown.
|
|
||||||
*/
|
|
||||||
selectedValue: DropdownItem | undefined;
|
|
||||||
/**
|
|
||||||
* Callback when the selected value changes.
|
|
||||||
*/
|
|
||||||
onChange(option: DropdownItem): void;
|
|
||||||
/**
|
|
||||||
* Background color of the dropdown. Should be on tailwind palette. https://tailwindcss.com/docs/background-color
|
|
||||||
*/
|
|
||||||
backgroundColor?: string;
|
|
||||||
/**
|
|
||||||
* Text color of the dropdown. Should be on tailwind palette. https://tailwindcss.com/docs/text-color
|
|
||||||
*/
|
|
||||||
textColor?: string;
|
|
||||||
/**
|
|
||||||
* Width of the options list. Should be on tailwind width. https://tailwindcss.com/docs/width
|
|
||||||
*/
|
|
||||||
optionsWidth?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Dropdown: React.FC<DropdownProps> = ({
|
|
||||||
items,
|
|
||||||
selectedValue,
|
|
||||||
onChange,
|
|
||||||
backgroundColor,
|
|
||||||
textColor,
|
|
||||||
optionsWidth,
|
|
||||||
}: DropdownProps) => {
|
|
||||||
const handleDropdownChange = (option: DropdownItem): void => {
|
|
||||||
onChange(option);
|
|
||||||
};
|
|
||||||
const width = optionsWidth ? `w-${optionsWidth}` : 'w-full';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Listbox value={selectedValue} by="value" onChange={handleDropdownChange}>
|
|
||||||
{({ open }) => (
|
|
||||||
<div className="relative max-w-full">
|
|
||||||
<DropdownButton
|
|
||||||
selectedValue={selectedValue}
|
|
||||||
open={open}
|
|
||||||
backgroundColor={backgroundColor}
|
|
||||||
textColor={textColor}
|
|
||||||
/>
|
|
||||||
<Transition
|
|
||||||
as={Fragment}
|
|
||||||
leave="transition ease-in duration-100"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<Listbox.Options
|
|
||||||
className={`absolute mt-1 max-h-36 ${width} right-0 z-10 overflow-auto rounded-xl bg-black px-3 pt-2 border-solid border-slate6 border text-base focus:outline-none sm:text-sm`}
|
|
||||||
>
|
|
||||||
{items.map((option: DropdownItem) => (
|
|
||||||
<DropdownOption key={option.value} option={option} />
|
|
||||||
))}
|
|
||||||
</Listbox.Options>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Listbox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export * from './dropdown';
|
|
||||||
export * from './combobox';
|
export * from './combobox';
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,23 @@ import { Combobox, Flex, Icon, IconName } from '@/components';
|
||||||
|
|
||||||
type Item = { id: number; label: string; icon: IconName };
|
type Item = { id: number; label: string; icon: IconName };
|
||||||
|
|
||||||
|
type ItemDropdown = { id: number; label: string };
|
||||||
|
|
||||||
const Items: Item[] = [
|
const Items: Item[] = [
|
||||||
{ id: 1, label: 'Option 1', icon: 'branch' },
|
{ id: 1, label: 'Option 1', icon: 'branch' },
|
||||||
{ id: 2, label: 'Option 2', icon: 'ethereum' },
|
{ id: 2, label: 'Option 2', icon: 'ethereum' },
|
||||||
{ id: 3, label: 'Option 3', icon: 'metamask' },
|
{ id: 3, label: 'Option 3', icon: 'metamask' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ItemsDropdown: ItemDropdown[] = [
|
||||||
|
{ id: 1, label: 'Option 1' },
|
||||||
|
{ id: 2, label: 'Option 2' },
|
||||||
|
{ id: 3, label: 'Option 3' },
|
||||||
|
];
|
||||||
|
|
||||||
export const ComboboxTest: React.FC = () => {
|
export const ComboboxTest: React.FC = () => {
|
||||||
const selected = useState<Item>();
|
const selected = useState<Item>();
|
||||||
|
const selectedDropdown = useState<ItemDropdown>(ItemsDropdown[0]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
|
@ -24,25 +33,19 @@ export const ComboboxTest: React.FC = () => {
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Combobox unattached items={Items} selected={selected} queryKey="label">
|
<Combobox
|
||||||
|
unattached
|
||||||
|
items={ItemsDropdown}
|
||||||
|
selected={selectedDropdown}
|
||||||
|
queryKey="label"
|
||||||
|
>
|
||||||
{({ Field, Options }) => (
|
{({ Field, Options }) => (
|
||||||
<>
|
<>
|
||||||
<Field>
|
<Field css={{ backgroundColor: '$slate4', borderColor: '$slate4' }}>
|
||||||
{(selected) => (
|
{(selected) => <>{selected?.label || 'Select an option'}</>}
|
||||||
<>
|
|
||||||
<Icon name={selected?.icon || 'search'} />
|
|
||||||
{selected?.label || 'Select an option'}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Options>
|
<Options disableSearch>{(item) => <>{item.label}</>}</Options>
|
||||||
{(item) => (
|
|
||||||
<>
|
|
||||||
<Icon name={item.icon} /> {item.label}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Options>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Combobox>
|
</Combobox>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import {
|
import { Combobox, InputGroup, InputGroupText } from '@/components';
|
||||||
Dropdown,
|
|
||||||
DropdownItem,
|
|
||||||
InputGroup,
|
|
||||||
InputGroupText,
|
|
||||||
} from '@/components';
|
|
||||||
import { useDebounce } from '@/hooks';
|
import { useDebounce } from '@/hooks';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
import { Explore } from '../explore.context';
|
import { Explore } from '../explore.context';
|
||||||
import { NFASearchFragmentStyles as S } from './nfa-search.styles';
|
import { NFASearchFragmentStyles as S } from './nfa-search.styles';
|
||||||
|
|
||||||
const orderResults: DropdownItem[] = [
|
type SortItem = {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const orderResults: SortItem[] = [
|
||||||
{ value: 'newest', label: 'Newest' },
|
{ value: 'newest', label: 'Newest' },
|
||||||
{ value: 'oldest', label: 'Oldest' },
|
{ value: 'oldest', label: 'Oldest' },
|
||||||
{ value: 'a-z', label: 'Sort A-Z' },
|
{ value: 'a-z', label: 'Sort A-Z' },
|
||||||
|
|
@ -26,34 +27,36 @@ export const NFASearchFragment: React.FC = () => {
|
||||||
setSearch,
|
setSearch,
|
||||||
setPageNumber,
|
setPageNumber,
|
||||||
} = Explore.useContext();
|
} = Explore.useContext();
|
||||||
const [selectedValue, setSelectedValue] = useState<DropdownItem>(
|
const [selectedValue, setSelectedValue] = useState<SortItem>(orderResults[0]);
|
||||||
orderResults[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSortChange = (item: DropdownItem): void => {
|
const handleSortChange = (item: SortItem | undefined): void => {
|
||||||
setSelectedValue(item);
|
if (item) {
|
||||||
setPageNumber(0);
|
setSelectedValue(item);
|
||||||
setEndReached(false);
|
setPageNumber(0);
|
||||||
|
setEndReached(false);
|
||||||
|
|
||||||
switch (item.value) {
|
switch (item.value) {
|
||||||
case 'newest':
|
case 'newest':
|
||||||
setOrderBy('tokenId');
|
setOrderBy('tokenId');
|
||||||
setOrderDirection('desc');
|
setOrderDirection('desc');
|
||||||
break;
|
break;
|
||||||
case 'oldest':
|
case 'oldest':
|
||||||
setOrderBy('tokenId');
|
setOrderBy('tokenId');
|
||||||
setOrderDirection('asc');
|
setOrderDirection('asc');
|
||||||
break;
|
break;
|
||||||
case 'a-z':
|
case 'a-z':
|
||||||
setOrderBy('name');
|
setOrderBy('name');
|
||||||
setOrderDirection('asc');
|
setOrderDirection('asc');
|
||||||
break;
|
break;
|
||||||
case 'z-a':
|
case 'z-a':
|
||||||
setOrderBy('name');
|
setOrderBy('name');
|
||||||
setOrderDirection('desc');
|
setOrderDirection('desc');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AppLog.errorToast('Error selecting sort option. Try again');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -78,15 +81,29 @@ export const NFASearchFragment: React.FC = () => {
|
||||||
<S.Input.Icon name="search" />
|
<S.Input.Icon name="search" />
|
||||||
<InputGroupText placeholder="Search" onChange={handleSearchChange} />
|
<InputGroupText placeholder="Search" onChange={handleSearchChange} />
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
<Combobox
|
||||||
<Dropdown
|
|
||||||
items={orderResults}
|
items={orderResults}
|
||||||
selectedValue={selectedValue}
|
selected={[selectedValue, handleSortChange]}
|
||||||
onChange={handleSortChange}
|
css={{ minWidth: '$28' }}
|
||||||
backgroundColor="slate4"
|
queryKey="label"
|
||||||
textColor="slate11"
|
>
|
||||||
optionsWidth="40"
|
{({ Field, Options }) => (
|
||||||
/>
|
<>
|
||||||
|
<Field
|
||||||
|
css={{
|
||||||
|
backgroundColor: '$slate4',
|
||||||
|
borderColor: '$slate4',
|
||||||
|
color: '$slate11',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(selected) => selected?.label || 'Select'}
|
||||||
|
</Field>
|
||||||
|
<Options disableSearch css={{ minWidth: '$44', left: 'unset' }}>
|
||||||
|
{(item) => item.label}
|
||||||
|
</Options>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Combobox>
|
||||||
</S.Input.Wrapper>
|
</S.Input.Wrapper>
|
||||||
</S.Container>
|
</S.Container>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ export const NFASearchFragmentStyles = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: '30rem',
|
maxWidth: '30rem',
|
||||||
justifySelf: 'center',
|
justifySelf: 'center',
|
||||||
|
|
||||||
button: {
|
|
||||||
minWidth: '$28',
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
Icon: styled(Icon, {
|
Icon: styled(Icon, {
|
||||||
fontSize: '$lg',
|
fontSize: '$lg',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue