Skip to content

Commit

Permalink
Merge pull request #42 from alleslabs/refactor/contract-list
Browse files Browse the repository at this point in the history
Refactor/contract list
  • Loading branch information
poomthiti committed Dec 29, 2022
2 parents ff119d9 + a63db12 commit d1673dd
Show file tree
Hide file tree
Showing 23 changed files with 291 additions and 279 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug fixes

- [#42](https://github.com/alleslabs/celatone-frontend/pull/42) Properly show CTAs on contract-list page and edit zero/disconnected state
- [#45](https://github.com/alleslabs/celatone-frontend/pull/45) Add chain ID and code details to contract detail data loader
1 change: 0 additions & 1 deletion src/lib/components/forms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ export * from "./TagSelection";
export * from "./TextInput";
export * from "./SelectInput";
export * from "./TextAreaInput";
export * from "./types";
export * from "./ControllerInput";
export * from "./ControllerTextarea";
4 changes: 0 additions & 4 deletions src/lib/components/forms/types.ts

This file was deleted.

14 changes: 8 additions & 6 deletions src/lib/components/modal/contract/RemoveContract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ import { truncate } from "lib/utils";

interface ModalProps {
contractInfo: ContractInfo;
list: Option;
contractRemovalInfo: Option;
menuItemProps: MenuItemProps;
}

export function RemoveContract({
contractInfo,
list,
contractRemovalInfo,
menuItemProps,
}: ModalProps) {
const displayName = contractInfo.name
? contractInfo.name
: truncate(contractInfo.contractAddress);

const handleRemove = useHandleContractSave({
title: `Removed ${displayName} from ${list.label}`,
title: `Removed ${displayName} from ${contractRemovalInfo.label}`,
contractAddress: contractInfo.contractAddress,
instantiator: contractInfo.instantiator,
label: contractInfo.label,
created: contractInfo.created,
lists: contractInfo.lists?.filter((item) => item.value !== list.value),
lists: contractInfo.lists?.filter(
(item) => item.value !== contractRemovalInfo.value
),
});

return (
Expand All @@ -44,10 +46,10 @@ export function RemoveContract({
>
<Text>
<Highlight
query={[displayName, list.label]}
query={[displayName, contractRemovalInfo.label]}
styles={{ fontWeight: "bold", color: "inherit" }}
>
{`This action will remove ${displayName} from ${list.label}.
{`This action will remove ${displayName} from ${contractRemovalInfo.label}.
The contract’s off-chain detail will be preserved in other lists or when you save this contract again.`}
</Highlight>
</Text>
Expand Down
52 changes: 27 additions & 25 deletions src/lib/components/modal/select-contract/AllContractLists.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,59 @@
import { Box, SimpleGrid } from "@chakra-ui/react";
import type { Dispatch, SetStateAction } from "react";
import { useMemo, useState } from "react";

import { TextInput } from "lib/components/forms";
import { EmptyState } from "lib/components/state/EmptyState";
import type { ContractListInfo } from "lib/stores/contract";

import { ListCard } from "./ListCard";
import { ContractListCard } from "./ContractListCard";

interface AllContractListsProps {
search: string;
setSearch: Dispatch<SetStateAction<string>>;
contractLists: ContractListInfo[];
handleListSelect: (value: string) => void;
isReadOnly: boolean;
isReadOnly?: boolean;
formLabelBgColor?: string;
}

export const AllContractLists = ({
search,
setSearch,
handleListSelect,
contractLists,
isReadOnly,
formLabelBgColor,
handleListSelect,
}: AllContractListsProps) => {
const filteredContractLists = contractLists.filter((item) =>
item.name.toLowerCase().includes(search.toLowerCase())
const [searchKeyword, setSearchKeyword] = useState("");

const filteredContractLists = useMemo(
() =>
contractLists.filter((item) =>
item.name.toLowerCase().includes(searchKeyword.toLowerCase())
),
[contractLists, searchKeyword]
);

return (
<Box minH="xs" w="100%">
<TextInput
variant="floating"
value={search}
setInputState={setSearch}
value={searchKeyword}
setInputState={setSearchKeyword}
label="Search for your lists"
labelBgColor={formLabelBgColor}
size="md"
mb={isReadOnly ? 4 : 12}
/>
<Box my="18px" />
<SimpleGrid columns={{ sm: 1, md: 2, lg: 3 }} spacing={4} w="full">
{filteredContractLists.map((item) => (
<ListCard
key={item.slug}
item={item}
handleListSelect={handleListSelect}
isReadOnly={isReadOnly || !item.isInfoEditable}
showLastUpdated={item.isContractRemovable}
/>
))}
</SimpleGrid>
{filteredContractLists.length === 0 && (
{filteredContractLists.length === 0 ? (
<EmptyState message="None of your lists matches this search." />
) : (
<SimpleGrid columns={{ sm: 1, md: 2, lg: 3 }} spacing={4} w="full">
{filteredContractLists.map((item) => (
<ContractListCard
key={item.slug}
item={item}
handleListSelect={handleListSelect}
isReadOnly={isReadOnly || !item.isInfoEditable}
/>
))}
</SimpleGrid>
)}
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,37 @@ import {
MenuButton,
MenuList,
Spacer,
chakra,
} from "@chakra-ui/react";
import dayjs from "dayjs";
import { MdMoreHoriz, MdMode, MdDelete } from "react-icons/md";

import { EditList, RemoveList } from "../list";
import { getListIcon } from "lib/data";
import { getListIcon, INSTANTIATED_LIST_NAME } from "lib/data";
import type { ContractListInfo } from "lib/stores/contract";
import { formatSlugName } from "lib/utils";

interface ListCardProps {
const StyledIcon = chakra(Icon, {
baseStyle: {
boxSize: "4",
display: "flex",
alignItems: "center",
},
});

interface ContractListCardProps {
item: ContractListInfo;
handleListSelect: (value: string) => void;
isReadOnly: boolean;
showLastUpdated: boolean;
}
export const ListCard = ({

export const ContractListCard = ({
item,
handleListSelect,
isReadOnly,
showLastUpdated,
}: ListCardProps) => {
const iconProps = {
boxSize: "4",
display: "flex",
alignItems: "center",
};
}: ContractListCardProps) => {
const showLastUpdated = item.slug !== formatSlugName(INSTANTIATED_LIST_NAME);

return (
<LinkBox cursor="pointer">
<Flex
Expand Down Expand Up @@ -95,16 +101,14 @@ export const ListCard = ({
<EditList
list={{ label: item.name, value: item.slug }}
menuItemProps={{
icon: <Icon as={MdMode} style={iconProps} color="gray.600" />,
icon: <StyledIcon as={MdMode} color="gray.600" />,
children: "Edit list name",
}}
/>
<RemoveList
list={{ label: item.name, value: item.slug }}
menuItemProps={{
icon: (
<Icon as={MdDelete} style={iconProps} color="error.light" />
),
icon: <StyledIcon as={MdDelete} color="error.light" />,
children: "Remove list",
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,90 +1,86 @@
import { Box, Flex } from "@chakra-ui/react";
import { matchSorter } from "match-sorter";
import type { Dispatch, SetStateAction } from "react";
import { useState } from "react";
import { useMemo, useState } from "react";

import { TagSelection, TextInput } from "lib/components/forms";
import { EmptyState } from "lib/components/state/EmptyState";
import { ZeroState } from "lib/components/state/ZeroState";
import { ContractListReadOnlyTable } from "lib/pages/contracts/components/ContractListReadOnlyTable";
import { ContractListTable } from "lib/pages/contracts/components/ContractListTable";
import { ContractListReadOnlyTable } from "lib/pages/contract-list/components/ContractListReadOnlyTable";
import { ContractListTable } from "lib/pages/contract-list/components/ContractListTable";
import type { ContractInfo, ContractListInfo } from "lib/stores/contract";
import type { Option } from "lib/types";
import type { ContractAddr, Option } from "lib/types";

interface FilteredListDetailProps {
search: string;
tagFilter: string[];
contracts: ContractInfo[];
isReadOnly: boolean;
isContractRemovable?: Option;
onContractSelect?: (addr: string) => void;
}

interface ListDetailProps {
search: string;
setSearch: Dispatch<SetStateAction<string>>;
contractListInfo: ContractListInfo;
isReadOnly: boolean;
isContractRemovable?: Option;
onContractSelect?: (addr: string) => void;
isReadOnly?: boolean;
contractRemovalInfo?: Option;
onContractSelect?: (addr: ContractAddr) => void;
}

const FilteredListDetail = ({
search,
tagFilter,
contracts,
isReadOnly,
isContractRemovable,
contractRemovalInfo,
onContractSelect = () => {},
}: FilteredListDetailProps) => {
const filteredContracts = matchSorter(contracts, search, {
keys: ["name", "description", "label", "address"],
sorter: (sortedItem) => sortedItem,
}).filter((contract) =>
tagFilter.every((tag) => contract.tags?.includes(tag))
);
if (filteredContracts.length === 0)
if (contracts.length === 0)
return (
<EmptyState
message="No contracts match found.
Make sure you are searching with contract address, name, or description."
/>
);
if (!isReadOnly)
return (
<ContractListTable
contracts={filteredContracts}
isContractRemovable={isContractRemovable}
/>
);
return (

return isReadOnly ? (
<ContractListReadOnlyTable
contracts={filteredContracts}
contracts={contracts}
onContractSelect={onContractSelect}
/>
) : (
<ContractListTable
contracts={contracts}
contractRemovalInfo={contractRemovalInfo}
/>
);
};

export const ListDetail = ({
search,
setSearch,
interface ContractListDetailProps {
contractListInfo: ContractListInfo;
isReadOnly?: boolean;
isInstantiatedByMe?: boolean;
onContractSelect?: (addr: ContractAddr) => void;
}

export const ContractListDetail = ({
contractListInfo,
isReadOnly,
isContractRemovable,
isInstantiatedByMe = false,
onContractSelect,
}: ListDetailProps) => {
}: ContractListDetailProps) => {
const [searchKeyword, setSearchKeyword] = useState("");
const [tagFilter, setTagFilter] = useState<string[]>([]);

const filteredContracts = useMemo(
() =>
matchSorter(contractListInfo.contracts, searchKeyword, {
keys: ["name", "description", "label", "address"],
sorter: (sortedItem) => sortedItem,
}).filter((contract) =>
tagFilter.every((tag) => contract.tags?.includes(tag))
),
[contractListInfo.contracts, searchKeyword, tagFilter]
);

return (
<Box minH="xs">
<Box minH="xs" pb="48px">
<Box px={isReadOnly ? "0px" : "48px"}>
<Flex gap={2} w="full" my={isReadOnly ? "24px" : "48px"}>
<TextInput
variant="floating"
value={search}
setInputState={setSearch}
value={searchKeyword}
setInputState={setSearchKeyword}
placeholder="Search with contract address, name, or description"
size="lg"
size={!isReadOnly ? "lg" : "md"}
/>
{!isReadOnly && (
<TagSelection
Expand All @@ -102,14 +98,17 @@ export const ListDetail = ({
<ZeroState
list={{ label: contractListInfo.name, value: contractListInfo.slug }}
isReadOnly={isReadOnly}
isInstantiatedByMe={isInstantiatedByMe}
/>
) : (
<FilteredListDetail
search={search}
tagFilter={tagFilter}
contracts={contractListInfo.contracts}
contracts={filteredContracts}
isReadOnly={isReadOnly}
isContractRemovable={isContractRemovable}
contractRemovalInfo={
contractListInfo.isContractRemovable
? { label: contractListInfo.name, value: contractListInfo.slug }
: undefined
}
onContractSelect={onContractSelect}
/>
)}
Expand Down
Loading

1 comment on commit d1673dd

@vercel
Copy link

@vercel vercel bot commented on d1673dd Dec 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.