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.Welcome} element={<Welcome />} />
|
||||
<Route path={ROUTES.CreateAccount} element={<CreateAccount />} />
|
||||
<Route path={ROUTES.Markets} element={<MarketsOffersPage />} />
|
||||
<Route
|
||||
path={ROUTES.Markets}
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<MarketsOffersPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={ROUTES.MyWallet}
|
||||
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.
|
||||
// =============================================================================
|
||||
|
||||
import { createStyles, TextInput as MTextInput } from "@mantine/core";
|
||||
import type { TextInputProps as MTextInputProps } from "@mantine/core";
|
||||
import type {
|
||||
TextInputProps as MTextInputProps,
|
||||
NumberInputProps as MNumberInputProps,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
createStyles,
|
||||
TextInput as MTextInput,
|
||||
NumberInput as MNumberInput,
|
||||
} from "@mantine/core";
|
||||
|
||||
interface TextInputProps extends MTextInputProps {
|
||||
id: string;
|
||||
@ -24,9 +31,21 @@ interface TextInputProps extends MTextInputProps {
|
||||
export function TextInput(props: TextInputProps) {
|
||||
const { id, ...rest } = props;
|
||||
const { classes } = useStyles();
|
||||
|
||||
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) => ({
|
||||
label: {
|
||||
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 { TableBody } from "./TableBody";
|
||||
import { useStyles } from "./Table.style";
|
||||
import { updateTableCell } from "./_utils";
|
||||
|
||||
export function Table(props: TableProps) {
|
||||
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, {
|
||||
data,
|
||||
@ -37,6 +47,17 @@ export function Table(props: TableProps) {
|
||||
state,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getExpandedRowModel: getExpandedRowModel(),
|
||||
meta: {
|
||||
updateData: (rowIndex: number, columnId: string, value: unknown) => {
|
||||
const newData = updateTableCell(data, rowIndex, columnId, value);
|
||||
onEditableDataChange && onEditableDataChange(newData);
|
||||
},
|
||||
},
|
||||
...(defaultColumn
|
||||
? {
|
||||
defaultColumn,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -20,7 +20,7 @@ import { useTableContext } from "./use-table-context";
|
||||
export function TableBody() {
|
||||
const {
|
||||
table,
|
||||
props: { rowSubComponent },
|
||||
props: { rowSubComponent, onRowClick },
|
||||
} = useTableContext();
|
||||
|
||||
return (
|
||||
@ -31,6 +31,7 @@ export function TableBody() {
|
||||
key={row.id}
|
||||
onClick={() => {
|
||||
row.toggleExpanded();
|
||||
onRowClick && onRowClick(row);
|
||||
}}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
|
@ -18,11 +18,13 @@
|
||||
import type { ColumnDef, Row, TableState } from "@tanstack/react-table";
|
||||
import type { TableProps as MTableProps } from "@mantine/core";
|
||||
|
||||
// TODO: Add type or generic
|
||||
export interface TableProps {
|
||||
columns: Array<ColumnDef<any>>;
|
||||
table: any;
|
||||
data: Array<any>;
|
||||
state?: Partial<TableState>;
|
||||
defaultColumn?: any;
|
||||
|
||||
showHeader?: boolean;
|
||||
showFooter?: boolean;
|
||||
@ -31,6 +33,11 @@ export interface TableProps {
|
||||
|
||||
tableWrap?: MTableProps;
|
||||
variant?: TableVariant;
|
||||
|
||||
onEditableDataChange?: (v: Array<any>) => void;
|
||||
|
||||
pointerRow?: boolean;
|
||||
onRowClick?: (column: any) => void;
|
||||
}
|
||||
|
||||
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.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./_types";
|
||||
export * from "./Table";
|
||||
export * from "./cells";
|
||||
|
@ -20,6 +20,7 @@ import type { TableInstance } from "@tanstack/react-table";
|
||||
import type { TableProps } from "../_types";
|
||||
|
||||
interface TableContextValue {
|
||||
// TODO: Add type or generic
|
||||
table: TableInstance<any>;
|
||||
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 />,
|
||||
label: "Markets",
|
||||
link: "/markets",
|
||||
route: "/markets",
|
||||
},
|
||||
{
|
||||
icon: <OffersIcon />,
|
||||
|
@ -99,11 +99,53 @@ export enum LangKeys {
|
||||
AccountBackupRestoreBtn = "account.backup.restore.btn",
|
||||
AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification",
|
||||
AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification",
|
||||
|
||||
MarketsOffersColumnPrice = "marketsOffers.columnPrice",
|
||||
MarketsOffersColumnAmount = "marketsOffers.columnAmount",
|
||||
MarketsOffersColumnCost = "marketsOffers.columnCost",
|
||||
MarketsOffersColumnPaymentMethod = "marketsOffers.columnPaymentMethod",
|
||||
MarketsOffersColumnAccountAge = "marketsOffers.columnAccountAge",
|
||||
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.MarketsOffersColumnAccountTrades]: "Account Trades",
|
||||
[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;
|
||||
|
@ -125,6 +125,56 @@ const LangPackES: { [key in LangKeys]: string } = {
|
||||
[LangKeys.MarketsOffersColumnAccountAge]: "Edad de la cuenta",
|
||||
[LangKeys.MarketsOffersColumnAccountTrades]: "Operaciones de cuenta",
|
||||
[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;
|
||||
|
@ -31,6 +31,8 @@ export enum QueryKeys {
|
||||
XmrTxs = "Haveno.XmrTransactions",
|
||||
MarketsOffers = "Haveno.MarketsOffers",
|
||||
MyOffers = "Haveno.MyOffers",
|
||||
PaymentMethods = "Haveno.PaymentMethods",
|
||||
MarketsPairs = "Haveno.MarketsPairs",
|
||||
|
||||
// Storage
|
||||
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.
|
||||
// =============================================================================
|
||||
|
||||
import { createStyles } from "@mantine/core";
|
||||
import { createStyles, Stack } from "@mantine/core";
|
||||
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||
import { MarketsOffers } from "@organisms/MarketsOffers";
|
||||
import { MarketOffersFilterBar } from "@organisms/MarketOffersFilterBar";
|
||||
|
||||
export function MarketsOffersPage() {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
<NavbarLayout classNames={{ contentArea: classes.contentArea }}>
|
||||
<MarketsOffers />
|
||||
<Stack spacing={0} className={classes.innerContent}>
|
||||
<MarketOffersFilterBar />
|
||||
<MarketsOffers />
|
||||
</Stack>
|
||||
</NavbarLayout>
|
||||
);
|
||||
}
|
||||
@ -32,4 +36,7 @@ const useStyles = createStyles(() => ({
|
||||
contentArea: {
|
||||
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