chore(MarketOffers): Implement the market offers filter.

This commit is contained in:
a.bouhuolia 2022-06-05 22:40:12 +02:00
parent 07a984bb52
commit f1dad626aa
19 changed files with 539 additions and 1 deletions

View file

@ -0,0 +1,42 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { MarketoffersSelectPaymentMethods } from ".";
export default {
title: "molecules/MarketoffersSelectPaymentMethods",
component: MarketoffersSelectPaymentMethods,
} as ComponentMeta<typeof MarketoffersSelectPaymentMethods>;
const Template: ComponentStory<
typeof MarketoffersSelectPaymentMethods
> = () => {
return <MarketoffersSelectPaymentMethods data={data} />;
};
export const Default = Template.bind({});
Default.args = {};
const data = [
{
method: "Celpay",
rateTradeLimit: 20,
rateTradeLimitCurrency: "XMR",
info: "Global",
},
];

View file

@ -0,0 +1,54 @@
import { createTable } from "@tanstack/react-table";
import { createStyles } from "@mantine/core";
import { Table } from "@molecules/Table";
const table = createTable().setRowType<TMarketOfferPaymentMethod>();
interface MarketoffersSelectPaymentMethods {
data: TMarketOfferPaymentMethod[];
}
export function MarketoffersSelectPaymentMethods({
data,
}: MarketoffersSelectPaymentMethods) {
const { classes } = useStyles();
return (
<Table
table={table}
columns={columns}
data={data}
tableWrap={{
verticalSpacing: "xs",
}}
/>
);
}
const columns = [
table.createDataColumn("method", {
id: "method",
header: "Method",
}),
table.createDataColumn("rateTradeLimit", {
id: "rateTradeLimit",
header: "Per Trade Limit",
size: 400,
}),
table.createDataColumn("info", {
id: "info",
header: "Info",
size: 400,
})
];
export interface TMarketOfferPaymentMethod {
method: string;
rateTradeLimit: number;
rateTradeLimitCurrency: string;
info: string;
}
const useStyles = createStyles((theme) => ({
root: {},
}));

View file

@ -0,0 +1 @@
export * from "./MarketOffersSelectPaymentMethods";

View file

@ -0,0 +1,45 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import {
MarketOffersTradingPairTable,
TMarketOffersTradingPair,
} from "./MarketOffersTradingPairTable";
export default {
title: "molecules/MarketOffersTradingPairTable",
component: MarketOffersTradingPairTable,
} as ComponentMeta<typeof MarketOffersTradingPairTable>;
const Template: ComponentStory<typeof MarketOffersTradingPairTable> = () => {
return <MarketOffersTradingPairTable data={data} />;
};
export const Default = Template.bind({});
Default.args = {};
const data = [
{
fromPair: "EUR",
toPair: "XMR",
lastPrice: 101.122,
lastPriceCurrency: "EUR",
dayChangeRate: 12.12,
dayChangeVolume: 1222.123,
},
] as TMarketOffersTradingPair;

View file

@ -0,0 +1,61 @@
import { createTable } from "@tanstack/react-table";
import { createStyles } from "@mantine/core";
import { Table } from "@molecules/Table";
const table = createTable().setRowType<TMarketOffersTradingPair>();
interface MarketOffersTradingPairTableProps {
data: TMarketOffersTradingPair[];
}
export function MarketOffersTradingPairTable({
data,
}: MarketOffersTradingPairTableProps) {
const { classes } = useStyles();
return (
<Table
table={table}
columns={columns}
data={data}
tableWrap={{
verticalSpacing: "xs",
}}
/>
);
}
const columns = [
table.createDataColumn("fromPair", {
id: "pair",
header: "Pair",
}),
table.createDataColumn("lastPrice", {
id: "lastPrice",
header: "Last Price",
size: 400,
}),
table.createDataColumn("dayChangeRate", {
id: "lastPrice",
header: "24th Change",
size: 400,
}),
table.createDataColumn("dayChangeVolume", {
id: "lastPrice",
header: "24h Vol",
size: 400,
}),
];
export interface TMarketOffersTradingPair {
fromPair: string;
toPair: string;
lastPrice: number;
lastPriceCurrency: number;
dayChangeRate: number;
dayChangeVolume: number;
}
const useStyles = createStyles((theme) => ({
root: {},
}));

View file

@ -0,0 +1,30 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { MarketOffersFilterAmountForm } from "./MarketOffersFilterAmountForm";
export default {
title: "organisms/MarketOffersFilterAmountForm",
component: MarketOffersFilterAmountForm,
} as ComponentMeta<typeof MarketOffersFilterAmountForm>;
const Template: ComponentStory<typeof MarketOffersFilterAmountForm> = () => {
return <MarketOffersFilterAmountForm />;
};
export const Default = Template.bind({});
Default.args = {};

View file

@ -0,0 +1,87 @@
import { TextInput } from "@atoms/TextInput";
import { Box, Grid, Text } from "@mantine/core";
import { useForm } from "@mantine/hooks";
export function MarketOffersFilterAmountForm() {
const form = useForm({
initialValues: {
minAmountFrom: null,
minAmountTo: null,
maxAmountFrom: null,
maxAmountTo: null,
},
});
return (
<form
onSubmit={form.onSubmit((values: MarketOffersFilterAmountFormValues) =>
console.log(values)
)}
>
<Grid>
<Grid.Col span={8}>
<Text weight={500}>Minimum amount</Text>
<Text color="gray">Set the minimum amount you want to buy.</Text>
</Grid.Col>
<Grid.Col span={4}>
<TextInput
id={"minAmountFrom"}
{...form.getInputProps("minAmountFrom")}
rightSection={
<Text mr="xl" color="gray">
EUR
</Text>
}
mb="lg"
/>
<TextInput
id={"minAmountTo"}
{...form.getInputProps("minAmountTo")}
rightSection={
<Text mr="xl" color="gray">
XMR
</Text>
}
/>
</Grid.Col>
</Grid>
<Grid mt="xl">
<Grid.Col span={8}>
<Text weight={500}>Maximum amount</Text>
<Text color="gray">Set the maximum amount you want to buy.</Text>
</Grid.Col>
<Grid.Col span={4}>
<TextInput
id={"maxAmountFrom"}
{...form.getInputProps("maxAmountFrom")}
rightSection={
<Text mr="xl" color="gray">
XMR
</Text>
}
mb="lg"
/>
<TextInput
id={"maxAmountTo"}
{...form.getInputProps("maxAmountTo")}
rightSection={
<Text mr="xl" color="gray">
EUR
</Text>
}
/>
</Grid.Col>
</Grid>
</form>
);
}
interface MarketOffersFilterAmountFormValues {
maxAmountTo: string;
maxAmountFrom: string;
minAmountTo: string;
minAmountFrom: string;
}

View file

@ -0,0 +1,30 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { MarketOffersFilterBar } from ".";
export default {
title: "organisms/MarketOffersFilterBar",
component: MarketOffersFilterBar,
} as ComponentMeta<typeof MarketOffersFilterBar>;
const Template: ComponentStory<typeof MarketOffersFilterBar> = () => {
return <MarketOffersFilterBar />;
};
export const Default = Template.bind({});
Default.args = {};

View file

@ -0,0 +1,123 @@
import { LangKeys } from "@constants/lang";
import { Divider, Group, Button, createStyles } from "@mantine/core";
import { useModals } from "@mantine/modals";
import { MarketOffersTradingPair } from "@organisms/MarketOffersTradingPair";
import { FormattedMessage } from "react-intl";
export function MarketOffersFilterBar() {
const { classes } = useStyles();
const modals = useModals();
const handlePairsBtnClick = () => {
modals.openConfirmModal({
title: "Select trading pair",
children: <MarketOffersTradingPair />,
size: 570,
});
};
return (
<Group position="apart" className={classes.root}>
<Group>
<Button radius="md" size="md">
XMR/EUR
</Button>
<Divider className={classes.divider} orientation="vertical" />
<Button
color="gray"
variant="outline"
radius="md"
size="md"
classNames={{ root: classes.buttonBold, label: classes.buttonLabel }}
onClick={handlePairsBtnClick}
>
XMR/EUR
</Button>
<Button
color="gray"
variant="outline"
radius="md"
size="md"
classNames={{ root: classes.buttonBold, label: classes.buttonLabel }}
>
<FormattedMessage
id={LangKeys.MarketOffersAmount}
defaultMessage={"Amount"}
/>
</Button>
<Button
color="gray"
variant="outline"
radius="md"
size="md"
classNames={{ label: classes.buttonLabel }}
>
<FormattedMessage
id={LangKeys.MarketOffersAccountDetails}
defaultMessage={"Payment method"}
/>
</Button>
<Button
color="gray"
variant="outline"
radius="md"
size="md"
classNames={{ label: classes.buttonLabel }}
>
<FormattedMessage
id={LangKeys.MarketOffersAccountDetails}
defaultMessage={"Account details"}
/>
</Button>
</Group>
<Group>
<Button
variant="outline"
color="dark"
radius="md"
size="md"
classNames={{
root: classes.buttonBold,
}}
>
Show market depth
</Button>
<Button radius="md" size="md">
<FormattedMessage
id={LangKeys.MarketOffersCreateOffer}
defaultMessage={"Create Offer"}
/>
</Button>
</Group>
</Group>
);
}
const useStyles = createStyles((theme) => ({
root: {
minHeight: 84,
padding: 15,
borderBottom: `1px solid ${theme.colors.gray[3]}`,
},
button: {
borderColor: "#E8E7EC",
},
buttonLabel: {
color: "#111",
},
buttonBold: {
borderWidth: 2,
borderColor: "#111",
},
divider: {
marginTop: "auto",
marginBottom: "auto",
height: 28,
},
}));

View file

@ -0,0 +1 @@
export * from "./MarketoffersFilterBar";

View file

@ -0,0 +1,17 @@
import { FC } from "react";
export function MarketOffersTradingPair() {
return (
<MarketOffersTradingPairBoot>
<MarketOffersTradingPairLoaded />
</MarketOffersTradingPairBoot>
);
}
const MarketOffersTradingPairLoaded: FC = ({ children }) => {
return <>{children}</>;
};
const MarketOffersTradingPairBoot: FC = ({ children }) => {
return <>{children}</>;
};

View file

@ -0,0 +1 @@
export * from "./MarketOffersTradingPair";

View file

@ -99,4 +99,10 @@ export enum LangKeys {
AccountBackupRestoreBtn = "account.backup.restore.btn",
AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification",
AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification",
MarketOffersAmount = "marketOffers.filter.amount",
MarketOffersPaymentMethod = "marketOffers.filter.amount",
MarketOffersAccountDetails = "marketOffers.filter.accountDetails",
MarketOffersShowMarketDepth = "marketOffers.filter.showMarketDepth",
MarketOffersHideMarketDepth = "marketOffers.filter.hideMarketDepth",
MarketOffersCreateOffer = "marketOffers.filter.createOffer",
}

View file

@ -116,6 +116,13 @@ const LangPackEN: { [key in LangKeys]: string } = {
"The backup has been downloaded successfully.",
[LangKeys.AccountBackupRestoreSuccessNotif]:
"The backup has been restored successfully.",
};
[LangKeys.MarketOffersAmount]: 'Amount',
[LangKeys.MarketOffersPaymentMethod]: 'Payment method',
[LangKeys.MarketOffersAccountDetails]: 'Account details',
[LangKeys.MarketOffersShowMarketDepth]: 'Show market depth',
[LangKeys.MarketOffersHideMarketDepth]: 'Hide market depth',
[LangKeys.MarketOffersCreateOffer]: 'Create offer',
};
export default LangPackEN;

View file

@ -29,6 +29,7 @@ export enum QueryKeys {
XmrSeed = "Haveno.XmrSeed",
XmrPrimaryAddress = "Haveno.XmrPrimaryAddress",
XmrTxs = "Haveno.XmrTransactions",
PaymentMethods = "Haveno.PaymentMethods",
// Storage
StorageAccountInfo = "Storage.AccountInfo",

View file

@ -0,0 +1,32 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import { useQuery } from "react-query";
import type { PaymentMethod } from "haveno-ts";
import { useHavenoClient } from "./useHavenoClient";
import { QueryKeys } from "@constants/query-keys";
interface PaymentMethodsQuery {
assetCode: string;
}
export function usePaymentMethods(query: PaymentMethodsQuery) {
const client = useHavenoClient();
return useQuery<Array<PaymentMethod>, Error>(QueryKeys.PaymentAccounts, () =>
client.getPaymentMethods(query.assetCode)
);
}