chore: UI elements for Accounts screen

- Typography
- TextInput, Select
- AddPaymentMethod
- supported currencies and payment methods
- theme updates
This commit is contained in:
Subir 2022-05-06 01:49:37 +05:30
parent b029419e7f
commit 5460d3afae
No known key found for this signature in database
GPG key ID: 2D633D8047FD3FF0
61 changed files with 2427 additions and 70 deletions

View file

@ -17,6 +17,7 @@
import { Routes, Route } from "react-router-dom";
import { Home, Welcome } from "@pages/Onboarding";
import { Wallet } from "@pages/Wallet";
import { PaymentMethods, AddPaymentMethod } from "@pages/Account";
export const ROUTES = {
Home: "/",
@ -24,6 +25,8 @@ export const ROUTES = {
RestoreBackup: "/onboarding/restore-backup",
SetupAccount: "/onboarding/setup",
Wallet: "/wallet",
AccountPaymentMethods: "/account/payment-methods",
AccountAddPaymentMethod: "/account/payment-methods/add",
};
export function AppRoutes() {
@ -32,6 +35,11 @@ export function AppRoutes() {
<Route path={ROUTES.Home} element={<Home />} />
<Route path={ROUTES.Welcome} element={<Welcome />} />
<Route path={ROUTES.Wallet} element={<Wallet />} />
<Route path={ROUTES.AccountPaymentMethods} element={<PaymentMethods />} />
<Route
path={ROUTES.AccountAddPaymentMethod}
element={<AddPaymentMethod />}
/>
</Routes>
);
}

View file

@ -14,20 +14,17 @@
// limitations under the License.
// =============================================================================
import { Box, createStyles, keyframes, Stack, Text } from "@mantine/core";
import { FormattedMessage } from "react-intl";
import { Box, createStyles, keyframes, Stack } from "@mantine/core";
import { LangKeys } from "@constants/lang/LangKeys";
import { BodyText } from "@atoms/Typography";
export function ConnectionProgress() {
const { classes } = useStyles();
return (
<Stack align="center" justify="center">
<Text size="sm">
<FormattedMessage
id={LangKeys.ConnectingToNetwork}
defaultMessage="Connecting to Monero Network"
/>
</Text>
<BodyText size="lg" stringId={LangKeys.ConnectingToNetwork}>
Connecting to Monero Network
</BodyText>
<Box className={classes.container}>
<Box className={classes.bar} />
</Box>

View file

@ -6,7 +6,7 @@ exports[`atoms::ConnectionProgress > renders without exploding 1`] = `
class="mantine-Stack-root mantine-njf2rt"
>
<div
class="mantine-Text-root mantine-102fqds"
class="mantine-Text-root mantine-1ourfup"
>
Connecting to Monero Network
</div>

View file

@ -0,0 +1,68 @@
// =============================================================================
// 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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { Select } from ".";
export default {
title: "atoms/Select",
component: Select,
} as ComponentMeta<typeof Select>;
const Template: ComponentStory<typeof Select> = (args) => {
return (
<Stack>
<Select {...args} />
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {
data: [
{ value: "react", label: "React" },
{ value: "ng", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "vue", label: "Vue" },
],
placeholder: "Pick one",
};
export const Clearable = Template.bind({});
Clearable.args = {
clearable: true,
data: [
{ value: "react", label: "React" },
{ value: "ng", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "vue", label: "Vue" },
],
placeholder: "Pick one",
searchable: false,
};
export const Searchable = Template.bind({});
Searchable.args = {
data: [
{ value: "react", label: "React" },
{ value: "ng", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "vue", label: "Vue" },
],
placeholder: "Pick one",
searchable: true,
};

View file

@ -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 } from "@testing-library/react";
import { Select } from ".";
describe("atoms::Select", () => {
it("renders without exploding", () => {
const { asFragment } = render(
<Select
id="select"
label="Select your favorite framework"
placeholder="Pick one"
data={[
{ value: "react", label: "React" },
{ value: "ng", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "vue", label: "Vue" },
]}
/>
);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,84 @@
// =============================================================================
// 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, useMemo } from "react";
import type { SelectProps as MSelectProps } from "@mantine/core";
import { createStyles, Select as MSelect } from "@mantine/core";
import { ReactComponent as ArrowIcon } from "@assets/arrow-down.svg";
interface SelectProps extends MSelectProps {
id: string;
}
export function Select(props: SelectProps) {
const { clearable, value, onChange } = props;
const [isEmpty, setEmpty] = useState(!value);
const showClearButton = useMemo(
() => clearable === true && !isEmpty,
[clearable, isEmpty]
);
const { classes } = useStyles({ showClearButton });
const handleChange = (value: string) => {
setEmpty(!value);
if (onChange) {
onChange(value);
}
};
return (
<MSelect
{...props}
classNames={classes}
onChange={handleChange}
rightSection={
showClearButton ? undefined : (
<ArrowIcon className={classes.arrowIcon} />
)
}
withinPortal={false}
/>
);
}
const useStyles = createStyles<string, { showClearButton: boolean }>(
(theme, params) => ({
label: {
fontSize: "0.875rem",
fontWeight: 600,
marginBottom: theme.spacing.sm,
},
input: {
fontSize: "0.875rem",
fontWeight: 700,
height: "3rem",
padding: "1rem",
},
item: {
fontSize: "0.875rem",
fontWeight: 500,
padding: "1rem",
},
...(params.showClearButton
? null
: {
rightSection: { pointerEvents: "none" },
}),
arrowIcon: {
width: "0.5rem",
},
})
);

View file

@ -0,0 +1,65 @@
// Vitest Snapshot v1
exports[`atoms::Select > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-Select-root mantine-18udhi"
>
<label
class="mantine-Select-label mantine-7802ha"
for="select"
id="select-label"
>
Select your favorite framework
</label>
<div
aria-controls="select"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="select-items"
role="combobox"
tabindex="-1"
>
<input
defaultvalue=""
type="hidden"
/>
<div
class="mantine-Select-wrapper mantine-12sbrde"
>
<input
aria-autocomplete="list"
aria-invalid="false"
autocomplete="nope"
class="mantine-Select-defaultVariant mantine-Select-input mantine-93d3e4"
data-mantine-stop-propagation="false"
defaultvalue=""
id="select"
placeholder="Pick one"
readonly=""
type="text"
/>
<div
class="mantine-Select-rightSection mantine-14dm59e"
>
<svg
class="mantine-18du5gu"
fill="none"
height="1em"
viewBox="0 0 7 4"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M2.91916 3.69319C3.23963 4.04927 3.76166 4.04657 4.07971 3.69319L6.82329 0.644746C7.14377 0.288661 7.01636 0 6.53811 0H0.460749C-0.0172283 0 -0.14248 0.291357 0.17557 0.644746L2.91916 3.69319Z"
fill="#111111"
fill-rule="evenodd"
/>
</svg>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;

View file

@ -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 "./Select";

View file

@ -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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { TextInput } from ".";
export default {
title: "atoms/TextInput",
component: TextInput,
} as ComponentMeta<typeof TextInput>;
const Template: ComponentStory<typeof TextInput> = (args) => {
return (
<Stack>
<TextInput {...args} />
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {
id: "email",
label: "Your email",
placeholder: "johndoe@gmail.com",
};

View file

@ -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 { describe, expect, it } from "vitest";
import { render } from "@testing-library/react";
import { TextInput } from ".";
describe("atoms::TextInput", () => {
it("renders without exploding", () => {
const { asFragment } = render(
<TextInput
id="textfield"
label="Email"
placeholder="johndoe@gmail.com"
type="email"
/>
);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,42 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import type { TextInputProps as MTextInputProps } from "@mantine/core";
import { createStyles, TextInput as MTextInput } from "@mantine/core";
interface TextInputProps extends MTextInputProps {
id: string;
}
export function TextInput(props: TextInputProps) {
const { id, ...rest } = props;
const { classes } = useStyles();
return <MTextInput classNames={classes} id={id} {...rest} />;
}
const useStyles = createStyles((theme) => ({
label: {
fontSize: "0.875rem",
fontWeight: 600,
marginBottom: theme.spacing.sm,
},
input: {
fontSize: "0.875rem",
fontWeight: 700,
height: "3rem",
padding: "1rem",
},
}));

View file

@ -0,0 +1,28 @@
// Vitest Snapshot v1
exports[`atoms::TextInput > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-TextInput-root mantine-18udhi"
>
<label
class="mantine-TextInput-label mantine-7802ha"
for="textfield"
id="textfield-label"
>
Email
</label>
<div
class="mantine-TextInput-wrapper mantine-12sbrde"
>
<input
aria-invalid="false"
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-dagq8e"
id="textfield"
placeholder="johndoe@gmail.com"
type="email"
/>
</div>
</div>
</DocumentFragment>
`;

View file

@ -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 "./TextInput";

View file

@ -0,0 +1,37 @@
// =============================================================================
// 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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { BodyText } from ".";
export default {
title: "atoms/Typography/ BodyText",
component: BodyText,
} as ComponentMeta<typeof BodyText>;
const Template: ComponentStory<typeof BodyText> = (args) => {
return (
<Stack>
<BodyText {...args}>Body Text</BodyText>
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {
heavy: false,
};

View file

@ -0,0 +1,46 @@
// =============================================================================
// 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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { Heading } from ".";
export default {
title: "atoms/Typography/Heading",
component: Heading,
argTypes: {
order: {
options: [1, 2, 3, 4, 5],
control: {
type: "radio",
},
},
},
} as ComponentMeta<typeof Heading>;
const Template: ComponentStory<typeof Heading> = (args) => {
return (
<Stack>
<Heading {...args} />
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {
order: 1,
children: "This is a heading",
};

View 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 type { ReactText } from "react";
import { FormattedMessage } from "react-intl";
import type { TitleProps } from "@mantine/core";
import { Title } from "@mantine/core";
import type { LangKeys } from "@constants/lang";
interface HeadingProps extends TitleProps {
children: ReactText;
stringId?: LangKeys;
}
export function Heading(props: HeadingProps) {
const { children, stringId, ...rest } = props;
return (
<Title {...rest}>
{stringId ? (
<FormattedMessage id={stringId} defaultMessage={children.toString()} />
) : (
children
)}
</Title>
);
}

View file

@ -0,0 +1,35 @@
// =============================================================================
// 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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { InfoText } from ".";
export default {
title: "atoms/Typography/InfoText",
component: InfoText,
} as ComponentMeta<typeof InfoText>;
const Template: ComponentStory<typeof InfoText> = (args) => {
return (
<Stack>
<InfoText {...args}>Info Text</InfoText>
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {};

View file

@ -0,0 +1,35 @@
// =============================================================================
// 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 { Stack } from "@mantine/core";
import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { LabelText } from ".";
export default {
title: "atoms/Typography/LabelText",
component: LabelText,
} as ComponentMeta<typeof LabelText>;
const Template: ComponentStory<typeof LabelText> = (args) => {
return (
<Stack>
<LabelText {...args}>Label Text</LabelText>
</Stack>
);
};
export const Default = Template.bind({});
Default.args = {};

View file

@ -0,0 +1,101 @@
// =============================================================================
// 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 { ReactText } from "react";
import { FormattedMessage } from "react-intl";
import type { TextProps as MTextProps } from "@mantine/core";
import { Text as MText, createStyles } from "@mantine/core";
import type { LangKeys } from "@constants/lang";
type TextProps<TComponent> = MTextProps<TComponent> & {
children: ReactText;
stringId?: LangKeys;
};
type BodyTextProps<TComponent> = TextProps<TComponent> & {
heavy?: boolean;
};
export function BodyText<TComponent = "p">(props: BodyTextProps<TComponent>) {
const { children, className, heavy, size, stringId, ...rest } = props;
const { classes, cx } = useStyles();
return (
<MText
{...rest}
className={cx(className, {
[classes.body]: !size || size === "md",
[classes.bodyLg]: size === "lg",
[classes.bodyHeavy]: Boolean(heavy),
})}
size={size}
>
{stringId ? (
<FormattedMessage id={stringId} defaultMessage={children.toString()} />
) : (
children
)}
</MText>
);
}
export function InfoText<TComponent = "p">(props: TextProps<TComponent>) {
const { children, className, stringId, ...rest } = props;
const { classes, cx } = useStyles();
return (
<MText {...rest} className={cx(className, classes.info)}>
{stringId ? (
<FormattedMessage id={stringId} defaultMessage={children.toString()} />
) : (
children
)}
</MText>
);
}
export function LabelText(props: TextProps<"label">) {
const { children, className, stringId, ...rest } = props;
const { classes, cx } = useStyles();
return (
<MText component="label" {...rest} className={cx(className, classes.label)}>
{stringId ? (
<FormattedMessage id={stringId} defaultMessage={children.toString()} />
) : (
children
)}
</MText>
);
}
const useStyles = createStyles((theme) => ({
info: {
color: theme.colors.gray[6],
fontSize: "0.875rem",
},
body: {
fontSize: "0.8125rem",
},
bodyHeavy: {
fontWeight: 500,
},
bodyLg: {
fontSize: "1rem",
fontWeight: 500,
},
label: {},
}));

View file

@ -0,0 +1,57 @@
// =============================================================================
// 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 } from "@testing-library/react";
import { BodyText, Heading, InfoText, LabelText } from ".";
describe("atoms::Typography", () => {
describe("::BodyText", () => {
it("renders without exploding", () => {
const { asFragment } = render(<BodyText>body text</BodyText>);
expect(asFragment()).toMatchSnapshot();
});
it("renders heavy variant exploding", () => {
const { asFragment } = render(<BodyText heavy>body text</BodyText>);
expect(asFragment()).toMatchSnapshot();
});
});
describe("::Heading", () => {
it("renders h1 by default", () => {
const { asFragment } = render(<Heading>heading text</Heading>);
expect(asFragment()).toMatchSnapshot();
});
it("renders h2", () => {
const { asFragment } = render(<Heading order={2}>heading text</Heading>);
expect(asFragment()).toMatchSnapshot();
});
});
describe("::InfoText", () => {
it("renders without exploding", () => {
const { asFragment } = render(<InfoText>Info text</InfoText>);
expect(asFragment()).toMatchSnapshot();
});
});
describe("::LabelText", () => {
it("renders without exploding", () => {
const { asFragment } = render(<LabelText>Label text</LabelText>);
expect(asFragment()).toMatchSnapshot();
});
});
});

View file

@ -0,0 +1,71 @@
// Vitest Snapshot v1
exports[`atoms::Typography > ::BodyText > renders heavy variant exploding 1`] = `
<DocumentFragment>
<div
class="mantine-Text-root mantine-119fjqf"
>
body text
</div>
</DocumentFragment>
`;
exports[`atoms::Typography > ::BodyText > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-Text-root mantine-1q69ff6"
>
body text
</div>
</DocumentFragment>
`;
exports[`atoms::Typography > ::Heading > renders h1 1`] = `
<DocumentFragment>
<h1
class="mantine-Title-root mantine-xz2ygd"
>
heading text
</h1>
</DocumentFragment>
`;
exports[`atoms::Typography > ::Heading > renders h1 by default 1`] = `
<DocumentFragment>
<h1
class="mantine-Title-root mantine-xz2ygd"
>
heading text
</h1>
</DocumentFragment>
`;
exports[`atoms::Typography > ::Heading > renders h2 1`] = `
<DocumentFragment>
<h2
class="mantine-Title-root mantine-1dbhwd4"
>
heading text
</h2>
</DocumentFragment>
`;
exports[`atoms::Typography > ::InfoText > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-Text-root mantine-5q0bln"
>
Info text
</div>
</DocumentFragment>
`;
exports[`atoms::Typography > ::LabelText > renders without exploding 1`] = `
<DocumentFragment>
<label
class="mantine-Text-root mantine-12ze1td"
>
Label text
</label>
</DocumentFragment>
`;

View file

@ -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 "./Heading";
export * from "./Text";

View file

@ -0,0 +1,55 @@
// =============================================================================
// 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, UnstyledButton } from "@mantine/core";
import { ReactComponent as AddIcon } from "@assets/circle-plus.svg";
import { HEIGHT, WIDTH } from "./_constants";
interface AddCardProps {
onClick: () => void;
}
export function AddPaymentMethodButton({ onClick }: AddCardProps) {
const { classes } = useStyles();
return (
<UnstyledButton className={classes.card} onClick={onClick}>
<AddIcon />
</UnstyledButton>
);
}
const useStyles = createStyles((theme) => ({
card: {
background: theme.colors.gray[2],
borderRadius: "0.625rem",
display: "grid",
height: HEIGHT,
opacity: 0.75,
placeContent: "center",
transition: "opacity 0.2s",
width: WIDTH,
"&:hover": {
opacity: 1,
},
"& svg": {
height: "1.75rem",
width: "1.75rem",
},
},
}));

View file

@ -0,0 +1,81 @@
// =============================================================================
// 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,
createStyles,
Group,
Stack,
Text,
UnstyledButton,
} from "@mantine/core";
import { ReactComponent as MenuIcon } from "@assets/ellipsis.svg";
import { HEIGHT, WIDTH, CurrencyLogos } from "./_constants";
import type { SupportedCurrencies } from "./_types";
import { BodyText } from "@atoms/Typography";
import { useMemo } from "react";
interface PaymentMethodCardProps {
currency: SupportedCurrencies;
accountId: string;
}
export function PaymentMethodCard(props: PaymentMethodCardProps) {
const { accountId, currency } = props;
const { classes } = useStyles();
const Logo = useMemo(() => CurrencyLogos[currency].Logo, [currency]);
return (
<Box className={classes.card}>
<Stack>
<Group position="apart">
<Group>
<Logo className={classes.logo} />
<Text className={classes.name}>{CurrencyLogos[currency].name}</Text>
</Group>
<UnstyledButton>
<MenuIcon className={classes.menuIcon} />
</UnstyledButton>
</Group>
<BodyText heavy sx={{ wordBreak: "break-word" }}>
{accountId}
</BodyText>
</Stack>
</Box>
);
}
const useStyles = createStyles((theme) => ({
card: {
background: theme.colors.white[0],
border: `solid 1px ${theme.colors.gray[2]}`,
borderRadius: "0.625rem",
height: HEIGHT,
padding: "1.25rem",
width: WIDTH,
},
logo: {
height: "1.75rem",
width: "1.75rem",
},
name: {
color: theme.colors.gray[6],
},
menuIcon: {
width: "1rem",
},
}));

View file

@ -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 type { FC } from "react";
import { ReactComponent as BtcLogo } from "@assets/btc.svg";
import { ReactComponent as EthLogo } from "@assets/eth.svg";
import { ReactComponent as EurLogo } from "@assets/eur.svg";
import type { SupportedCurrencies } from "./_types";
export const WIDTH = "17rem";
export const HEIGHT = "7.25rem";
interface CurrencyDetails {
Logo: FC<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
name: string;
}
export const CurrencyLogos: Record<SupportedCurrencies, CurrencyDetails> = {
BTC: {
Logo: BtcLogo,
name: "Bitcoin",
},
ETH: {
Logo: EthLogo,
name: "Ethereum",
},
EUR: {
Logo: EurLogo,
name: "Euro",
},
};

View file

@ -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 type SupportedCurrencies = "BTC" | "ETH" | "EUR";

View file

@ -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 "./AddPaymentMethodButton";
export * from "./PaymentMethodCard";

View file

@ -3,7 +3,7 @@
exports[`molecules::WalletBalance > renders without exploding 1`] = `
<DocumentFragment>
<button
class="mantine-UnstyledButton-root mantine-1v0qbt7"
class="mantine-UnstyledButton-root mantine-3om9so"
type="button"
>
<div
@ -45,7 +45,7 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
</defs>
</svg>
<div
class="mantine-Text-root mantine-Group-child mantine-15e7vnj"
class="mantine-Text-root mantine-Group-child mantine-1iw7oli"
>
Available Balance
</div>
@ -57,7 +57,7 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
class="mantine-Group-root mantine-6y1794"
>
<div
class="mantine-Text-root mantine-Group-child mantine-3vx9ct"
class="mantine-Text-root mantine-Group-child mantine-q5labh"
>
10.647382650365
</div>
@ -78,7 +78,7 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
</svg>
</div>
<div
class="mantine-Text-root mantine-14xctlx"
class="mantine-Text-root mantine-1pfxwhx"
>
(EUR 2441,02)
</div>
@ -98,12 +98,12 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Total
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
14.048212174412
</div>
@ -112,12 +112,12 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Reserved
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
2.874598526325
</div>
@ -126,12 +126,12 @@ exports[`molecules::WalletBalance > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Locked
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
0.854975624859
</div>

View file

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

View file

@ -0,0 +1,26 @@
// =============================================================================
// 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 } from "@testing-library/react";
import { AddPaymentMethod } from ".";
describe("organisms::AddPaymentMethod", () => {
it("renders without exploding", () => {
const { asFragment } = render(<AddPaymentMethod />);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,126 @@
// =============================================================================
// 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 { useEffect, useMemo } from "react";
import { Collapse, Group, Space, Stack } from "@mantine/core";
import { useForm, joiResolver } from "@mantine/form";
import Joi from "joi";
import { Button } from "@atoms/Buttons";
import { Select } from "@atoms/Select";
import { SupportedCurrencies } from "@constants/currencies";
import { PaymentMethods as _PaymentMethods } from "@constants/payment-methods";
import { TextInput } from "@atoms/TextInput";
interface FormValues {
currency: string;
paymentMethod: string;
accountNumber: string;
}
export function AddPaymentMethod() {
const { getInputProps, onSubmit, setFieldValue, values } =
useForm<FormValues>({
schema: joiResolver(schema),
initialValues: {
currency: "",
paymentMethod: "",
accountNumber: "",
},
});
const PaymentMethods = useMemo(() => {
if (!values.currency) {
return [];
}
const currency = SupportedCurrencies.find(
(curr) => curr.id === values.currency
);
if (!currency) {
return [];
}
return Object.entries(_PaymentMethods)
.filter(([, method]) => currency.paymentMethods.includes(method.id))
.map(([, method]) => ({
value: method.id,
label: method.name,
}))
.sort((a, b) => (a.label > b.label ? 1 : -1));
}, [values?.currency]);
const handleSubmit = (values: FormValues) => console.log(values);
useEffect(() => {
setFieldValue("paymentMethod", "");
}, [values.currency]);
useEffect(() => {
setFieldValue("accountNumber", "");
}, [values.paymentMethod]);
return (
<Stack spacing="lg">
<form onSubmit={onSubmit(handleSubmit)}>
<Stack sx={{ maxWidth: "32rem" }}>
<Select
data={Currencies}
id="currency"
label="Select the currency for your new payment account"
placeholder="Pick one"
{...getInputProps("currency")}
/>
<Collapse in={Boolean(values.currency)}>
<Select
creatable
data={PaymentMethods}
id="paymentMethod"
label="Select your preferred payment method"
placeholder="Pick one"
searchable
{...getInputProps("paymentMethod")}
/>
</Collapse>
<Collapse in={Boolean(values.paymentMethod)}>
<TextInput
id="accountNumber"
label="Account Number"
{...getInputProps("accountNumber")}
/>
</Collapse>
<Group position="right" mt="md">
<Button type="submit">Save</Button>
</Group>
</Stack>
</form>
<Space h="xl" />
<Stack></Stack>
</Stack>
);
}
const schema = Joi.object({
currency: Joi.string().required(),
paymentMethod: Joi.string().required(),
accountNumber: Joi.string().required(),
});
const Currencies = SupportedCurrencies.map((curr) => ({
value: curr.id,
label: curr.name,
}));

View file

@ -0,0 +1,199 @@
// Vitest Snapshot v1
exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-Stack-root mantine-1kgx180"
>
<form>
<div
class="mantine-Stack-root mantine-1i7neaa"
>
<div
class="mantine-Select-root mantine-18udhi"
>
<label
class="mantine-Select-label mantine-7802ha"
for="currency"
id="currency-label"
>
Select the currency for your new payment account
</label>
<div
aria-controls="currency"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="currency-items"
role="combobox"
tabindex="-1"
>
<input
defaultvalue=""
type="hidden"
/>
<div
class="mantine-Select-wrapper mantine-12sbrde"
>
<input
aria-autocomplete="list"
aria-invalid="false"
autocomplete="nope"
class="mantine-Select-defaultVariant mantine-Select-input mantine-93d3e4"
data-mantine-stop-propagation="false"
defaultvalue=""
id="currency"
placeholder="Pick one"
readonly=""
type="text"
/>
<div
class="mantine-Select-rightSection mantine-14dm59e"
>
<svg
class="mantine-18du5gu"
fill="none"
height="1em"
viewBox="0 0 7 4"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M2.91916 3.69319C3.23963 4.04927 3.76166 4.04657 4.07971 3.69319L6.82329 0.644746C7.14377 0.288661 7.01636 0 6.53811 0H0.460749C-0.0172283 0 -0.14248 0.291357 0.17557 0.644746L2.91916 3.69319Z"
fill="#111111"
fill-rule="evenodd"
/>
</svg>
</div>
</div>
</div>
</div>
<div
aria-hidden="true"
class="mantine-1avyp1d"
style="box-sizing: border-box; display: none; height: 0px; overflow: hidden;"
>
<div
style="opacity: 0; transition: opacity 200ms ease;"
>
<div
class="mantine-Select-root mantine-18udhi"
>
<label
class="mantine-Select-label mantine-7802ha"
for="paymentMethod"
id="paymentMethod-label"
>
Select your preferred payment method
</label>
<div
aria-controls="paymentMethod"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="paymentMethod-items"
role="combobox"
tabindex="-1"
>
<input
defaultvalue=""
type="hidden"
/>
<div
class="mantine-Select-wrapper mantine-12sbrde"
>
<input
aria-autocomplete="list"
aria-invalid="false"
autocomplete="nope"
class="mantine-Select-defaultVariant mantine-Select-input mantine-16ko5f2"
data-mantine-stop-propagation="false"
defaultvalue=""
id="paymentMethod"
placeholder="Pick one"
type="text"
/>
<div
class="mantine-Select-rightSection mantine-14dm59e"
>
<svg
class="mantine-18du5gu"
fill="none"
height="1em"
viewBox="0 0 7 4"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M2.91916 3.69319C3.23963 4.04927 3.76166 4.04657 4.07971 3.69319L6.82329 0.644746C7.14377 0.288661 7.01636 0 6.53811 0H0.460749C-0.0172283 0 -0.14248 0.291357 0.17557 0.644746L2.91916 3.69319Z"
fill="#111111"
fill-rule="evenodd"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
<div
aria-hidden="true"
class="mantine-1avyp1d"
style="box-sizing: border-box; display: none; height: 0px; overflow: hidden;"
>
<div
style="opacity: 0; transition: opacity 200ms ease;"
>
<div
class="mantine-TextInput-root mantine-18udhi"
>
<label
class="mantine-TextInput-label mantine-7802ha"
for="accountNumber"
id="accountNumber-label"
>
Account Number
</label>
<div
class="mantine-TextInput-wrapper mantine-12sbrde"
>
<input
aria-invalid="false"
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-dagq8e"
defaultvalue=""
id="accountNumber"
type="text"
/>
</div>
</div>
</div>
</div>
<div
class="mantine-Group-root mantine-mk7hdv"
>
<button
class="mantine-Button-filled mantine-Button-root mantine-Group-child mantine-1i7r9hr"
type="submit"
>
<div
class="mantine-3xbgk5 mantine-Button-inner"
>
<span
class="mantine-qo1k2 mantine-Button-label"
>
Save
</span>
</div>
</button>
</div>
</div>
</form>
<div
class="mantine-1eabysl"
/>
<div
class="mantine-Stack-root mantine-lfk3cq"
/>
</div>
</DocumentFragment>
`;

View file

@ -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 "./AddPaymentMethod";

View file

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

View file

@ -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 { Group, Space, Stack } from "@mantine/core";
import { Heading, BodyText } from "@atoms/Typography";
import {
AddPaymentMethodButton,
PaymentMethodCard,
} from "@molecules/PaymentMethodCard";
interface PaymentMethodsProps {
onAdd: () => void;
}
export function PaymentMethodList({ onAdd }: PaymentMethodsProps) {
return (
<Stack spacing="lg">
<Stack sx={{ maxWidth: "32rem" }}>
<Heading order={2}>Your available payment accounts</Heading>
<BodyText heavy>
To engage in trades you need payment methods to support these trades.
The payment methods are stored locally on your device and details are
only shown to counter parties in trades when needed.
</BodyText>
</Stack>
<Space h="xl" />
<Group spacing="xl">
<PaymentMethodCard
accountId="1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"
currency="BTC"
/>
<PaymentMethodCard accountId="tqTFn5Au4m4GFg7x" currency="ETH" />
<PaymentMethodCard accountId="112233" currency="EUR" />
<AddPaymentMethodButton onClick={onAdd} />
</Group>
</Stack>
);
}

View file

@ -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 "./PaymentMethodList";

View file

@ -19,7 +19,7 @@ import type { ComponentStory, ComponentMeta } from "@storybook/react";
import { Sidebar } from ".";
export default {
title: "molecules/Sidebar",
title: "organisms/Sidebar",
component: Sidebar,
} as ComponentMeta<typeof Sidebar>;

View file

@ -24,7 +24,7 @@ export function Sidebar() {
const { classes } = useStyles();
return (
<Stack className={classes.container}>
<Navbar height="100%" p={0} width={{ base: WIDTH }}>
<Navbar height="100%" p={0} pl="sm" width={{ base: WIDTH }}>
<Navbar.Section>
<Box component={Logo} className={classes.logo} />
</Navbar.Section>

View file

@ -34,7 +34,6 @@ export function NavLink({ icon, isActive = false, label }: NavLinkProps) {
color="gray"
size="xs"
transform="uppercase"
weight={700}
>
{label}
</Text>
@ -81,7 +80,8 @@ const useStyles = createStyles<string, { isActive: boolean }>(
theme.colorScheme === "dark"
? theme.colors.dark[6]
: theme.colors.dark[8],
fontSize: "0.6875rem",
fontWeight: 700,
transition: "opacity 0.2s",
},
})

View file

@ -6,7 +6,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-Stack-root mantine-dczm8e"
>
<nav
class="mantine-Navbar-root mantine-11l21uw"
class="mantine-Navbar-root mantine-pjloqa"
>
<div
class="mantine-khtkeg"
@ -33,7 +33,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-khtkeg"
>
<button
class="mantine-UnstyledButton-root mantine-1wgvoyu"
class="mantine-UnstyledButton-root mantine-1fq8ags"
type="button"
>
<div
@ -53,7 +53,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
/>
</svg>
<div
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1omu9y9"
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1b3ywxa"
>
Markets
</div>
@ -64,7 +64,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-khtkeg"
>
<button
class="mantine-UnstyledButton-root mantine-1wgvoyu"
class="mantine-UnstyledButton-root mantine-1fq8ags"
type="button"
>
<div
@ -88,7 +88,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
/>
</svg>
<div
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1omu9y9"
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1b3ywxa"
>
My Offers
</div>
@ -99,7 +99,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-khtkeg"
>
<button
class="mantine-UnstyledButton-root mantine-1wgvoyu"
class="mantine-UnstyledButton-root mantine-1fq8ags"
type="button"
>
<div
@ -127,7 +127,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
/>
</svg>
<div
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1omu9y9"
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1b3ywxa"
>
My Trades
</div>
@ -138,7 +138,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-khtkeg"
>
<button
class="mantine-UnstyledButton-root mantine-1wgvoyu"
class="mantine-UnstyledButton-root mantine-1fq8ags"
type="button"
>
<div
@ -158,7 +158,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
/>
</svg>
<div
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1omu9y9"
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1b3ywxa"
>
Notifications
</div>
@ -169,7 +169,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-khtkeg"
>
<button
class="mantine-UnstyledButton-root mantine-1wgvoyu"
class="mantine-UnstyledButton-root mantine-1fq8ags"
type="button"
>
<div
@ -188,7 +188,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
/>
</svg>
<div
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1omu9y9"
class="mantine-Text-root mantine-Group-child __mantine-ref-text mantine-1b3ywxa"
>
Account
</div>
@ -202,7 +202,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-b98ato"
>
<button
class="mantine-UnstyledButton-root mantine-1v0qbt7"
class="mantine-UnstyledButton-root mantine-3om9so"
type="button"
>
<div
@ -244,7 +244,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
</defs>
</svg>
<div
class="mantine-Text-root mantine-Group-child mantine-15e7vnj"
class="mantine-Text-root mantine-Group-child mantine-1iw7oli"
>
Available Balance
</div>
@ -256,7 +256,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-Group-root mantine-6y1794"
>
<div
class="mantine-Text-root mantine-Group-child mantine-3vx9ct"
class="mantine-Text-root mantine-Group-child mantine-q5labh"
>
10.647382650365
</div>
@ -277,7 +277,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
</svg>
</div>
<div
class="mantine-Text-root mantine-14xctlx"
class="mantine-Text-root mantine-1pfxwhx"
>
(EUR 2441,02)
</div>
@ -297,12 +297,12 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Total
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
14.048212174412
</div>
@ -311,12 +311,12 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Reserved
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
2.874598526325
</div>
@ -325,12 +325,12 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
class="mantine-Stack-root mantine-1kb6t4k"
>
<div
class="mantine-Text-root mantine-xtp7ze"
class="mantine-Text-root mantine-kaqdcf"
>
Locked
</div>
<div
class="mantine-Text-root mantine-pxv98s"
class="mantine-Text-root mantine-14d5cdm"
>
0.854975624859
</div>

View file

@ -16,7 +16,7 @@
import type { FC } from "react";
import { Box, createStyles, Group } from "@mantine/core";
import { Sidebar } from "@molecules/Sidebar";
import { Sidebar } from "@organisms/Sidebar";
export const NavbarLayout: FC = (props) => {
const { children } = props;
@ -28,7 +28,6 @@ export const NavbarLayout: FC = (props) => {
</Group>
);
};
// fcfcfc
const useStyles = createStyles((theme) => ({
container: {

View file

@ -0,0 +1,129 @@
// =============================================================================
// 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 { ReactComponent as BtcLogo } from "@assets/btc.svg";
import { ReactComponent as EthLogo } from "@assets/eth.svg";
import { ReactComponent as EurLogo } from "@assets/eur.svg";
import { PaymentMethodIds } from "./payment-methods";
export const SupportedCurrencies = [
{
id: "BTC",
name: "Bitcoin",
logo: BtcLogo,
paymentMethods: [PaymentMethodIds.BLOCK_CHAINS_ID],
},
{
id: "ETH",
name: "Ethereum",
logo: EthLogo,
paymentMethods: [PaymentMethodIds.BLOCK_CHAINS_ID],
},
{
id: "EUR",
name: "Euro",
logo: EurLogo,
paymentMethods: [
// EUR
PaymentMethodIds.SEPA_ID,
PaymentMethodIds.SEPA_INSTANT_ID,
PaymentMethodIds.MONEY_BEAM_ID,
// UK
PaymentMethodIds.FASTER_PAYMENTS_ID,
// Sweden
PaymentMethodIds.SWISH_ID,
// Global
PaymentMethodIds.CASH_DEPOSIT_ID,
PaymentMethodIds.CASH_BY_MAIL_ID,
PaymentMethodIds.MONEY_GRAM_ID,
PaymentMethodIds.WESTERN_UNION_ID,
PaymentMethodIds.NATIONAL_BANK_ID,
PaymentMethodIds.SAME_BANK_ID,
PaymentMethodIds.SPECIFIC_BANKS_ID,
PaymentMethodIds.HAL_CASH_ID,
PaymentMethodIds.F2F_ID,
PaymentMethodIds.AMAZON_GIFT_CARD_ID,
// Trans national
PaymentMethodIds.UPHOLD_ID,
PaymentMethodIds.REVOLUT_ID,
PaymentMethodIds.PERFECT_MONEY_ID,
PaymentMethodIds.ADVANCED_CASH_ID,
PaymentMethodIds.TRANSFERWISE_ID,
PaymentMethodIds.TRANSFERWISE_USD_ID,
PaymentMethodIds.PAYSERA_ID,
PaymentMethodIds.PAXUM_ID,
PaymentMethodIds.NEQUI_ID,
PaymentMethodIds.BIZUM_ID,
PaymentMethodIds.PIX_ID,
PaymentMethodIds.CAPITUAL_ID,
PaymentMethodIds.CELPAY_ID,
PaymentMethodIds.MONESE_ID,
PaymentMethodIds.SATISPAY_ID,
PaymentMethodIds.TIKKIE_ID,
PaymentMethodIds.VERSE_ID,
PaymentMethodIds.STRIKE_ID,
PaymentMethodIds.SWIFT_ID,
PaymentMethodIds.ACH_TRANSFER_ID,
PaymentMethodIds.DOMESTIC_WIRE_TRANSFER_ID,
],
},
{
id: "USD",
name: "US Dollar",
logo: EurLogo,
paymentMethods: [
// US
PaymentMethodIds.CLEAR_X_CHANGE_ID,
PaymentMethodIds.POPMONEY_ID,
PaymentMethodIds.US_POSTAL_MONEY_ORDER_ID,
// Global
PaymentMethodIds.CASH_DEPOSIT_ID,
PaymentMethodIds.CASH_BY_MAIL_ID,
PaymentMethodIds.MONEY_GRAM_ID,
PaymentMethodIds.WESTERN_UNION_ID,
PaymentMethodIds.NATIONAL_BANK_ID,
PaymentMethodIds.SAME_BANK_ID,
PaymentMethodIds.SPECIFIC_BANKS_ID,
PaymentMethodIds.HAL_CASH_ID,
PaymentMethodIds.F2F_ID,
PaymentMethodIds.AMAZON_GIFT_CARD_ID,
// Trans national
PaymentMethodIds.UPHOLD_ID,
PaymentMethodIds.REVOLUT_ID,
PaymentMethodIds.PERFECT_MONEY_ID,
PaymentMethodIds.ADVANCED_CASH_ID,
PaymentMethodIds.TRANSFERWISE_ID,
PaymentMethodIds.TRANSFERWISE_USD_ID,
PaymentMethodIds.PAYSERA_ID,
PaymentMethodIds.PAXUM_ID,
PaymentMethodIds.NEQUI_ID,
PaymentMethodIds.BIZUM_ID,
PaymentMethodIds.PIX_ID,
PaymentMethodIds.CAPITUAL_ID,
PaymentMethodIds.CELPAY_ID,
PaymentMethodIds.MONESE_ID,
PaymentMethodIds.SATISPAY_ID,
PaymentMethodIds.TIKKIE_ID,
PaymentMethodIds.VERSE_ID,
PaymentMethodIds.STRIKE_ID,
PaymentMethodIds.SWIFT_ID,
PaymentMethodIds.ACH_TRANSFER_ID,
PaymentMethodIds.DOMESTIC_WIRE_TRANSFER_ID,
],
},
];

View file

@ -0,0 +1,433 @@
// =============================================================================
// 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 startCase from "lodash/startCase";
import camelCase from "lodash/camelCase";
// Ref: https://github.com/bisq-network/bisq/blob/master/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
const DEFAULT_TRADE_LIMIT_VERY_LOW_RISK = 1;
const DEFAULT_TRADE_LIMIT_LOW_RISK = 0.5;
const DEFAULT_TRADE_LIMIT_MID_RISK = 0.25;
const DEFAULT_TRADE_LIMIT_HIGH_RISK = 0.125;
export enum PaymentMethodIds {
UPHOLD_ID = "UPHOLD",
MONEY_BEAM_ID = "MONEY_BEAM",
POPMONEY_ID = "POPMONEY",
REVOLUT_ID = "REVOLUT",
PERFECT_MONEY_ID = "PERFECT_MONEY",
SEPA_ID = "SEPA",
SEPA_INSTANT_ID = "SEPA_INSTANT",
FASTER_PAYMENTS_ID = "FASTER_PAYMENTS",
NATIONAL_BANK_ID = "NATIONAL_BANK",
JAPAN_BANK_ID = "JAPAN_BANK",
AUSTRALIA_PAYID_ID = "AUSTRALIA_PAYID",
SAME_BANK_ID = "SAME_BANK",
SPECIFIC_BANKS_ID = "SPECIFIC_BANKS",
SWISH_ID = "SWISH",
ALI_PAY_ID = "ALI_PAY",
WECHAT_PAY_ID = "WECHAT_PAY",
CLEAR_X_CHANGE_ID = "CLEAR_X_CHANGE",
INTERAC_E_TRANSFER_ID = "INTERAC_E_TRANSFER",
US_POSTAL_MONEY_ORDER_ID = "US_POSTAL_MONEY_ORDER",
CASH_DEPOSIT_ID = "CASH_DEPOSIT",
MONEY_GRAM_ID = "MONEY_GRAM",
WESTERN_UNION_ID = "WESTERN_UNION",
HAL_CASH_ID = "HAL_CASH",
F2F_ID = "F2F",
BLOCK_CHAINS_ID = "BLOCK_CHAINS",
PROMPT_PAY_ID = "PROMPT_PAY",
ADVANCED_CASH_ID = "ADVANCED_CASH",
TRANSFERWISE_ID = "TRANSFERWISE",
TRANSFERWISE_USD_ID = "TRANSFERWISE_USD",
PAYSERA_ID = "PAYSERA",
PAXUM_ID = "PAXUM",
NEFT_ID = "NEFT",
RTGS_ID = "RTGS",
IMPS_ID = "IMPS",
UPI_ID = "UPI",
PAYTM_ID = "PAYTM",
NEQUI_ID = "NEQUI",
BIZUM_ID = "BIZUM",
PIX_ID = "PIX",
AMAZON_GIFT_CARD_ID = "AMAZON_GIFT_CARD",
BLOCK_CHAINS_INSTANT_ID = "BLOCK_CHAINS_INSTANT",
CASH_BY_MAIL_ID = "CASH_BY_MAIL",
CAPITUAL_ID = "CAPITUAL",
CELPAY_ID = "CELPAY",
MONESE_ID = "MONESE",
SATISPAY_ID = "SATISPAY",
TIKKIE_ID = "TIKKIE",
VERSE_ID = "VERSE",
STRIKE_ID = "STRIKE",
SWIFT_ID = "SWIFT",
ACH_TRANSFER_ID = "ACH_TRANSFER",
DOMESTIC_WIRE_TRANSFER_ID = "DOMESTIC_WIRE_TRANSFER",
}
const HOUR = 3_600_000; // ms in 1 hour
const DAY = 86_400_000; // ms in 1 day
export const PaymentMethods: Record<
PaymentMethodIds,
{
id: PaymentMethodIds;
name: string;
maxTradePeriod: number;
maxTradeLimit: number;
}
> = {
// EUR
[PaymentMethodIds.SEPA_ID]: {
id: PaymentMethodIds.SEPA_ID,
name: startCase(camelCase(PaymentMethodIds.SEPA_ID)),
maxTradePeriod: 6 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.SEPA_INSTANT_ID]: {
id: PaymentMethodIds.SEPA_INSTANT_ID,
name: startCase(camelCase(PaymentMethodIds.SEPA_INSTANT_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.MONEY_BEAM_ID]: {
id: PaymentMethodIds.MONEY_BEAM_ID,
name: startCase(camelCase(PaymentMethodIds.MONEY_BEAM_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// UK
[PaymentMethodIds.FASTER_PAYMENTS_ID]: {
id: PaymentMethodIds.FASTER_PAYMENTS_ID,
name: startCase(camelCase(PaymentMethodIds.FASTER_PAYMENTS_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// Sweden
[PaymentMethodIds.SWISH_ID]: {
id: PaymentMethodIds.SWISH_ID,
name: startCase(camelCase(PaymentMethodIds.SWISH_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
// US
[PaymentMethodIds.CLEAR_X_CHANGE_ID]: {
id: PaymentMethodIds.CLEAR_X_CHANGE_ID,
name: startCase(camelCase(PaymentMethodIds.CLEAR_X_CHANGE_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.POPMONEY_ID]: {
id: PaymentMethodIds.POPMONEY_ID,
name: startCase(camelCase(PaymentMethodIds.POPMONEY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.US_POSTAL_MONEY_ORDER_ID]: {
id: PaymentMethodIds.US_POSTAL_MONEY_ORDER_ID,
name: startCase(camelCase(PaymentMethodIds.US_POSTAL_MONEY_ORDER_ID)),
maxTradePeriod: 8 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// Canada
[PaymentMethodIds.INTERAC_E_TRANSFER_ID]: {
id: PaymentMethodIds.INTERAC_E_TRANSFER_ID,
name: startCase(camelCase(PaymentMethodIds.INTERAC_E_TRANSFER_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// Global
[PaymentMethodIds.CASH_DEPOSIT_ID]: {
id: PaymentMethodIds.CASH_DEPOSIT_ID,
name: startCase(camelCase(PaymentMethodIds.CASH_DEPOSIT_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.CASH_BY_MAIL_ID]: {
id: PaymentMethodIds.CASH_BY_MAIL_ID,
name: startCase(camelCase(PaymentMethodIds.CASH_BY_MAIL_ID)),
maxTradePeriod: 8 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.MONEY_GRAM_ID]: {
id: PaymentMethodIds.MONEY_GRAM_ID,
name: startCase(camelCase(PaymentMethodIds.MONEY_GRAM_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_MID_RISK,
},
[PaymentMethodIds.WESTERN_UNION_ID]: {
id: PaymentMethodIds.WESTERN_UNION_ID,
name: startCase(camelCase(PaymentMethodIds.WESTERN_UNION_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_MID_RISK,
},
[PaymentMethodIds.NATIONAL_BANK_ID]: {
id: PaymentMethodIds.NATIONAL_BANK_ID,
name: startCase(camelCase(PaymentMethodIds.NATIONAL_BANK_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.SAME_BANK_ID]: {
id: PaymentMethodIds.SAME_BANK_ID,
name: startCase(camelCase(PaymentMethodIds.SAME_BANK_ID)),
maxTradePeriod: 2 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.SPECIFIC_BANKS_ID]: {
id: PaymentMethodIds.SPECIFIC_BANKS_ID,
name: startCase(camelCase(PaymentMethodIds.SPECIFIC_BANKS_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.HAL_CASH_ID]: {
id: PaymentMethodIds.HAL_CASH_ID,
name: startCase(camelCase(PaymentMethodIds.HAL_CASH_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
[PaymentMethodIds.F2F_ID]: {
id: PaymentMethodIds.F2F_ID,
name: startCase(camelCase(PaymentMethodIds.F2F_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
[PaymentMethodIds.AMAZON_GIFT_CARD_ID]: {
id: PaymentMethodIds.AMAZON_GIFT_CARD_ID,
name: startCase(camelCase(PaymentMethodIds.AMAZON_GIFT_CARD_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// Trans national
[PaymentMethodIds.UPHOLD_ID]: {
id: PaymentMethodIds.UPHOLD_ID,
name: startCase(camelCase(PaymentMethodIds.UPHOLD_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.REVOLUT_ID]: {
id: PaymentMethodIds.REVOLUT_ID,
name: startCase(camelCase(PaymentMethodIds.REVOLUT_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.PERFECT_MONEY_ID]: {
id: PaymentMethodIds.PERFECT_MONEY_ID,
name: startCase(camelCase(PaymentMethodIds.PERFECT_MONEY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
[PaymentMethodIds.ADVANCED_CASH_ID]: {
id: PaymentMethodIds.ADVANCED_CASH_ID,
name: startCase(camelCase(PaymentMethodIds.ADVANCED_CASH_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_VERY_LOW_RISK,
},
[PaymentMethodIds.TRANSFERWISE_ID]: {
id: PaymentMethodIds.TRANSFERWISE_ID,
name: startCase(camelCase(PaymentMethodIds.TRANSFERWISE_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.TRANSFERWISE_USD_ID]: {
id: PaymentMethodIds.TRANSFERWISE_USD_ID,
name: startCase(camelCase(PaymentMethodIds.TRANSFERWISE_USD_ID)),
maxTradePeriod: 4 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.PAYSERA_ID]: {
id: PaymentMethodIds.PAYSERA_ID,
name: startCase(camelCase(PaymentMethodIds.PAYSERA_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.PAXUM_ID]: {
id: PaymentMethodIds.PAXUM_ID,
name: startCase(camelCase(PaymentMethodIds.PAXUM_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.NEFT_ID]: {
id: PaymentMethodIds.NEFT_ID,
name: startCase(camelCase(PaymentMethodIds.NEFT_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: 0.02,
},
[PaymentMethodIds.RTGS_ID]: {
id: PaymentMethodIds.RTGS_ID,
name: startCase(camelCase(PaymentMethodIds.RTGS_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.IMPS_ID]: {
id: PaymentMethodIds.IMPS_ID,
name: startCase(camelCase(PaymentMethodIds.IMPS_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.UPI_ID]: {
id: PaymentMethodIds.UPI_ID,
name: startCase(camelCase(PaymentMethodIds.UPI_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: 0.05,
},
[PaymentMethodIds.PAYTM_ID]: {
id: PaymentMethodIds.PAYTM_ID,
name: startCase(camelCase(PaymentMethodIds.PAYTM_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: 0.05,
},
[PaymentMethodIds.NEQUI_ID]: {
id: PaymentMethodIds.NEQUI_ID,
name: startCase(camelCase(PaymentMethodIds.NEQUI_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.BIZUM_ID]: {
id: PaymentMethodIds.BIZUM_ID,
name: startCase(camelCase(PaymentMethodIds.BIZUM_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: 0.04,
},
[PaymentMethodIds.PIX_ID]: {
id: PaymentMethodIds.PIX_ID,
name: startCase(camelCase(PaymentMethodIds.PIX_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.CAPITUAL_ID]: {
id: PaymentMethodIds.CAPITUAL_ID,
name: startCase(camelCase(PaymentMethodIds.CAPITUAL_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.CELPAY_ID]: {
id: PaymentMethodIds.CELPAY_ID,
name: startCase(camelCase(PaymentMethodIds.CELPAY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.MONESE_ID]: {
id: PaymentMethodIds.MONESE_ID,
name: startCase(camelCase(PaymentMethodIds.MONESE_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.SATISPAY_ID]: {
id: PaymentMethodIds.SATISPAY_ID,
name: startCase(camelCase(PaymentMethodIds.SATISPAY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.TIKKIE_ID]: {
id: PaymentMethodIds.TIKKIE_ID,
name: startCase(camelCase(PaymentMethodIds.TIKKIE_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: 0.05,
},
[PaymentMethodIds.VERSE_ID]: {
id: PaymentMethodIds.VERSE_ID,
name: startCase(camelCase(PaymentMethodIds.VERSE_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.STRIKE_ID]: {
id: PaymentMethodIds.STRIKE_ID,
name: startCase(camelCase(PaymentMethodIds.STRIKE_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.SWIFT_ID]: {
id: PaymentMethodIds.SWIFT_ID,
name: startCase(camelCase(PaymentMethodIds.SWIFT_ID)),
maxTradePeriod: 7 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_MID_RISK,
},
[PaymentMethodIds.ACH_TRANSFER_ID]: {
id: PaymentMethodIds.ACH_TRANSFER_ID,
name: startCase(camelCase(PaymentMethodIds.ACH_TRANSFER_ID)),
maxTradePeriod: 5 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
[PaymentMethodIds.DOMESTIC_WIRE_TRANSFER_ID]: {
id: PaymentMethodIds.DOMESTIC_WIRE_TRANSFER_ID,
name: startCase(camelCase(PaymentMethodIds.DOMESTIC_WIRE_TRANSFER_ID)),
maxTradePeriod: 3 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_HIGH_RISK,
},
// Japan
[PaymentMethodIds.JAPAN_BANK_ID]: {
id: PaymentMethodIds.JAPAN_BANK_ID,
name: startCase(camelCase(PaymentMethodIds.JAPAN_BANK_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
// Australia
[PaymentMethodIds.AUSTRALIA_PAYID_ID]: {
id: PaymentMethodIds.AUSTRALIA_PAYID_ID,
name: startCase(camelCase(PaymentMethodIds.AUSTRALIA_PAYID_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
// China
[PaymentMethodIds.ALI_PAY_ID]: {
id: PaymentMethodIds.ALI_PAY_ID,
name: startCase(camelCase(PaymentMethodIds.ALI_PAY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
[PaymentMethodIds.WECHAT_PAY_ID]: {
id: PaymentMethodIds.WECHAT_PAY_ID,
name: startCase(camelCase(PaymentMethodIds.WECHAT_PAY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
// Thailand
[PaymentMethodIds.PROMPT_PAY_ID]: {
id: PaymentMethodIds.PROMPT_PAY_ID,
name: startCase(camelCase(PaymentMethodIds.PROMPT_PAY_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_LOW_RISK,
},
// Altcoins
[PaymentMethodIds.BLOCK_CHAINS_ID]: {
id: PaymentMethodIds.BLOCK_CHAINS_ID,
name: startCase(camelCase(PaymentMethodIds.BLOCK_CHAINS_ID)),
maxTradePeriod: 1 * DAY,
maxTradeLimit: DEFAULT_TRADE_LIMIT_VERY_LOW_RISK,
},
// Altcoins with 1 hour trade period
[PaymentMethodIds.BLOCK_CHAINS_INSTANT_ID]: {
id: PaymentMethodIds.BLOCK_CHAINS_INSTANT_ID,
name: startCase(camelCase(PaymentMethodIds.BLOCK_CHAINS_INSTANT_ID)),
maxTradePeriod: HOUR,
maxTradeLimit: DEFAULT_TRADE_LIMIT_VERY_LOW_RISK,
},
};

View file

@ -0,0 +1,26 @@
// =============================================================================
// 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 { AddPaymentMethod as AddPaymentMethodOrganism } from "@organisms/AddPaymentMethod";
import { NavbarLayout } from "@templates/NavbarLayout";
export function AddPaymentMethod() {
return (
<NavbarLayout>
<AddPaymentMethodOrganism />
</NavbarLayout>
);
}

View file

@ -0,0 +1,32 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import { useNavigate } from "react-router-dom";
import { PaymentMethodList } from "@organisms/PaymentMethodList";
import { NavbarLayout } from "@templates/NavbarLayout";
import { ROUTES } from "@src/Routes";
export function PaymentMethods() {
const navigate = useNavigate();
return (
<NavbarLayout>
<PaymentMethodList
onAdd={() => navigate(ROUTES.AccountAddPaymentMethod)}
/>
</NavbarLayout>
);
}

View file

@ -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 "./AddPaymentMethod";
export * from "./PaymentMethods";

View file

@ -14,11 +14,11 @@
// limitations under the License.
// =============================================================================
import { FormattedMessage } from "react-intl";
import { Box, Space, Stack, Text } from "@mantine/core";
import { Box, Space, Stack } from "@mantine/core";
import { LangKeys } from "@constants/lang/LangKeys";
import { CenteredLayout } from "@templates/CenteredLayout";
import { ConnectionProgress } from "@atoms/ConnectionProgress";
import { Heading } from "@atoms/Typography";
import Logo from "@assets/logo.svg";
export function Home() {
@ -27,12 +27,13 @@ export function Home() {
<Stack align="center" justify="center" sx={{ flex: 1 }}>
<Stack>
<Box component="img" src={Logo} alt="Haveno" />
<Text size="lg">
<FormattedMessage
id={LangKeys.AppHeading2}
defaultMessage="Monero based decentralized exchange"
/>
</Text>
<Heading
order={2}
stringId={LangKeys.AppHeading2}
sx={{ fontWeight: 500 }}
>
Monero based decentralized exchange
</Heading>
</Stack>
<Space h="lg" />
<ConnectionProgress />

View file

@ -14,11 +14,11 @@
// limitations under the License.
// =============================================================================
import { Stack, Space, Text, Title, Container, Group } from "@mantine/core";
import { FormattedMessage } from "react-intl";
import { Stack, Space, Container, Group } from "@mantine/core";
import { LangKeys } from "@constants/lang/LangKeys";
import { CenteredLayout } from "@src/components/templates/CenteredLayout";
import { CenteredLayout } from "@templates/CenteredLayout";
import { Button } from "@atoms/Buttons";
import { BodyText, Heading } from "@atoms/Typography";
import { CONTENT_MAX_WIDTH } from "./_constants";
export function Welcome() {
@ -27,17 +27,15 @@ export function Welcome() {
<Stack align="center" justify="center" sx={{ flex: 1 }}>
<Stack>
<Container size={CONTENT_MAX_WIDTH}>
<Title order={1}>
<FormattedMessage
id={LangKeys.WelcomeToHaveno}
defaultMessage="Welcome to Haveno. The worlds first Monero based decentralised exchange."
/>
</Title>
<Heading order={1} stringId={LangKeys.WelcomeToHaveno}>
Welcome to Haveno. The world&apos;s first Monero based
decentralised exchange.
</Heading>
</Container>
<Container size={CONTENT_MAX_WIDTH}>
<Text size="md">
Before you can use Haveno, were going to set up your account.{" "}
</Text>
<BodyText size="lg">
Before you can use Haveno, we&apos;re going to set up your account
</BodyText>
</Container>
</Stack>
<Space h="lg" />

View file

@ -14,15 +14,19 @@
// limitations under the License.
// =============================================================================
import type { CSSObject } from "@mantine/core";
import InterFont from "@assets/fonts/Inter-Variable.ttf";
export const globalStyles = {
export const globalStyles: CSSObject = {
"@font-face": {
fontFamily: "Inter",
src: `url('${InterFont}')`,
fontWeight: "100 800",
fontStyle: "normal italic",
},
"*, *::before, *::after": {
boxSizing: "border-box",
},
body: {
margin: 0,
padding: 0,

View file

@ -17,9 +17,39 @@
import type { MantineThemeOverride } from "@mantine/core";
export const themeOverride: MantineThemeOverride = {
fontFamily: "Inter",
fontFamily: "Inter, sans-serif",
fontSizes: {
xl: 18,
lg: 16,
md: 14,
sm: 12,
xs: 10,
},
headings: {
fontFamily: "Inter",
fontFamily: "Inter, sans-serif",
fontWeight: 600,
sizes: {
h1: {
fontSize: "2.25rem",
lineHeight: 1.25,
},
h2: {
fontSize: "1.25rem",
lineHeight: 1.25,
},
h3: {
fontSize: "1.125rem",
lineHeight: 1.25,
},
h4: {
fontSize: "0.875rem",
lineHeight: 1.25,
},
h5: {
fontSize: "0.75rem",
lineHeight: 1.25,
},
},
},
other: {
buttonHeight: 48,
@ -40,7 +70,7 @@ export const themeOverride: MantineThemeOverride = {
gray: [
"#fcfcfc",
"#f1f3f5",
"#ececec",
"#E8E7EC",
"#dee2e6",
"#ced4da",
"#adb5bd",
@ -73,5 +103,17 @@ export const themeOverride: MantineThemeOverride = {
"#58190E",
"#2C0C07",
],
white: [
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
"#fff",
],
},
};