mirror of
https://github.com/haveno-dex/haveno-ui.git
synced 2024-10-01 07:35:39 -04:00
feat: market filters
--- Co-authored-by: @schowdhuri Reviewed-by: @schowdhuri
This commit is contained in:
parent
1c0e371c0a
commit
fcd209fd3a
@ -39,7 +39,14 @@ export function AppRoutes() {
|
|||||||
<Route path={ROUTES.Login} element={<Login />} />
|
<Route path={ROUTES.Login} element={<Login />} />
|
||||||
<Route path={ROUTES.Welcome} element={<Welcome />} />
|
<Route path={ROUTES.Welcome} element={<Welcome />} />
|
||||||
<Route path={ROUTES.CreateAccount} element={<CreateAccount />} />
|
<Route path={ROUTES.CreateAccount} element={<CreateAccount />} />
|
||||||
<Route path={ROUTES.Markets} element={<MarketsOffersPage />} />
|
<Route
|
||||||
|
path={ROUTES.Markets}
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<MarketsOffersPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={ROUTES.MyWallet}
|
path={ROUTES.MyWallet}
|
||||||
element={
|
element={
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { ReactNode } from "react";
|
||||||
|
import type { BoxProps } from "@mantine/core";
|
||||||
|
import { Box, createStyles } from "@mantine/core";
|
||||||
|
|
||||||
|
interface AmountChangeProps extends BoxProps<"div"> {
|
||||||
|
children: ReactNode;
|
||||||
|
positive?: boolean;
|
||||||
|
negative?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AmountChange(props: AmountChangeProps) {
|
||||||
|
const { children, positive = false, negative = false } = props;
|
||||||
|
const { classes } = useStyles({
|
||||||
|
positive,
|
||||||
|
negative,
|
||||||
|
});
|
||||||
|
return <Box className={classes.root}>{children}</Box>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AmountChangeStyleProps {
|
||||||
|
positive: boolean;
|
||||||
|
negative: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(
|
||||||
|
(theme, { positive, negative }: AmountChangeStyleProps) => ({
|
||||||
|
root: {
|
||||||
|
color: negative
|
||||||
|
? theme.colors.red[6]
|
||||||
|
: positive
|
||||||
|
? theme.colors.green[6]
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
@ -14,8 +14,15 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
import { createStyles, TextInput as MTextInput } from "@mantine/core";
|
import type {
|
||||||
import type { TextInputProps as MTextInputProps } from "@mantine/core";
|
TextInputProps as MTextInputProps,
|
||||||
|
NumberInputProps as MNumberInputProps,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import {
|
||||||
|
createStyles,
|
||||||
|
TextInput as MTextInput,
|
||||||
|
NumberInput as MNumberInput,
|
||||||
|
} from "@mantine/core";
|
||||||
|
|
||||||
interface TextInputProps extends MTextInputProps {
|
interface TextInputProps extends MTextInputProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -24,9 +31,21 @@ interface TextInputProps extends MTextInputProps {
|
|||||||
export function TextInput(props: TextInputProps) {
|
export function TextInput(props: TextInputProps) {
|
||||||
const { id, ...rest } = props;
|
const { id, ...rest } = props;
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
return <MTextInput classNames={classes} id={id} {...rest} />;
|
return <MTextInput classNames={classes} id={id} {...rest} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface NumberInputProps extends MNumberInputProps {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NumberInput(props: NumberInputProps) {
|
||||||
|
const { id, ...rest } = props;
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
return <MNumberInput classNames={classes} id={id} {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
label: {
|
label: {
|
||||||
fontSize: "0.875rem",
|
fontSize: "0.875rem",
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { ToggleButton } from "./ToggleButton";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "atoms/ToggleButton",
|
||||||
|
component: ToggleButton,
|
||||||
|
} as ComponentMeta<typeof ToggleButton>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof ToggleButton> = (args) => {
|
||||||
|
return <ToggleButton {...args} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
Default.args = {
|
||||||
|
labels: ["Sell XMR", "Buy XMR"],
|
||||||
|
};
|
@ -0,0 +1,38 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { describe, expect, it } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { ToggleButton } from "./ToggleButton";
|
||||||
|
|
||||||
|
describe("atoms::ToggleButton", () => {
|
||||||
|
it("renders without exploding", () => {
|
||||||
|
const { asFragment, unmount } = render(
|
||||||
|
<ToggleButton labels={["Sell", "Buy"]} />
|
||||||
|
);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders all tabs", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<ToggleButton labels={["Sell XMR", "Buy XMR"]} />
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Sell XMR")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Buy XMR")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,97 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createStyles, Tabs } from "@mantine/core";
|
||||||
|
|
||||||
|
interface ToggleButtonProps {
|
||||||
|
labels: Array<string>;
|
||||||
|
onChange?: (selectedIndex: number) => void;
|
||||||
|
active?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToggleButton({ labels, onChange, active }: ToggleButtonProps) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
const handleChange = (tabIndex: number) => {
|
||||||
|
onChange && onChange(tabIndex);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
variant="unstyled"
|
||||||
|
classNames={classes}
|
||||||
|
active={active}
|
||||||
|
onTabChange={handleChange}
|
||||||
|
>
|
||||||
|
{labels.map((label, index) => (
|
||||||
|
<Tabs.Tab key={index} label={label} />
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
tabControl: {
|
||||||
|
backgroundColor:
|
||||||
|
theme.colorScheme === "dark"
|
||||||
|
? theme.colors.dark[6]
|
||||||
|
: theme.colors.gray[0],
|
||||||
|
border: `0 solid ${
|
||||||
|
theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[2]
|
||||||
|
}`,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
color:
|
||||||
|
theme.colorScheme === "dark"
|
||||||
|
? theme.colors.dark[0]
|
||||||
|
: theme.colors.gray[9],
|
||||||
|
fontSize: theme.fontSizes.md,
|
||||||
|
fontWeight: 500,
|
||||||
|
padding: `${theme.spacing.lg}px ${theme.spacing.md}px`,
|
||||||
|
|
||||||
|
"&:not(:first-of-type)": {
|
||||||
|
borderLeft: 0,
|
||||||
|
},
|
||||||
|
"&:first-of-type": {
|
||||||
|
borderTopLeftRadius: theme.radius.md,
|
||||||
|
borderBottomLeftRadius: theme.radius.md,
|
||||||
|
borderLeftWidth: 1,
|
||||||
|
},
|
||||||
|
"&:last-of-type": {
|
||||||
|
borderTopRightRadius: theme.radius.md,
|
||||||
|
borderBottomRightRadius: theme.radius.md,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tabActive: {
|
||||||
|
color: theme.white,
|
||||||
|
position: "relative",
|
||||||
|
|
||||||
|
"&:before": {
|
||||||
|
backgroundColor: theme.colors.blue[6],
|
||||||
|
borderRadius: theme.radius.md,
|
||||||
|
bottom: -1,
|
||||||
|
content: `""`,
|
||||||
|
left: -1,
|
||||||
|
position: "absolute",
|
||||||
|
right: -1,
|
||||||
|
top: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tabInner: {
|
||||||
|
position: "relative",
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,54 @@
|
|||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`atoms::ToggleButton > renders without exploding 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<div
|
||||||
|
class="mantine-Tabs-root mantine-jjhffj"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-1s8spa1 mantine-Tabs-tabsListWrapper"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
class="mantine-Group-root __mantine-ref-tabsList mantine-Tabs-tabsList mantine-1tggmaq"
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-selected="true"
|
||||||
|
class="mantine-Tabs-tabControl __mantine-ref-tabActive mantine-Tabs-tabActive mantine-Group-child mantine-9dd9nw"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Tabs-tabInner mantine-12bvju6"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-1s8spa1 mantine-Tabs-tabLabel"
|
||||||
|
>
|
||||||
|
Sell
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-selected="false"
|
||||||
|
class="mantine-Tabs-tabControl mantine-Group-child mantine-1pl658a"
|
||||||
|
role="tab"
|
||||||
|
tabindex="-1"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Tabs-tabInner mantine-12bvju6"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-1s8spa1 mantine-Tabs-tabLabel"
|
||||||
|
>
|
||||||
|
Buy
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
@ -0,0 +1,154 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useState, useEffect } from "react";
|
||||||
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { createTable } from "@tanstack/react-table";
|
||||||
|
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||||
|
import { Table } from "./Table";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "atoms/Table/EditableTable",
|
||||||
|
component: Table,
|
||||||
|
} as ComponentMeta<typeof Table>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Table> = () => {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
table={table}
|
||||||
|
data={data}
|
||||||
|
columns={columns}
|
||||||
|
defaultColumn={defaultColumn}
|
||||||
|
onEditableDataChange={(values) => {
|
||||||
|
console.log(values);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
Default.args = {};
|
||||||
|
|
||||||
|
interface Person {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
age: number;
|
||||||
|
visits: number;
|
||||||
|
status: string;
|
||||||
|
progress: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = createTable().setRowType<Person>().setTableMetaType<{
|
||||||
|
updateData: (rowIndex: number, columnId: string, value: unknown) => void;
|
||||||
|
}>();
|
||||||
|
type TableGenerics = typeof table.generics;
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
table.createGroup({
|
||||||
|
header: "Name",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("firstName", {
|
||||||
|
cell: (info) => info.getValue(),
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn((row) => row.lastName, {
|
||||||
|
id: "lastName",
|
||||||
|
cell: (info) => info.getValue(),
|
||||||
|
header: () => <span>Last Name</span>,
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
table.createGroup({
|
||||||
|
header: "Info",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("age", {
|
||||||
|
header: () => "Age",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createGroup({
|
||||||
|
header: "More Info",
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("visits", {
|
||||||
|
header: () => <span>Visits</span>,
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("status", {
|
||||||
|
header: "Status",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("progress", {
|
||||||
|
header: "Profile Progress",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const data: Array<Person> = [
|
||||||
|
{
|
||||||
|
firstName: "tanner",
|
||||||
|
lastName: "linsley",
|
||||||
|
age: 24,
|
||||||
|
visits: 100,
|
||||||
|
status: "In Relationship",
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: "tandy",
|
||||||
|
lastName: "miller",
|
||||||
|
age: 40,
|
||||||
|
visits: 40,
|
||||||
|
status: "Single",
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: "joe",
|
||||||
|
lastName: "dirte",
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: "Complicated",
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultColumn: Partial<ColumnDef<TableGenerics>> = {
|
||||||
|
cell: ({ getValue, row: { index }, column: { id }, instance }) => {
|
||||||
|
const initialValue = getValue();
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
|
const onBlur = () => {
|
||||||
|
instance.options.meta?.updateData(index, id, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
value={value as string}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
onBlur={onBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
@ -26,10 +26,20 @@ import { TableProvider } from "./use-table-context";
|
|||||||
import { TableHeader } from "./TableHeader";
|
import { TableHeader } from "./TableHeader";
|
||||||
import { TableBody } from "./TableBody";
|
import { TableBody } from "./TableBody";
|
||||||
import { useStyles } from "./Table.style";
|
import { useStyles } from "./Table.style";
|
||||||
|
import { updateTableCell } from "./_utils";
|
||||||
|
|
||||||
export function Table(props: TableProps) {
|
export function Table(props: TableProps) {
|
||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
const { table, columns, data, tableWrap, variant, state } = props;
|
const {
|
||||||
|
table,
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
tableWrap,
|
||||||
|
variant,
|
||||||
|
onEditableDataChange,
|
||||||
|
defaultColumn,
|
||||||
|
state,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const tableInstance = useTableInstance(table, {
|
const tableInstance = useTableInstance(table, {
|
||||||
data,
|
data,
|
||||||
@ -37,6 +47,17 @@ export function Table(props: TableProps) {
|
|||||||
state,
|
state,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getExpandedRowModel: getExpandedRowModel(),
|
getExpandedRowModel: getExpandedRowModel(),
|
||||||
|
meta: {
|
||||||
|
updateData: (rowIndex: number, columnId: string, value: unknown) => {
|
||||||
|
const newData = updateTableCell(data, rowIndex, columnId, value);
|
||||||
|
onEditableDataChange && onEditableDataChange(newData);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(defaultColumn
|
||||||
|
? {
|
||||||
|
defaultColumn,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,7 +20,7 @@ import { useTableContext } from "./use-table-context";
|
|||||||
export function TableBody() {
|
export function TableBody() {
|
||||||
const {
|
const {
|
||||||
table,
|
table,
|
||||||
props: { rowSubComponent },
|
props: { rowSubComponent, onRowClick },
|
||||||
} = useTableContext();
|
} = useTableContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -31,6 +31,7 @@ export function TableBody() {
|
|||||||
key={row.id}
|
key={row.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
row.toggleExpanded();
|
row.toggleExpanded();
|
||||||
|
onRowClick && onRowClick(row);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
@ -18,11 +18,13 @@
|
|||||||
import type { ColumnDef, Row, TableState } from "@tanstack/react-table";
|
import type { ColumnDef, Row, TableState } from "@tanstack/react-table";
|
||||||
import type { TableProps as MTableProps } from "@mantine/core";
|
import type { TableProps as MTableProps } from "@mantine/core";
|
||||||
|
|
||||||
|
// TODO: Add type or generic
|
||||||
export interface TableProps {
|
export interface TableProps {
|
||||||
columns: Array<ColumnDef<any>>;
|
columns: Array<ColumnDef<any>>;
|
||||||
table: any;
|
table: any;
|
||||||
data: Array<any>;
|
data: Array<any>;
|
||||||
state?: Partial<TableState>;
|
state?: Partial<TableState>;
|
||||||
|
defaultColumn?: any;
|
||||||
|
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
showFooter?: boolean;
|
showFooter?: boolean;
|
||||||
@ -31,6 +33,11 @@ export interface TableProps {
|
|||||||
|
|
||||||
tableWrap?: MTableProps;
|
tableWrap?: MTableProps;
|
||||||
variant?: TableVariant;
|
variant?: TableVariant;
|
||||||
|
|
||||||
|
onEditableDataChange?: (v: Array<any>) => void;
|
||||||
|
|
||||||
|
pointerRow?: boolean;
|
||||||
|
onRowClick?: (column: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TableVariant {
|
export enum TableVariant {
|
||||||
|
34
packages/renderer/src/components/molecules/Table/_utils.ts
Normal file
34
packages/renderer/src/components/molecules/Table/_utils.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export const updateTableCell = (
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
data: Array<any>,
|
||||||
|
rowIndex: number,
|
||||||
|
columnId: string,
|
||||||
|
value: unknown
|
||||||
|
) => {
|
||||||
|
return data.map((row, index) => {
|
||||||
|
if (index === rowIndex) {
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
...data[rowIndex]!,
|
||||||
|
[columnId]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,128 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createTable } from "@tanstack/react-table";
|
||||||
|
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||||
|
import { Table } from "../Table";
|
||||||
|
import { CheckboxCell } from "./CheckboxCell";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "atoms/Table/CheckboxCell",
|
||||||
|
component: Table,
|
||||||
|
} as ComponentMeta<typeof Table>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof Table> = (args) => {
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
data={args.data}
|
||||||
|
onEditableDataChange={(values) => {
|
||||||
|
console.log(values);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
interface Person {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
age: number;
|
||||||
|
visits: number;
|
||||||
|
status: string;
|
||||||
|
progress: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = createTable().setRowType<Person>().setTableMetaType<{
|
||||||
|
updateData: (rowIndex: number, columnId: string, value: unknown) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
table.createGroup({
|
||||||
|
header: "Name",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("firstName", {
|
||||||
|
cell: (params) => <CheckboxCell {...params} />,
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn((row) => row.lastName, {
|
||||||
|
id: "lastName",
|
||||||
|
cell: (info) => info.getValue(),
|
||||||
|
header: () => <span>Last Name</span>,
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
table.createGroup({
|
||||||
|
header: "Info",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("age", {
|
||||||
|
header: () => "Age",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createGroup({
|
||||||
|
header: "More Info",
|
||||||
|
columns: [
|
||||||
|
table.createDataColumn("visits", {
|
||||||
|
header: () => <span>Visits</span>,
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("status", {
|
||||||
|
header: "Status",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("progress", {
|
||||||
|
header: "Profile Progress",
|
||||||
|
footer: (props) => props.column.id,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
Default.args = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
firstName: "tanner",
|
||||||
|
lastName: "linsley",
|
||||||
|
age: 24,
|
||||||
|
visits: 100,
|
||||||
|
status: "In Relationship",
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: "tandy",
|
||||||
|
lastName: "miller",
|
||||||
|
age: 40,
|
||||||
|
visits: 40,
|
||||||
|
status: "Single",
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: "joe",
|
||||||
|
lastName: "dirte",
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: "Complicated",
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
] as Array<Person>,
|
||||||
|
};
|
@ -0,0 +1,59 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import type { CheckboxProps } from "@mantine/core";
|
||||||
|
import { Checkbox } from "@mantine/core";
|
||||||
|
import type { Cell, Column, Row, TableInstance } from "@tanstack/react-table";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
// TODO: Add type or generic
|
||||||
|
interface CheckboxCellProps {
|
||||||
|
instance: TableInstance<any>;
|
||||||
|
row: Row<any>;
|
||||||
|
column: Column<any>;
|
||||||
|
cell: Cell<any>;
|
||||||
|
getValue: () => any;
|
||||||
|
checkboxProps?: CheckboxProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CheckboxCell = ({
|
||||||
|
getValue,
|
||||||
|
row: { index },
|
||||||
|
column: { id },
|
||||||
|
instance,
|
||||||
|
checkboxProps,
|
||||||
|
}: CheckboxCellProps) => {
|
||||||
|
const initialValue = getValue();
|
||||||
|
const [value, setValue] = useState<boolean>(initialValue);
|
||||||
|
|
||||||
|
const onBlur = () => {
|
||||||
|
instance.options.meta?.updateData(index, id, value);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(initialValue);
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
id={`${id}-${index}`}
|
||||||
|
checked={value as boolean}
|
||||||
|
onChange={(e) => setValue(e.target.checked)}
|
||||||
|
onBlur={onBlur}
|
||||||
|
{...checkboxProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./CheckboxCell";
|
@ -14,4 +14,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./_types";
|
||||||
export * from "./Table";
|
export * from "./Table";
|
||||||
|
export * from "./cells";
|
||||||
|
@ -20,6 +20,7 @@ import type { TableInstance } from "@tanstack/react-table";
|
|||||||
import type { TableProps } from "../_types";
|
import type { TableProps } from "../_types";
|
||||||
|
|
||||||
interface TableContextValue {
|
interface TableContextValue {
|
||||||
|
// TODO: Add type or generic
|
||||||
table: TableInstance<any>;
|
table: TableInstance<any>;
|
||||||
props: TableProps;
|
props: TableProps;
|
||||||
}
|
}
|
||||||
|
@ -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 { MarketOffersFilterAccountsForm } from "./MarketOffersFilterAccountsForm";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "organisms/MarketOffersFilterAccountsForm",
|
||||||
|
component: MarketOffersFilterAccountsForm,
|
||||||
|
} as ComponentMeta<typeof MarketOffersFilterAccountsForm>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof MarketOffersFilterAccountsForm> = () => {
|
||||||
|
return <MarketOffersFilterAccountsForm />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
@ -0,0 +1,197 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { FormattedMessage } from "react-intl";
|
||||||
|
import { Grid, Text, Checkbox, createStyles, Group } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { Button, TextButton } from "@atoms/Buttons";
|
||||||
|
import { NumberInput } from "@atoms/TextInput";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
import { transformToForm } from "@src/utils/misc";
|
||||||
|
|
||||||
|
interface MarketOffersFilterAccountsFormProps {
|
||||||
|
onSubmit?: (values: MarketOffersFilterAccountsForm) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersFilterAccountsForm({
|
||||||
|
onSubmit,
|
||||||
|
}: MarketOffersFilterAccountsFormProps) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [offersState, setOffersState] = useOffersFilterState();
|
||||||
|
|
||||||
|
const form = useForm<MarketOffersFilterAccountsForm>({
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
// We only care about the fields in the form and remove other fields.
|
||||||
|
// Previously unfilled optional values come as null, so remove those as well.
|
||||||
|
...transformToForm(offersState, initialValues),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClearFilter = () => {
|
||||||
|
form.setValues({ ...initialValues });
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={form.onSubmit((values) => {
|
||||||
|
setOffersState((oldFilter) => ({
|
||||||
|
...oldFilter,
|
||||||
|
...values,
|
||||||
|
}));
|
||||||
|
onSubmit && onSubmit(values);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Grid mb="xl">
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<Text weight={500}>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountLabelSignedAccounts}
|
||||||
|
defaultMessage="Signed Accounts"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountDescSignedAccounts}
|
||||||
|
defaultMessage="Only show accounts that have been signed. Please be aware that new accounts need to get the chance to get signed."
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col sx={{ display: "flex" }} span={4}>
|
||||||
|
<Checkbox
|
||||||
|
id="signedAccounts"
|
||||||
|
radius="sm"
|
||||||
|
size="md"
|
||||||
|
sx={{ alignItems: "flex-start", marginLeft: "auto" }}
|
||||||
|
{...form.getInputProps("signedAccounts", { type: "checkbox" })}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid mb="xl">
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<Text weight={500}>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountLabelMinAccountAge}
|
||||||
|
defaultMessage="Minimum account age"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountDescMinAccountAge}
|
||||||
|
defaultMessage="Only show trade offers with a minimum account age."
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<NumberInput
|
||||||
|
id="minimumAccountAge"
|
||||||
|
{...form.getInputProps("minimumAccountAge")}
|
||||||
|
rightSection={
|
||||||
|
<Text pr="md" color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountDays}
|
||||||
|
defaultMessage="Days"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={50}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<Text weight={500}>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountLabelAmountTrades}
|
||||||
|
defaultMessage="Minimum amount of trades"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountDescAmountTrades}
|
||||||
|
defaultMessage="Only show trade offers from accounts with a minimum amount of completed trades"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<NumberInput
|
||||||
|
id="minimumTradesAmount"
|
||||||
|
{...form.getInputProps("minimumTradesAmount")}
|
||||||
|
rightSection={
|
||||||
|
<Text mr="md" color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountTrades}
|
||||||
|
defaultMessage="Trades"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={65}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Group position="apart" className={classes.footer}>
|
||||||
|
<TextButton
|
||||||
|
onClick={handleClearFilter}
|
||||||
|
className={classes.clearFilterBtn}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountClearFiltersBtn}
|
||||||
|
defaultMessage="Clear filters"
|
||||||
|
/>
|
||||||
|
</TextButton>
|
||||||
|
<Button type="submit" flavor="primary">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketFilterAccountClearFiltersBtn}
|
||||||
|
defaultMessage="Save filters"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarketOffersFilterAccountsForm {
|
||||||
|
signedAccounts: boolean;
|
||||||
|
minimumTradesAmount?: number | null;
|
||||||
|
minimumAccountAge?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
footer: {
|
||||||
|
paddingTop: theme.spacing.xl,
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
borderTop: `1px solid ${theme.colors.gray[1]}`,
|
||||||
|
marginTop: theme.spacing.xl,
|
||||||
|
marginLeft: theme.spacing.xl * -1,
|
||||||
|
marginRight: theme.spacing.xl * -1,
|
||||||
|
},
|
||||||
|
clearFilterBtn: {
|
||||||
|
fontSize: theme.spacing.lg,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
signedAccounts: false,
|
||||||
|
minimumAccountAge: undefined,
|
||||||
|
minimumTradesAmount: undefined,
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersFilterAccountsForm";
|
@ -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 = {};
|
@ -0,0 +1,188 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { FormattedMessage } from "react-intl";
|
||||||
|
import { createStyles, Grid, Group, Text } from "@mantine/core";
|
||||||
|
import { useForm } from "@mantine/hooks";
|
||||||
|
import { NumberInput } from "@atoms/TextInput";
|
||||||
|
import { Button, TextButton } from "@atoms/Buttons";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
import { transformToForm } from "@utils/misc";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
|
||||||
|
interface MarketOffersFilterAmountFormProps {
|
||||||
|
onSubmit?: (values: MarketOffersFilterAmountFormValues) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersFilterAmountForm({
|
||||||
|
onSubmit,
|
||||||
|
}: MarketOffersFilterAmountFormProps) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [offersState, setOffersState] = useOffersFilterState();
|
||||||
|
|
||||||
|
const form = useForm<MarketOffersFilterAmountFormValues>({
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
// We only care about the fields in the form and remove other fields.
|
||||||
|
// Previously unfilled optional values come as null, so remove those as well.
|
||||||
|
...transformToForm(offersState, initialValues),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCreateFilter = () => {
|
||||||
|
form.setValues({ ...initialValues });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={form.onSubmit((values) => {
|
||||||
|
setOffersState((oldFilter) => ({
|
||||||
|
...oldFilter,
|
||||||
|
...values,
|
||||||
|
}));
|
||||||
|
onSubmit && onSubmit(values);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<Text weight={500}>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterFieldMinAmountTrades}
|
||||||
|
defaultMessage="Minimum amount of trades"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterFieldMinAmountTradesDesc}
|
||||||
|
defaultMessage="Set the minimum amount you want to buy."
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<NumberInput
|
||||||
|
id="minAmountFrom"
|
||||||
|
rightSection={
|
||||||
|
<Text color="gray" pr="sm">
|
||||||
|
EUR
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={45}
|
||||||
|
mb="lg"
|
||||||
|
{...form.getInputProps("minimumBaseCurrencyAmount")}
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
id="minAmountTo"
|
||||||
|
rightSection={
|
||||||
|
<Text pr="sm" color="gray">
|
||||||
|
XMR
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={45}
|
||||||
|
{...form.getInputProps("minimumCryptoAmount")}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid mt="xl">
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<Text weight={500}>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterFieldMaxAmount}
|
||||||
|
defaultMessage="Maximum amount"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterFieldMaxAmountDesc}
|
||||||
|
defaultMessage="Set the maximum amount you want to buy."
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Grid.Col>{" "}
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<NumberInput
|
||||||
|
id="maxAmountFrom"
|
||||||
|
{...form.getInputProps("maximumCryptoAmount")}
|
||||||
|
rightSection={
|
||||||
|
<Text pr="sm" color="gray">
|
||||||
|
XMR
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={45}
|
||||||
|
mb="lg"
|
||||||
|
/>
|
||||||
|
<NumberInput
|
||||||
|
id="maxAmountTo"
|
||||||
|
{...form.getInputProps("maximumBaseCurrencyAmount")}
|
||||||
|
rightSection={
|
||||||
|
<Text pr="sm" color="gray">
|
||||||
|
EUR
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
rightSectionWidth={45}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Group position="apart" className={classes.footer}>
|
||||||
|
<TextButton
|
||||||
|
onClick={handleCreateFilter}
|
||||||
|
className={classes.clearFilterBtn}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterAmountClearFiltersBtn}
|
||||||
|
defaultMessage="Clear filters"
|
||||||
|
/>
|
||||||
|
</TextButton>
|
||||||
|
<Button type="submit" flavor="primary">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketAmountFilterAmountSaveBtn}
|
||||||
|
defaultMessage="Save filters"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
footer: {
|
||||||
|
borderTop: `1px solid ${theme.colors.gray[1]}`,
|
||||||
|
marginLeft: theme.spacing.xl * -1,
|
||||||
|
marginRight: theme.spacing.xl * -1,
|
||||||
|
marginTop: theme.spacing.xl,
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
paddingTop: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
clearFilterBtn: {
|
||||||
|
fontSize: theme.spacing.lg,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface MarketOffersFilterAmountFormValues {
|
||||||
|
minimumCryptoAmount?: number | null;
|
||||||
|
minimumBaseCurrencyAmount?: number | null;
|
||||||
|
maximumCryptoAmount?: number | null;
|
||||||
|
maximumBaseCurrencyAmount?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
minimumCryptoAmount: undefined,
|
||||||
|
minimumBaseCurrencyAmount: undefined,
|
||||||
|
maximumCryptoAmount: undefined,
|
||||||
|
maximumBaseCurrencyAmount: undefined,
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersFilterAmountForm";
|
@ -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 = {};
|
@ -0,0 +1,200 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { Divider, Group, createStyles, Text, Box } from "@mantine/core";
|
||||||
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
import { isEmpty } from "lodash";
|
||||||
|
import {
|
||||||
|
useMarketOffersPairModal,
|
||||||
|
useMarketOffersPaymentMethods,
|
||||||
|
useMarketOffersAmountModal,
|
||||||
|
useMarketOffersAccountModal,
|
||||||
|
} from "./hooks";
|
||||||
|
import { MarketOffersFilterButton } from "./MarketOffersFilterButton";
|
||||||
|
import {
|
||||||
|
useAccountDetailsLabel,
|
||||||
|
useMarketOffersFilterAmountLabel,
|
||||||
|
} from "./_hooks";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
import { ReactComponent as BtcIcon } from "@assets/btc.svg";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
import { ToggleButton } from "@atoms/ToggleButton/ToggleButton";
|
||||||
|
|
||||||
|
export function MarketOffersFilterBar() {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [offersFilter, setOffersFilter] = useOffersFilterState();
|
||||||
|
|
||||||
|
// Market offers pair filter modal.
|
||||||
|
const marketOffersPairModal = useMarketOffersPairModal();
|
||||||
|
|
||||||
|
// Market offers payment methods filter modal.
|
||||||
|
const marketOffersPaymentMethodsModal = useMarketOffersPaymentMethods();
|
||||||
|
|
||||||
|
// Market offers account filter modal.
|
||||||
|
const marketOffersAccountModal = useMarketOffersAccountModal();
|
||||||
|
|
||||||
|
// Market offers amount filter modal.
|
||||||
|
const marketOffersAmountModal = useMarketOffersAmountModal();
|
||||||
|
|
||||||
|
const accountDetailsLabel = useAccountDetailsLabel();
|
||||||
|
const filterAmountLabel = useMarketOffersFilterAmountLabel();
|
||||||
|
|
||||||
|
// Handles the buy/sell switch change.
|
||||||
|
const handleBuySellSwitch = (tabIndex: number) => {
|
||||||
|
setOffersFilter((filter) => ({
|
||||||
|
...filter,
|
||||||
|
direction: tabIndex === 0 ? "sell" : "buy",
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
const handleParisBtnClick = () => {
|
||||||
|
marketOffersPairModal.openModal();
|
||||||
|
};
|
||||||
|
const handlePaymentMethodsBtnClick = () => {
|
||||||
|
marketOffersPaymentMethodsModal.openModal();
|
||||||
|
};
|
||||||
|
const handleAccountBtnClick = () => {
|
||||||
|
marketOffersAccountModal.openModal();
|
||||||
|
};
|
||||||
|
const handleAmountBtnClick = () => {
|
||||||
|
marketOffersAmountModal.openModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group position="apart" className={classes.root}>
|
||||||
|
<Group>
|
||||||
|
<Group spacing="sm">
|
||||||
|
<ToggleButton
|
||||||
|
labels={[
|
||||||
|
formatMessage(
|
||||||
|
{
|
||||||
|
id: LangKeys.MarketOffersSwitchSell,
|
||||||
|
defaultMessage: "Sell {currency}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
currency: "XMR",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
formatMessage(
|
||||||
|
{
|
||||||
|
id: LangKeys.MarketOffersSwitchBuy,
|
||||||
|
defaultMessage: "Buy {currency}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
currency: "XMR",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
onChange={handleBuySellSwitch}
|
||||||
|
/>
|
||||||
|
<Text color="gray">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersWith}
|
||||||
|
defaultMessage="with"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
<MarketOffersFilterButton
|
||||||
|
active={!isEmpty(offersFilter.assetCode)}
|
||||||
|
onClick={handleParisBtnClick}
|
||||||
|
>
|
||||||
|
<Box mr="sm">
|
||||||
|
<BtcIcon height={17} width={17} />
|
||||||
|
</Box>
|
||||||
|
{!isEmpty(offersFilter.assetCode) ? (
|
||||||
|
offersFilter.assetCode?.toUpperCase()
|
||||||
|
) : (
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersCurrency}
|
||||||
|
defaultMessage="Currency"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
</Group>
|
||||||
|
<Divider className={classes.divider} orientation="vertical" />
|
||||||
|
|
||||||
|
<MarketOffersFilterButton
|
||||||
|
active={!!filterAmountLabel}
|
||||||
|
onClick={handleAmountBtnClick}
|
||||||
|
>
|
||||||
|
{filterAmountLabel || (
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersAmount}
|
||||||
|
defaultMessage="Amount"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
|
||||||
|
<MarketOffersFilterButton
|
||||||
|
active={!isEmpty(offersFilter.paymentMethods)}
|
||||||
|
bubbleText={
|
||||||
|
isEmpty(offersFilter.paymentMethods)
|
||||||
|
? ""
|
||||||
|
: offersFilter?.paymentMethods?.length + ""
|
||||||
|
}
|
||||||
|
onClick={handlePaymentMethodsBtnClick}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersPaymentMethod}
|
||||||
|
defaultMessage="Payment method"
|
||||||
|
/>
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
|
||||||
|
<MarketOffersFilterButton
|
||||||
|
active={!!accountDetailsLabel}
|
||||||
|
onClick={handleAccountBtnClick}
|
||||||
|
>
|
||||||
|
{accountDetailsLabel || (
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersAccountDetails}
|
||||||
|
defaultMessage="Account details"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<Divider className={classes.divider} orientation="vertical" />
|
||||||
|
<MarketOffersFilterButton>
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersShowMarketDepth}
|
||||||
|
defaultMessage="Show market depth"
|
||||||
|
/>
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
|
||||||
|
<MarketOffersFilterButton variant="filled" color="blue" size="md">
|
||||||
|
<FormattedMessage
|
||||||
|
id={LangKeys.MarketOffersCreateOffer}
|
||||||
|
defaultMessage="Create Offer"
|
||||||
|
/>
|
||||||
|
</MarketOffersFilterButton>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
background: theme.white,
|
||||||
|
borderBottom: `1px solid ${theme.colors.gray[3]}`,
|
||||||
|
minHeight: 84,
|
||||||
|
padding: "18px 22px",
|
||||||
|
},
|
||||||
|
divider: {
|
||||||
|
marginBottom: "auto",
|
||||||
|
marginTop: "auto",
|
||||||
|
height: 28,
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,98 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { ButtonProps as MButtonProps, ButtonVariant } from "@mantine/core";
|
||||||
|
import { Button, Box, createStyles } from "@mantine/core";
|
||||||
|
|
||||||
|
export interface ButtonProps extends MButtonProps<"button"> {
|
||||||
|
active?: boolean;
|
||||||
|
bubbleText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarketOffersFilterButtonStyleProps {
|
||||||
|
active: boolean;
|
||||||
|
variant: ButtonVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersFilterButton(props: ButtonProps) {
|
||||||
|
const {
|
||||||
|
bubbleText,
|
||||||
|
className,
|
||||||
|
active,
|
||||||
|
variant = "outline",
|
||||||
|
classNames,
|
||||||
|
...others
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const { cx, classes } = useStyles(
|
||||||
|
{
|
||||||
|
active: active || false,
|
||||||
|
variant: variant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MarketOffersFilterButton",
|
||||||
|
classNames,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
color="gray"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
variant={variant}
|
||||||
|
{...others}
|
||||||
|
className={cx(
|
||||||
|
classes.root,
|
||||||
|
{
|
||||||
|
[classes.outline]: variant === "outline",
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{bubbleText && <Box className={classes.bubbleText}>{bubbleText}</Box>}
|
||||||
|
{props.children}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(
|
||||||
|
(theme, { active }: MarketOffersFilterButtonStyleProps) => ({
|
||||||
|
root: {
|
||||||
|
paddingLeft: theme.spacing.sm,
|
||||||
|
paddingRight: theme.spacing.sm,
|
||||||
|
position: "relative",
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
borderColor: active ? "#111" : "#E8E7EC",
|
||||||
|
borderWidth: active ? 2 : 1,
|
||||||
|
color: "#111",
|
||||||
|
},
|
||||||
|
bubbleText: {
|
||||||
|
backgroundColor: "#111",
|
||||||
|
borderRadius: 15,
|
||||||
|
boxShadow: "0 0 0 2px #fff",
|
||||||
|
color: "#fff",
|
||||||
|
position: "absolute",
|
||||||
|
padding: "2px 4px",
|
||||||
|
top: -5,
|
||||||
|
right: -5,
|
||||||
|
fontSize: 10,
|
||||||
|
lineHeight: 1,
|
||||||
|
minWidth: 15,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
@ -0,0 +1,82 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useIntl } from "react-intl";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the active label of account details button.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function useAccountDetailsLabel() {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [offersFilterState] = useOffersFilterState();
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
formatMessage({
|
||||||
|
id: LangKeys.MarketOffersSigned,
|
||||||
|
defaultMessage: "Signed",
|
||||||
|
}),
|
||||||
|
offersFilterState.signedAccounts,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
formatMessage(
|
||||||
|
{
|
||||||
|
id: LangKeys.MarketOffersTradesAmount,
|
||||||
|
defaultMessage: ">{value} days",
|
||||||
|
},
|
||||||
|
{ value: offersFilterState.minimumTradesAmount }
|
||||||
|
),
|
||||||
|
offersFilterState.minimumTradesAmount,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
formatMessage(
|
||||||
|
{
|
||||||
|
id: LangKeys.MarketOffersDaysAge,
|
||||||
|
defaultMessage: ">{value} days",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: offersFilterState.minimumAccountAge,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
offersFilterState.minimumAccountAge,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
.filter((option) => option[1])
|
||||||
|
.map((option) => option[0])
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the active label of amount button.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function useMarketOffersFilterAmountLabel() {
|
||||||
|
const [offersFilterState] = useOffersFilterState();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!offersFilterState.minimumBaseCurrencyAmount &&
|
||||||
|
!offersFilterState.maximumBaseCurrencyAmount
|
||||||
|
) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fromAmount = offersFilterState.minimumBaseCurrencyAmount || "~";
|
||||||
|
const toAmount = offersFilterState.maximumBaseCurrencyAmount || "~";
|
||||||
|
|
||||||
|
return `${fromAmount} - ${toAmount} XMR`;
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./useMarketOffersPairModal";
|
||||||
|
export * from "./useMarketOffersPaymentMethods";
|
||||||
|
export * from "./useMarketOffersAccountModal";
|
||||||
|
export * from "./useMarketOffersAmountModal";
|
@ -0,0 +1,39 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useModals } from "@mantine/modals";
|
||||||
|
import { MarketOffersFilterAccountsForm } from "@organisms/MarketOffersFilterAccountsForm";
|
||||||
|
|
||||||
|
export function useMarketOffersAccountModal() {
|
||||||
|
const modals = useModals();
|
||||||
|
|
||||||
|
return {
|
||||||
|
openModal: () => {
|
||||||
|
const modalId = modals.openModal({
|
||||||
|
title: "Amount",
|
||||||
|
children: (
|
||||||
|
<MarketOffersFilterAccountsForm
|
||||||
|
onSubmit={() => {
|
||||||
|
modals.closeModal(modalId);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
size: "lg",
|
||||||
|
withCloseButton: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useModals } from "@mantine/modals";
|
||||||
|
import { MarketOffersFilterAmountForm } from "@organisms/MarketOffersFilterAmountForm";
|
||||||
|
|
||||||
|
export function useMarketOffersAmountModal() {
|
||||||
|
const modals = useModals();
|
||||||
|
|
||||||
|
return {
|
||||||
|
openModal: () => {
|
||||||
|
const modalId = modals.openModal({
|
||||||
|
title: "Amount",
|
||||||
|
children: (
|
||||||
|
<MarketOffersFilterAmountForm
|
||||||
|
onSubmit={() => {
|
||||||
|
modals.closeModal(modalId);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
size: "lg",
|
||||||
|
withCloseButton: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createStyles } from "@mantine/core";
|
||||||
|
import { useModals } from "@mantine/modals";
|
||||||
|
import { MarketOffersTradingPair } from "@organisms/MarketOffersTradingPair";
|
||||||
|
|
||||||
|
export function useMarketOffersPairModal() {
|
||||||
|
const modals = useModals();
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
return {
|
||||||
|
openModal: () => {
|
||||||
|
const modalId = modals.openModal({
|
||||||
|
title: "Select trading pair",
|
||||||
|
children: (
|
||||||
|
<MarketOffersTradingPair
|
||||||
|
onSubmit={() => {
|
||||||
|
modals.closeModal(modalId);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
withCloseButton: true,
|
||||||
|
size: 570,
|
||||||
|
classNames: classes,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
title: {
|
||||||
|
fontSize: theme.fontSizes.md,
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 10,
|
||||||
|
marginTop: -10,
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
marginLeft: theme.spacing.sm * -2,
|
||||||
|
marginRight: theme.spacing.sm * -2,
|
||||||
|
marginBottom: theme.spacing.sm * -2,
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,53 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createStyles } from "@mantine/core";
|
||||||
|
import { useModals } from "@mantine/modals";
|
||||||
|
import { MarketOffersFilterPaymentMethods } from "@organisms/MarketOffersFilterPaymentMethods";
|
||||||
|
|
||||||
|
export function useMarketOffersPaymentMethods() {
|
||||||
|
const modals = useModals();
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
return {
|
||||||
|
openModal: () => {
|
||||||
|
modals.openModal({
|
||||||
|
title: "Filter on payment methods",
|
||||||
|
children: <MarketOffersFilterPaymentMethods />,
|
||||||
|
size: 970,
|
||||||
|
withCloseButton: true,
|
||||||
|
classNames: classes,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
padding: "0 !important",
|
||||||
|
},
|
||||||
|
modal: {
|
||||||
|
padding: "0 !important",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: theme.fontSizes.md,
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
padding: "12px 20px",
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersFilterBar";
|
@ -0,0 +1,71 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { includes } from "lodash";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import type { TMarketOfferPaymentMethod } from "@organisms/MarketOffersPaymentMethodsTable";
|
||||||
|
import { MarketOffersPaymentMethodsTable } from "@organisms/MarketOffersPaymentMethodsTable";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
import { usePaymentMethods } from "@hooks/haveno/usePaymentMethods";
|
||||||
|
|
||||||
|
export function MarketOffersFilterPaymentMethodsLoaded() {
|
||||||
|
const { data: paymentMethods } = usePaymentMethods();
|
||||||
|
const [filter, setFilter] = useOffersFilterState();
|
||||||
|
|
||||||
|
const tableData = useMemo(
|
||||||
|
() =>
|
||||||
|
paymentMethods?.map((item) => ({
|
||||||
|
...item,
|
||||||
|
methodChecked: includes(filter.paymentMethods, item.methodKey),
|
||||||
|
})),
|
||||||
|
[paymentMethods]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleEditableDataChange = (
|
||||||
|
newData: Array<TMarketOfferPaymentMethod>
|
||||||
|
) => {
|
||||||
|
setFilter((oldQuery) => ({
|
||||||
|
...oldQuery,
|
||||||
|
paymentMethods: newData
|
||||||
|
.filter((payment) => payment.methodChecked)
|
||||||
|
.map((payment) => payment.methodKey),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
if (!tableData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<MarketOffersPaymentMethodsTable
|
||||||
|
data={tableData}
|
||||||
|
onEditableDataChange={handleEditableDataChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarketOffersFilterPaymentMethodsBoot: FC = ({ children }) => {
|
||||||
|
const { isLoading } = usePaymentMethods();
|
||||||
|
|
||||||
|
return isLoading ? <>Loading</> : <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function MarketOffersFilterPaymentMethods() {
|
||||||
|
return (
|
||||||
|
<MarketOffersFilterPaymentMethodsBoot>
|
||||||
|
<MarketOffersFilterPaymentMethodsLoaded />
|
||||||
|
</MarketOffersFilterPaymentMethodsBoot>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersFilterPaymentMethods";
|
@ -0,0 +1,65 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 type { TMarketOfferPaymentMethod } from "./_types";
|
||||||
|
import { MarketOffersPaymentMethodsTable } from ".";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "organisms/MarketOffersPaymentMethodsTable",
|
||||||
|
component: MarketOffersPaymentMethodsTable,
|
||||||
|
} as ComponentMeta<typeof MarketOffersPaymentMethodsTable>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof MarketOffersPaymentMethodsTable> = (
|
||||||
|
args
|
||||||
|
) => {
|
||||||
|
return <MarketOffersPaymentMethodsTable data={args.data} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
Default.args = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "USA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "ACH",
|
||||||
|
methodKey: "ach",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global (AUS, TRY, USD)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "Cash by mail",
|
||||||
|
methodKey: "cash-by-mail",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Spain",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "Domestic Wire Transfer",
|
||||||
|
methodKey: "domestic-wire-transfer",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global",
|
||||||
|
},
|
||||||
|
] as Array<TMarketOfferPaymentMethod>,
|
||||||
|
};
|
@ -0,0 +1,95 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { describe, expect, it } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import type { TMarketOfferPaymentMethod } from "./_types";
|
||||||
|
import { MarketOffersPaymentMethodsTable } from ".";
|
||||||
|
import { AppProviders } from "@atoms/AppProviders";
|
||||||
|
|
||||||
|
describe("molecules::MarketOffersPaymentMethodsTable", () => {
|
||||||
|
it("renders without exploding", () => {
|
||||||
|
const { asFragment, unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersPaymentMethodsTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders all columns", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersPaymentMethodsTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Method")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Rate Trade Limit")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Info")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders cells of method name ", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersPaymentMethodsTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Celpay")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("ACH")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders cells of rate trade limit ", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersPaymentMethodsTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("20 XMR")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("40 XMR")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders cells of rate trade limit ", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersPaymentMethodsTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("USA")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Global (AUS, TRY, USD)")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "USA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "ACH",
|
||||||
|
methodKey: "ach",
|
||||||
|
rateTradeLimit: 40,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global (AUS, TRY, USD)",
|
||||||
|
},
|
||||||
|
] as Array<TMarketOfferPaymentMethod>;
|
@ -0,0 +1,137 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createTable } from "@tanstack/react-table";
|
||||||
|
import { createStyles } from "@mantine/core";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import {
|
||||||
|
MarketOffersPaymentMethodsInfo,
|
||||||
|
MarketOffersPaymentMethodsLimit,
|
||||||
|
} from "./MarketOffersPaymentMethodsTableCells";
|
||||||
|
import type { TMarketOfferPaymentMethod } from "./_types";
|
||||||
|
import type { TableProps } from "@molecules/Table";
|
||||||
|
import { CheckboxCell, Table } from "@molecules/Table";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
|
||||||
|
const table = createTable().setRowType<TMarketOfferPaymentMethod>();
|
||||||
|
|
||||||
|
interface MarketOffersPaymentMethodsTableProps extends Partial<TableProps> {
|
||||||
|
data: Array<TMarketOfferPaymentMethod>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersPaymentMethodsTable({
|
||||||
|
data,
|
||||||
|
...rest
|
||||||
|
}: MarketOffersPaymentMethodsTableProps) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const columns = useMarketOffersPaymentMethodsColumns();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
{...rest}
|
||||||
|
table={table}
|
||||||
|
columns={columns}
|
||||||
|
data={data}
|
||||||
|
tableWrap={{
|
||||||
|
verticalSpacing: "xs",
|
||||||
|
striped: true,
|
||||||
|
className: classes.root,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useMarketOffersPaymentMethodsColumns = () => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return [
|
||||||
|
table.createDataColumn("methodChecked", {
|
||||||
|
id: "methodChecked",
|
||||||
|
header: " ",
|
||||||
|
cell: (props) => (
|
||||||
|
<CheckboxCell {...props} checkboxProps={{ radius: "xs", size: "sm" }} />
|
||||||
|
),
|
||||||
|
size: 30,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("methodName", {
|
||||||
|
id: "methodName",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketPaymentMethodColMethodName,
|
||||||
|
defaultMessage: "Method",
|
||||||
|
}),
|
||||||
|
size: 300,
|
||||||
|
}),
|
||||||
|
table.createDataColumn("rateTradeLimit", {
|
||||||
|
id: "rateTradeLimit",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketPaymentMethodColRateTradeLimit,
|
||||||
|
defaultMessage: "Rate Trade Limit",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<MarketOffersPaymentMethodsLimit row={row?.original} />
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
table.createDataColumn("info", {
|
||||||
|
id: "info",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketPaymentMethodColInfo,
|
||||||
|
defaultMessage: "Info",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
cell: ({ row }) => <MarketOffersPaymentMethodsInfo row={row?.original} />,
|
||||||
|
meta: { textAlign: "right" },
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
thead: {
|
||||||
|
tr: {
|
||||||
|
th: {
|
||||||
|
color: theme.colors.gray[9],
|
||||||
|
fontSize: theme.fontSizes.xs,
|
||||||
|
paddingBottom: 8,
|
||||||
|
paddingTop: 8,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
|
||||||
|
"&:first-of-type": {
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
"&:last-of-type": {
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tbody: {
|
||||||
|
tr: {
|
||||||
|
td: {
|
||||||
|
borderBottom: 0,
|
||||||
|
fontSize: theme.fontSizes.md,
|
||||||
|
|
||||||
|
"&:first-of-type": {
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
"&:last-of-type": {
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,44 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { Text } from "@mantine/core";
|
||||||
|
import type { TMarketOfferPaymentMethod } from "./_types";
|
||||||
|
import { Currency } from "@atoms/Currency";
|
||||||
|
|
||||||
|
export function MarketOffersPaymentMethodsLimit({
|
||||||
|
row,
|
||||||
|
}: {
|
||||||
|
row?: TMarketOfferPaymentMethod;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Text size="sm" color="gray">
|
||||||
|
<Currency value={row?.rateTradeLimit || 0} minimumFractionDigits={0} />{" "}
|
||||||
|
{row?.rateTradeLimitCurrency}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersPaymentMethodsInfo({
|
||||||
|
row,
|
||||||
|
}: {
|
||||||
|
row?: TMarketOfferPaymentMethod;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Text size="sm" color="gray">
|
||||||
|
{row?.info}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`molecules::MarketOffersPaymentMethodsTable > renders without exploding 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<table
|
||||||
|
class="mantine-Table-root __mantine-ref-striped mantine-Table-striped mantine-6859xh"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 30px;"
|
||||||
|
>
|
||||||
|
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 300px;"
|
||||||
|
>
|
||||||
|
Method
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
Rate Trade Limit
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
Info
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="width: 30px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Checkbox-root mantine-16ttirm"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-qbjb2s mantine-Checkbox-inner"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="mantine-1mvrtfa mantine-Checkbox-input"
|
||||||
|
id="methodChecked-0"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
class="__mantine-ref-icon mantine-180iq9w mantine-Checkbox-icon"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 10 7"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z"
|
||||||
|
fill="currentColor"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 300px;"
|
||||||
|
>
|
||||||
|
Celpay
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Text-root mantine-67rh7b"
|
||||||
|
>
|
||||||
|
20 XMR
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Text-root mantine-67rh7b"
|
||||||
|
>
|
||||||
|
USA
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="width: 30px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Checkbox-root mantine-16ttirm"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-qbjb2s mantine-Checkbox-inner"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="mantine-1mvrtfa mantine-Checkbox-input"
|
||||||
|
id="methodChecked-1"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
class="__mantine-ref-icon mantine-180iq9w mantine-Checkbox-icon"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 10 7"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z"
|
||||||
|
fill="currentColor"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 300px;"
|
||||||
|
>
|
||||||
|
ACH
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Text-root mantine-67rh7b"
|
||||||
|
>
|
||||||
|
40 XMR
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Text-root mantine-67rh7b"
|
||||||
|
>
|
||||||
|
Global (AUS, TRY, USD)
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
@ -0,0 +1,24 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export interface TMarketOfferPaymentMethod {
|
||||||
|
methodChecked?: boolean;
|
||||||
|
methodName: string;
|
||||||
|
methodKey: string;
|
||||||
|
rateTradeLimit: number;
|
||||||
|
rateTradeLimitCurrency: string;
|
||||||
|
info: string;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersPaymentMethodsTable";
|
||||||
|
export type { TMarketOfferPaymentMethod } from "./_types";
|
@ -0,0 +1,76 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useCallback } from "react";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import type { Row } from "@tanstack/react-table";
|
||||||
|
import type { TMarketTradingPairTable } from "@organisms/MarketOffersTradingPairTable";
|
||||||
|
import { MarketOffersTradingPairTable } from "@organisms/MarketOffersTradingPairTable";
|
||||||
|
import { useOffersFilterState } from "@src/state/offersFilter";
|
||||||
|
import { useMarketsPairs } from "@hooks/haveno/useMarketPairs";
|
||||||
|
|
||||||
|
interface MarketOffersTradingPairProps {
|
||||||
|
onSubmit?: (row: Row<TMarketTradingPairTable>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersTradingPair({
|
||||||
|
onSubmit,
|
||||||
|
}: MarketOffersTradingPairProps) {
|
||||||
|
return (
|
||||||
|
<MarketOffersTradingPairBoot>
|
||||||
|
<MarketOffersTradingPairLoaded onSubmit={onSubmit} />
|
||||||
|
</MarketOffersTradingPairBoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarketOffersTradingPairLoadedProps {
|
||||||
|
onSubmit?: (row: Row<TMarketTradingPairTable>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MarketOffersTradingPairLoaded({
|
||||||
|
onSubmit,
|
||||||
|
}: MarketOffersTradingPairLoadedProps) {
|
||||||
|
const { data: marketsPairs } = useMarketsPairs();
|
||||||
|
const [, setOffersState] = useOffersFilterState();
|
||||||
|
|
||||||
|
const handleRowClick = useCallback(
|
||||||
|
(row: Row<TMarketTradingPairTable>) => {
|
||||||
|
// Sync the selected pair to atom global filter state.
|
||||||
|
setOffersState((oldFilter) => ({
|
||||||
|
...oldFilter,
|
||||||
|
assetCode: row.original?.fromPair || "",
|
||||||
|
}));
|
||||||
|
onSubmit && onSubmit(row);
|
||||||
|
},
|
||||||
|
[onSubmit]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!marketsPairs) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<MarketOffersTradingPairTable
|
||||||
|
data={marketsPairs}
|
||||||
|
onRowClick={handleRowClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarketOffersTradingPairBoot: FC = ({ children }) => {
|
||||||
|
const { isLoading } = useMarketsPairs();
|
||||||
|
|
||||||
|
return isLoading ? <>Loading</> : <>{children}</>;
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersTradingPair";
|
@ -0,0 +1,69 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 } from "./MarketOffersTradingPairTable";
|
||||||
|
import type { TMarketOffersTradingPair } from "./_types";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "organisms/MarketOffersTradingPairTable",
|
||||||
|
component: MarketOffersTradingPairTable,
|
||||||
|
} as ComponentMeta<typeof MarketOffersTradingPairTable>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof MarketOffersTradingPairTable> = (
|
||||||
|
args
|
||||||
|
) => {
|
||||||
|
return <MarketOffersTradingPairTable data={args.data} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
|
||||||
|
Default.args = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
fromPair: "EUR",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 101.122,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 12.12,
|
||||||
|
dayChangeVolume: 1222.123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "EUR",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 101.122,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 12.12,
|
||||||
|
dayChangeVolume: 1222.123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "EUR",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 101.122,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 12.12,
|
||||||
|
dayChangeVolume: 1222.123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "EUR",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 101.122,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 12.12,
|
||||||
|
dayChangeVolume: 1222.123,
|
||||||
|
},
|
||||||
|
] as Array<TMarketOffersTradingPair>,
|
||||||
|
};
|
@ -0,0 +1,87 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { describe, expect, it } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import type { TMarketOffersTradingPair } from "./_types";
|
||||||
|
import { MarketOffersTradingPairTable } from "./MarketOffersTradingPairTable";
|
||||||
|
import { AppProviders } from "@atoms/AppProviders";
|
||||||
|
|
||||||
|
describe("molecules::MarketoffersTradingPairTable", () => {
|
||||||
|
it("renders without exploding", () => {
|
||||||
|
const { asFragment, unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersTradingPairTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders all columns", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersTradingPairTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("Pair")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Last Price")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("24th Change")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("24th Vol")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders Pair cells", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersTradingPairTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("EUR/XMR")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("USD/XMR")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders Day Change Volume cells", () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<AppProviders>
|
||||||
|
<MarketOffersTradingPairTable data={data} />
|
||||||
|
</AppProviders>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText("4,233.123")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("1,222.123")).toBeInTheDocument();
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
fromPair: "EUR",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 101.12,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 12.12,
|
||||||
|
dayChangeVolume: 4233.123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "USD",
|
||||||
|
toPair: "XMR",
|
||||||
|
lastPrice: 105.12,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 83.12,
|
||||||
|
dayChangeVolume: 1222.123,
|
||||||
|
},
|
||||||
|
] as Array<TMarketOffersTradingPair>;
|
@ -0,0 +1,143 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { useIntl } from "react-intl";
|
||||||
|
import { createStyles } from "@mantine/core";
|
||||||
|
import type { TMarketOffersTradingPair } from "./_types";
|
||||||
|
import { marketTradingPairTable } from "./_types";
|
||||||
|
import {
|
||||||
|
MarketOfferPair24thChange,
|
||||||
|
MarketOfferPairLastPriceCell,
|
||||||
|
MarketOfferPair24thChangeVolume,
|
||||||
|
} from "./MarketOffersTradingPairTableCells";
|
||||||
|
import { pairColumnAccessor } from "./_utils";
|
||||||
|
import type { TableProps } from "@molecules/Table";
|
||||||
|
import { Table } from "@molecules/Table";
|
||||||
|
import { LangKeys } from "@constants/lang";
|
||||||
|
|
||||||
|
export interface MarketOffersTradingPairTableProps extends Partial<TableProps> {
|
||||||
|
data: Array<TMarketOffersTradingPair>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOffersTradingPairTable({
|
||||||
|
data,
|
||||||
|
...rest
|
||||||
|
}: MarketOffersTradingPairTableProps) {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const columns = useMarketTradingPairsColumns();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
{...rest}
|
||||||
|
table={marketTradingPairTable}
|
||||||
|
columns={columns}
|
||||||
|
data={data}
|
||||||
|
tableWrap={{
|
||||||
|
verticalSpacing: "md",
|
||||||
|
highlightOnHover: true,
|
||||||
|
className: classes.root,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useMarketTradingPairsColumns = () => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return [
|
||||||
|
marketTradingPairTable.createDataColumn(pairColumnAccessor, {
|
||||||
|
id: "pair",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketTradingPairColPair,
|
||||||
|
defaultMessage: "Pair",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
}),
|
||||||
|
marketTradingPairTable.createDataColumn("lastPrice", {
|
||||||
|
id: "lastPrice",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketTradingPairColLastPrice,
|
||||||
|
defaultMessage: "Last Price",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
cell: ({ row }) => <MarketOfferPairLastPriceCell row={row?.original} />,
|
||||||
|
}),
|
||||||
|
marketTradingPairTable.createDataColumn("dayChangeRate", {
|
||||||
|
id: "dayChangeRate",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketTradingPairColDayChange,
|
||||||
|
defaultMessage: "24th Change",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
cell: () => <MarketOfferPair24thChange />,
|
||||||
|
meta: { textAlign: "right" },
|
||||||
|
}),
|
||||||
|
marketTradingPairTable.createDataColumn("dayChangeVolume", {
|
||||||
|
id: "dayChangeVolume",
|
||||||
|
header: formatMessage({
|
||||||
|
id: LangKeys.MarketTradingPairColDayChangeVolume,
|
||||||
|
defaultMessage: "24h Vol",
|
||||||
|
}),
|
||||||
|
size: 400,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<MarketOfferPair24thChangeVolume row={row?.original} />
|
||||||
|
),
|
||||||
|
meta: { textAlign: "right" },
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
paddingTop: 20,
|
||||||
|
paddingBottom: 0,
|
||||||
|
|
||||||
|
thead: {
|
||||||
|
tr: {
|
||||||
|
th: {
|
||||||
|
color: theme.colors.gray[9],
|
||||||
|
fontSize: theme.fontSizes.xs,
|
||||||
|
paddingTop: 8,
|
||||||
|
paddingBottom: 8,
|
||||||
|
textTransform: "uppercase",
|
||||||
|
|
||||||
|
"&:first-of-type": {
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
"&:last-of-type": {
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tbody: {
|
||||||
|
tr: {
|
||||||
|
td: {
|
||||||
|
borderBottomColor: "transparent",
|
||||||
|
fontSize: theme.spacing.sm * 1.168,
|
||||||
|
fontWeight: 600,
|
||||||
|
|
||||||
|
"&:first-of-type": {
|
||||||
|
paddingLeft: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
"&:last-of-type": {
|
||||||
|
paddingRight: theme.spacing.xl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,49 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { Box, Group } from "@mantine/core";
|
||||||
|
import type { TMarketOffersTradingPair } from "./_types";
|
||||||
|
import { AmountChange } from "@atoms/AmountChange/AmountChange";
|
||||||
|
import { Currency } from "@atoms/Currency";
|
||||||
|
|
||||||
|
export function MarketOfferPairLastPriceCell({
|
||||||
|
row,
|
||||||
|
}: {
|
||||||
|
row?: TMarketOffersTradingPair;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Group spacing="md">
|
||||||
|
<Box>{row?.lastPriceCurrency}</Box>
|
||||||
|
<Box>
|
||||||
|
<Currency value={row?.lastPrice || 0} minimumFractionDigits={0} />
|
||||||
|
</Box>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOfferPair24thChange() {
|
||||||
|
return <AmountChange positive={true}>+3,5%</AmountChange>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarketOfferPair24thChangeVolume({
|
||||||
|
row,
|
||||||
|
}: {
|
||||||
|
row?: TMarketOffersTradingPair;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Currency value={row?.dayChangeVolume || 0} minimumFractionDigits={0} />
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`molecules::MarketoffersTradingPairTable > renders without exploding 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<table
|
||||||
|
class="mantine-Table-root __mantine-ref-hover mantine-Table-hover mantine-1u8kzyn"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
Pair
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
Last Price
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
24th Change
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colspan="1"
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
24th Vol
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
EUR/XMR
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-root mantine-6y1794"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-child mantine-1oprzqz"
|
||||||
|
>
|
||||||
|
EUR
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-child mantine-1oprzqz"
|
||||||
|
>
|
||||||
|
101.12
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-ozfbol"
|
||||||
|
>
|
||||||
|
+3,5%
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
4,233.123
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
USD/XMR
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-root mantine-6y1794"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-child mantine-1oprzqz"
|
||||||
|
>
|
||||||
|
EUR
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mantine-Group-child mantine-1oprzqz"
|
||||||
|
>
|
||||||
|
105.12
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mantine-ozfbol"
|
||||||
|
>
|
||||||
|
+3,5%
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style="width: 400px; text-align: right;"
|
||||||
|
>
|
||||||
|
1,222.123
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
@ -0,0 +1,31 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createTable } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
export interface TMarketOffersTradingPair {
|
||||||
|
fromPair: string;
|
||||||
|
toPair: string;
|
||||||
|
lastPrice: number;
|
||||||
|
lastPriceCurrency: string;
|
||||||
|
dayChangeRate: number;
|
||||||
|
dayChangeVolume: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const marketTradingPairTable =
|
||||||
|
createTable().setRowType<TMarketOffersTradingPair>();
|
||||||
|
|
||||||
|
export type TMarketTradingPairTable = typeof marketTradingPairTable.generics;
|
@ -0,0 +1,20 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { TMarketOffersTradingPair } from "./_types";
|
||||||
|
|
||||||
|
export const pairColumnAccessor = (row: TMarketOffersTradingPair): string =>
|
||||||
|
`${row.fromPair}/${row.toPair}`;
|
@ -0,0 +1,21 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
export * from "./MarketOffersTradingPairTable";
|
||||||
|
export type {
|
||||||
|
TMarketOffersTradingPair,
|
||||||
|
TMarketTradingPairTable,
|
||||||
|
} from "./_types";
|
@ -33,7 +33,7 @@ export const NAV_LINKS = [
|
|||||||
{
|
{
|
||||||
icon: <MarketsIcon />,
|
icon: <MarketsIcon />,
|
||||||
label: "Markets",
|
label: "Markets",
|
||||||
link: "/markets",
|
route: "/markets",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <OffersIcon />,
|
icon: <OffersIcon />,
|
||||||
|
@ -99,11 +99,53 @@ export enum LangKeys {
|
|||||||
AccountBackupRestoreBtn = "account.backup.restore.btn",
|
AccountBackupRestoreBtn = "account.backup.restore.btn",
|
||||||
AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification",
|
AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification",
|
||||||
AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification",
|
AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification",
|
||||||
|
|
||||||
MarketsOffersColumnPrice = "marketsOffers.columnPrice",
|
MarketsOffersColumnPrice = "marketsOffers.columnPrice",
|
||||||
MarketsOffersColumnAmount = "marketsOffers.columnAmount",
|
MarketsOffersColumnAmount = "marketsOffers.columnAmount",
|
||||||
MarketsOffersColumnCost = "marketsOffers.columnCost",
|
MarketsOffersColumnCost = "marketsOffers.columnCost",
|
||||||
MarketsOffersColumnPaymentMethod = "marketsOffers.columnPaymentMethod",
|
MarketsOffersColumnPaymentMethod = "marketsOffers.columnPaymentMethod",
|
||||||
MarketsOffersColumnAccountAge = "marketsOffers.columnAccountAge",
|
MarketsOffersColumnAccountAge = "marketsOffers.columnAccountAge",
|
||||||
MarketsOffersColumnAccountTrades = "marketsOffers.columnAccountTypes",
|
MarketsOffersColumnAccountTrades = "marketsOffers.columnAccountTypes",
|
||||||
|
MarketsTransactionsColumnPrice = "marketsTransactions.columnPrice",
|
||||||
|
MarketsTransactionsColumnAmount = "marketsTransactions.columnAmount",
|
||||||
|
MarketsTransactionsColumnCost = "marketsTransactions.columnCost",
|
||||||
|
MarketsTransactionsColumnPaymentMethod = "marketsTransactions.columnPaymentMethod",
|
||||||
|
MarketsTransactionsColumnAccountAge = "marketsTransactions.columnAccountAge",
|
||||||
|
MarketsTransactionsColumnAccountTrades = "marketsTransactions.columnAccountTypes",
|
||||||
|
MarketsTransactionsCashByMail = "marketsTransactions.cashByMail",
|
||||||
|
MarketOffersAmount = "marketOffers.filter.amount",
|
||||||
|
MarketOffersWith = "marketOffers.filter.with",
|
||||||
|
MarketOffersSwitchSell = "marketOffers.filter.switchSellBuy.sell",
|
||||||
|
MarketOffersSwitchBuy = "marketOffers.filter.switchSellBuy.buy",
|
||||||
|
MarketOffersPaymentMethod = "marketOffers.filter.paymentMethod",
|
||||||
|
MarketOffersAccountDetails = "marketOffers.filter.accountDetails",
|
||||||
|
MarketOffersShowMarketDepth = "marketOffers.filter.showMarketDepth",
|
||||||
|
MarketOffersHideMarketDepth = "marketOffers.filter.hideMarketDepth",
|
||||||
|
MarketOffersCreateOffer = "marketOffers.filter.createOffer",
|
||||||
|
MarketOffersCurrency = "marketOffers.filter.currency",
|
||||||
|
MarketOffersSigned = "marketOffers.filter.signed",
|
||||||
|
MarketOffersTradesAmount = "marketOffers.filter.tradesAmount",
|
||||||
|
MarketOffersDaysAge = "marketOffers.filter.daysAge",
|
||||||
|
MarketPaymentMethodColMethodName = "marketOffers.filter.paymentMethodColMethodName",
|
||||||
|
MarketPaymentMethodColRateTradeLimit = "marketOffers.filter.paymentMethodColRateTradeLimit",
|
||||||
|
MarketPaymentMethodColInfo = "marketOffers.filter.paymentMethodColInfo",
|
||||||
|
MarketTradingPairColPair = "marketOffers.tradingPairFilter.pairColumn",
|
||||||
|
MarketTradingPairColLastPrice = "marketOffers.tradingPairFilter.lastPriceColumn",
|
||||||
|
MarketTradingPairColDayChange = "marketOffers.tradingPairFilter.dayChangeColumn",
|
||||||
|
MarketTradingPairColDayChangeVolume = "marketOffers.tradingPairFilter.dayChangeVolumColumn",
|
||||||
|
MarketFilterAccountLabelSignedAccounts = "marketFilters.accountFilter.labelSignedAccounts",
|
||||||
|
MarketFilterAccountDescSignedAccounts = "marketFilters.accountFilter.descSignedAccounts",
|
||||||
|
MarketFilterAccountLabelMinAccountAge = "marketFilters.accountFilter.labelMinAccountAge",
|
||||||
|
MarketFilterAccountDescMinAccountAge = "marketFilters.accountFilter.descMinAccountAge",
|
||||||
|
MarketFilterAccountLabelAmountTrades = "marketFilters.accountFilter.labelAmountTrades",
|
||||||
|
MarketFilterAccountDescAmountTrades = "marketFilters.accountFilter.descAmountTrades",
|
||||||
|
MarketFilterAccountTrades = "marketFilters.accountFilter.trades",
|
||||||
|
MarketFilterAccountDays = "marketFilters.accountFilter.days",
|
||||||
|
MarketFilterAccountClearFiltersBtn = "marketFilters.accountFilter.clearFiltersBtn",
|
||||||
|
MarketFilterAccountSaveBtn = "marketFilters.accountFilter.saveBtn",
|
||||||
|
MarketAmountFilterFieldMinAmountTrades = "MarketFilterFieldMinAmountTrades",
|
||||||
|
MarketAmountFilterFieldMinAmountTradesDesc = "MarketFilterFieldMinAmountTradesDesc",
|
||||||
|
MarketAmountFilterFieldMaxAmount = "MarketFilterFieldMaxAmount",
|
||||||
|
MarketAmountFilterFieldMaxAmountDesc = "MarketFilterFieldMaxAmountDesc",
|
||||||
|
MarketAmountFilterAmountClearFiltersBtn = "MarketFilterAmountClearFiltersBtn",
|
||||||
|
MarketAmountFilterAmountSaveBtn = "MarketFilterAmountSaveBtn",
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,54 @@ const LangPackEN: { [key in LangKeys]: string } = {
|
|||||||
[LangKeys.MarketsOffersColumnAccountAge]: "Account Age",
|
[LangKeys.MarketsOffersColumnAccountAge]: "Account Age",
|
||||||
[LangKeys.MarketsOffersColumnAccountTrades]: "Account Trades",
|
[LangKeys.MarketsOffersColumnAccountTrades]: "Account Trades",
|
||||||
[LangKeys.MarketsOffersColumnPaymentMethod]: "Payment Method",
|
[LangKeys.MarketsOffersColumnPaymentMethod]: "Payment Method",
|
||||||
|
[LangKeys.MarketsTransactionsColumnPrice]: "Price",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAmount]: "Amount",
|
||||||
|
[LangKeys.MarketsTransactionsColumnCost]: "Costs",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAccountAge]: "Account Age",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAccountTrades]: "Account Trades",
|
||||||
|
[LangKeys.MarketsTransactionsColumnPaymentMethod]: "Payment Method",
|
||||||
|
[LangKeys.MarketsTransactionsCashByMail]: "Cash by mail",
|
||||||
|
[LangKeys.MarketOffersAmount]: "Amount",
|
||||||
|
[LangKeys.MarketOffersWith]: "with",
|
||||||
|
[LangKeys.MarketOffersSwitchSell]: "Sell {currency}",
|
||||||
|
[LangKeys.MarketOffersSwitchBuy]: "Buy {currency}",
|
||||||
|
[LangKeys.MarketOffersPaymentMethod]: "Payment method",
|
||||||
|
[LangKeys.MarketOffersAccountDetails]: "Account details",
|
||||||
|
[LangKeys.MarketOffersShowMarketDepth]: "Show market depth",
|
||||||
|
[LangKeys.MarketOffersHideMarketDepth]: "Hide market depth",
|
||||||
|
[LangKeys.MarketOffersCreateOffer]: "Create offer",
|
||||||
|
[LangKeys.MarketOffersCurrency]: "Currency",
|
||||||
|
[LangKeys.MarketOffersSigned]: "Signed",
|
||||||
|
[LangKeys.MarketOffersTradesAmount]: ">{value} trades",
|
||||||
|
[LangKeys.MarketOffersDaysAge]: ">{value} days",
|
||||||
|
[LangKeys.MarketPaymentMethodColMethodName]: "Method",
|
||||||
|
[LangKeys.MarketPaymentMethodColRateTradeLimit]: "Rate Trade Limit",
|
||||||
|
[LangKeys.MarketPaymentMethodColInfo]: "Info",
|
||||||
|
[LangKeys.MarketTradingPairColPair]: "Pair",
|
||||||
|
[LangKeys.MarketTradingPairColLastPrice]: "Last Price",
|
||||||
|
[LangKeys.MarketTradingPairColDayChange]: "24th Change",
|
||||||
|
[LangKeys.MarketTradingPairColDayChangeVolume]: "24th Vol",
|
||||||
|
[LangKeys.MarketFilterAccountLabelSignedAccounts]: "Signed accounts",
|
||||||
|
[LangKeys.MarketFilterAccountDescSignedAccounts]:
|
||||||
|
"Only show accounts that have been signed. Please be aware that new accounts need to get the chance to get signed.",
|
||||||
|
[LangKeys.MarketFilterAccountLabelMinAccountAge]: "Minimum account age",
|
||||||
|
[LangKeys.MarketFilterAccountDescMinAccountAge]:
|
||||||
|
"Only show trade offers with a minimum account age.",
|
||||||
|
[LangKeys.MarketFilterAccountLabelAmountTrades]: "Minimum amount of trades",
|
||||||
|
[LangKeys.MarketFilterAccountDescAmountTrades]:
|
||||||
|
"Only show trade offers from accounts with a minimum amount of completed trades",
|
||||||
|
[LangKeys.MarketFilterAccountTrades]: "Trades",
|
||||||
|
[LangKeys.MarketFilterAccountDays]: "Days",
|
||||||
|
[LangKeys.MarketFilterAccountClearFiltersBtn]: "Clear filters",
|
||||||
|
[LangKeys.MarketFilterAccountSaveBtn]: "Save filters",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMinAmountTrades]: "Minimum amount of trades",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMinAmountTradesDesc]:
|
||||||
|
"Set the minimum amount you want to buy.",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMaxAmount]: "Maximum amount",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMaxAmountDesc]:
|
||||||
|
"Set the maximum amount you want to buy.",
|
||||||
|
[LangKeys.MarketAmountFilterAmountClearFiltersBtn]: "Clear filters",
|
||||||
|
[LangKeys.MarketAmountFilterAmountSaveBtn]: "Save filters",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LangPackEN;
|
export default LangPackEN;
|
||||||
|
@ -125,6 +125,56 @@ const LangPackES: { [key in LangKeys]: string } = {
|
|||||||
[LangKeys.MarketsOffersColumnAccountAge]: "Edad de la cuenta",
|
[LangKeys.MarketsOffersColumnAccountAge]: "Edad de la cuenta",
|
||||||
[LangKeys.MarketsOffersColumnAccountTrades]: "Operaciones de cuenta",
|
[LangKeys.MarketsOffersColumnAccountTrades]: "Operaciones de cuenta",
|
||||||
[LangKeys.MarketsOffersColumnPaymentMethod]: "Método de pago",
|
[LangKeys.MarketsOffersColumnPaymentMethod]: "Método de pago",
|
||||||
|
[LangKeys.MarketsTransactionsColumnPrice]: "Precio",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAmount]: "Monto",
|
||||||
|
[LangKeys.MarketsTransactionsColumnCost]: "Costos",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAccountAge]: "Edad de la cuenta",
|
||||||
|
[LangKeys.MarketsTransactionsColumnAccountTrades]: "Operaciones de cuenta",
|
||||||
|
[LangKeys.MarketsTransactionsColumnPaymentMethod]: "Método de pago",
|
||||||
|
[LangKeys.MarketsTransactionsCashByMail]: "Cash by mail",
|
||||||
|
[LangKeys.MarketOffersAmount]: "Monto",
|
||||||
|
[LangKeys.MarketOffersWith]: "con",
|
||||||
|
[LangKeys.MarketOffersSwitchSell]: "Vender {currency}",
|
||||||
|
[LangKeys.MarketOffersSwitchBuy]: "Comprar {currency}",
|
||||||
|
[LangKeys.MarketOffersPaymentMethod]: "Método de pago",
|
||||||
|
[LangKeys.MarketOffersAccountDetails]: "Detalles de la cuenta",
|
||||||
|
[LangKeys.MarketOffersShowMarketDepth]: "Mostrar profundidad de mercado",
|
||||||
|
[LangKeys.MarketOffersHideMarketDepth]: "Ocultar profundidad de mercado",
|
||||||
|
[LangKeys.MarketOffersCreateOffer]: "Crear oferta",
|
||||||
|
[LangKeys.MarketOffersCurrency]: "Divisa",
|
||||||
|
[LangKeys.MarketOffersSigned]: "Firmada",
|
||||||
|
[LangKeys.MarketOffersTradesAmount]: ">{value} vientos alisios",
|
||||||
|
[LangKeys.MarketOffersDaysAge]: ">{value} días",
|
||||||
|
[LangKeys.MarketPaymentMethodColMethodName]: "Método",
|
||||||
|
[LangKeys.MarketPaymentMethodColRateTradeLimit]: "Límite comercial de tasa",
|
||||||
|
[LangKeys.MarketPaymentMethodColInfo]: "Información",
|
||||||
|
[LangKeys.MarketTradingPairColPair]: "Par",
|
||||||
|
[LangKeys.MarketTradingPairColLastPrice]: "Last Price",
|
||||||
|
[LangKeys.MarketTradingPairColDayChange]: "Cambio 24",
|
||||||
|
[LangKeys.MarketTradingPairColDayChangeVolume]: "Vol. 24",
|
||||||
|
[LangKeys.MarketFilterAccountLabelSignedAccounts]: "cuentas firmadas",
|
||||||
|
[LangKeys.MarketFilterAccountDescSignedAccounts]:
|
||||||
|
"Solo mostrar cuentas que hayan sido firmadas. Tenga en cuenta que las cuentas nuevas deben tener la oportunidad de ser firmadas.",
|
||||||
|
[LangKeys.MarketFilterAccountLabelMinAccountAge]: "Edad mínima de la cuenta",
|
||||||
|
[LangKeys.MarketFilterAccountDescMinAccountAge]:
|
||||||
|
"Mostrar solo ofertas comerciales con una edad mínima de cuenta.",
|
||||||
|
[LangKeys.MarketFilterAccountLabelAmountTrades]:
|
||||||
|
"Cantidad mínima de operaciones",
|
||||||
|
[LangKeys.MarketFilterAccountDescAmountTrades]:
|
||||||
|
"Mostrar solo ofertas comerciales de cuentas con una cantidad mínima de operaciones completadas",
|
||||||
|
[LangKeys.MarketFilterAccountTrades]: "Vientos alisios",
|
||||||
|
[LangKeys.MarketFilterAccountDays]: "Días",
|
||||||
|
[LangKeys.MarketFilterAccountClearFiltersBtn]: "Borrar filtros",
|
||||||
|
[LangKeys.MarketFilterAccountSaveBtn]: "Guardar filtros",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMinAmountTrades]:
|
||||||
|
"Cantidad mínima de operaciones",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMinAmountTradesDesc]:
|
||||||
|
"Establece la cantidad mínima que deseas comprar.",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMaxAmount]: "Importe máximo",
|
||||||
|
[LangKeys.MarketAmountFilterFieldMaxAmountDesc]:
|
||||||
|
"Establece la cantidad máxima que deseas comprar.",
|
||||||
|
[LangKeys.MarketAmountFilterAmountClearFiltersBtn]: "Borrar filtros",
|
||||||
|
[LangKeys.MarketAmountFilterAmountSaveBtn]: "Guardar filtros",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LangPackES;
|
export default LangPackES;
|
||||||
|
@ -31,6 +31,8 @@ export enum QueryKeys {
|
|||||||
XmrTxs = "Haveno.XmrTransactions",
|
XmrTxs = "Haveno.XmrTransactions",
|
||||||
MarketsOffers = "Haveno.MarketsOffers",
|
MarketsOffers = "Haveno.MarketsOffers",
|
||||||
MyOffers = "Haveno.MyOffers",
|
MyOffers = "Haveno.MyOffers",
|
||||||
|
PaymentMethods = "Haveno.PaymentMethods",
|
||||||
|
MarketsPairs = "Haveno.MarketsPairs",
|
||||||
|
|
||||||
// Storage
|
// Storage
|
||||||
StorageAccountInfo = "Storage.AccountInfo",
|
StorageAccountInfo = "Storage.AccountInfo",
|
||||||
|
70
packages/renderer/src/hooks/haveno/useMarketPairs.ts
Normal file
70
packages/renderer/src/hooks/haveno/useMarketPairs.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { QueryKeys } from "@constants/query-keys";
|
||||||
|
|
||||||
|
interface MarketsPairsData {
|
||||||
|
fromPair: string;
|
||||||
|
toPair: string;
|
||||||
|
lastPrice: number;
|
||||||
|
lastPriceCurrency: string;
|
||||||
|
dayChangeRate: number;
|
||||||
|
dayChangeVolume: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMarketsPairs() {
|
||||||
|
return useQuery<Array<MarketsPairsData>, Error>(
|
||||||
|
[QueryKeys.MarketsPairs],
|
||||||
|
// TODO: replace with actual implementation once haveno-ts is feature complete.
|
||||||
|
() => Promise.resolve(data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
fromPair: "XMR",
|
||||||
|
toPair: "USD",
|
||||||
|
lastPrice: 246.23,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 0.2,
|
||||||
|
dayChangeVolume: 0.2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "XMR",
|
||||||
|
toPair: "USD",
|
||||||
|
lastPrice: 246.23,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 0.2,
|
||||||
|
dayChangeVolume: 0.2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "XMR",
|
||||||
|
toPair: "USD",
|
||||||
|
lastPrice: 246.23,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 0.2,
|
||||||
|
dayChangeVolume: 0.2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fromPair: "XMR",
|
||||||
|
toPair: "USD",
|
||||||
|
lastPrice: 246.23,
|
||||||
|
lastPriceCurrency: "EUR",
|
||||||
|
dayChangeRate: 0.2,
|
||||||
|
dayChangeVolume: 0.2,
|
||||||
|
},
|
||||||
|
] as Array<MarketsPairsData>;
|
69
packages/renderer/src/hooks/haveno/usePaymentMethods.ts
Normal file
69
packages/renderer/src/hooks/haveno/usePaymentMethods.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { QueryKeys } from "@constants/query-keys";
|
||||||
|
|
||||||
|
interface PaymentMethodsQuery {
|
||||||
|
assetCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaymentMethodsValues {
|
||||||
|
methodName: string;
|
||||||
|
methodKey: string;
|
||||||
|
rateTradeLimit: number;
|
||||||
|
rateTradeLimitCurrency: string;
|
||||||
|
info: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePaymentMethods(query?: PaymentMethodsQuery) {
|
||||||
|
return useQuery<Array<PaymentMethodsValues>, Error>(
|
||||||
|
[QueryKeys.PaymentAccounts, query],
|
||||||
|
// TODO: replace with actual implementation once haveno-ts is feature complete.
|
||||||
|
() => Promise.resolve(data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay2",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay3",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
methodName: "Celpay",
|
||||||
|
methodKey: "celpay4",
|
||||||
|
rateTradeLimit: 20,
|
||||||
|
rateTradeLimitCurrency: "XMR",
|
||||||
|
info: "Global",
|
||||||
|
},
|
||||||
|
] as Array<PaymentMethodsValues>;
|
@ -14,16 +14,20 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
import { createStyles } from "@mantine/core";
|
import { createStyles, Stack } from "@mantine/core";
|
||||||
import { NavbarLayout } from "@templates/NavbarLayout";
|
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||||
import { MarketsOffers } from "@organisms/MarketsOffers";
|
import { MarketsOffers } from "@organisms/MarketsOffers";
|
||||||
|
import { MarketOffersFilterBar } from "@organisms/MarketOffersFilterBar";
|
||||||
|
|
||||||
export function MarketsOffersPage() {
|
export function MarketsOffersPage() {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavbarLayout classNames={{ contentArea: classes.contentArea }}>
|
<NavbarLayout classNames={{ contentArea: classes.contentArea }}>
|
||||||
|
<Stack spacing={0} className={classes.innerContent}>
|
||||||
|
<MarketOffersFilterBar />
|
||||||
<MarketsOffers />
|
<MarketsOffers />
|
||||||
|
</Stack>
|
||||||
</NavbarLayout>
|
</NavbarLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -32,4 +36,7 @@ const useStyles = createStyles(() => ({
|
|||||||
contentArea: {
|
contentArea: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
},
|
},
|
||||||
|
innerContent: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
40
packages/renderer/src/pages/Markets/MarketsTransactions.tsx
Normal file
40
packages/renderer/src/pages/Markets/MarketsTransactions.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 { createStyles, Stack } from "@mantine/core";
|
||||||
|
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||||
|
import { MarketOffersFilterBar } from "@organisms/MarketOffersFilterBar";
|
||||||
|
|
||||||
|
export function MarketsTransactionsPage() {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavbarLayout classNames={{ contentArea: classes.contentArea }}>
|
||||||
|
<Stack spacing={0} className={classes.innerContentArea}>
|
||||||
|
<MarketOffersFilterBar />
|
||||||
|
</Stack>
|
||||||
|
</NavbarLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(() => ({
|
||||||
|
contentArea: {
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
innerContentArea: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
}));
|
52
packages/renderer/src/state/offersFilter.ts
Normal file
52
packages/renderer/src/state/offersFilter.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 {
|
||||||
|
atom,
|
||||||
|
useRecoilState,
|
||||||
|
useRecoilValue,
|
||||||
|
useSetRecoilState,
|
||||||
|
} from "recoil";
|
||||||
|
|
||||||
|
export type OfferFilter = {
|
||||||
|
direction: string;
|
||||||
|
assetCode: string;
|
||||||
|
minimumCryptoAmount?: number | null;
|
||||||
|
maximumCryptoAmount?: number | null;
|
||||||
|
minimumBaseCurrencyAmount?: number | null;
|
||||||
|
maximumBaseCurrencyAmount?: number | null;
|
||||||
|
paymentMethods?: Array<string>;
|
||||||
|
signedAccounts?: boolean;
|
||||||
|
minimumAccountAge?: number | null;
|
||||||
|
minimumTradesAmount?: number | null;
|
||||||
|
};
|
||||||
|
const defaultValue = {
|
||||||
|
direction: "sell",
|
||||||
|
assetCode: "BTC",
|
||||||
|
};
|
||||||
|
export const offersFilterAtom = atom<OfferFilter>({
|
||||||
|
key: "offersFilter",
|
||||||
|
default: defaultValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useGetOffersFilterState = () =>
|
||||||
|
useRecoilValue<OfferFilter>(offersFilterAtom);
|
||||||
|
|
||||||
|
export const useSetOffersFilterState = () =>
|
||||||
|
useSetRecoilState<OfferFilter>(offersFilterAtom);
|
||||||
|
|
||||||
|
export const useOffersFilterState = () =>
|
||||||
|
useRecoilState<OfferFilter>(offersFilterAtom);
|
36
packages/renderer/src/utils/misc.ts
Normal file
36
packages/renderer/src/utils/misc.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// =============================================================================
|
||||||
|
// 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 _ from "lodash";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the form fields and removes fields that out of form from
|
||||||
|
* the given intial values, and removes previously unfilled optional values
|
||||||
|
* come as `null` as well.
|
||||||
|
*
|
||||||
|
* @param {Record<string, unknown>} param - Form values.
|
||||||
|
* @param {Record<string, unknown>} initialValues - Form initial values.
|
||||||
|
* @return {Record<string, unknown>}
|
||||||
|
*/
|
||||||
|
export const transformToForm = (
|
||||||
|
obj: Record<string, unknown>,
|
||||||
|
initialValues: Record<string, unknown>
|
||||||
|
): Record<string, unknown> => {
|
||||||
|
return _.pickBy(
|
||||||
|
obj,
|
||||||
|
(val, key) => val !== null && Object.keys(initialValues).includes(key)
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user