chore: Account screen secondary nav

- Account screen secondary nav
- minor refactoring for routes
- i18n

---
Reviewed-by: schowdhuri
This commit is contained in:
Ahmed Bouhuolia 2022-05-06 15:33:03 +02:00 committed by GitHub
parent ce81d96bd0
commit 9591f3f658
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 45212 additions and 44 deletions

44472
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,17 +17,14 @@
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
import { Home, Welcome } from "@pages/Onboarding"; import { Home, Welcome } from "@pages/Onboarding";
import { Wallet } from "@pages/Wallet"; import { Wallet } from "@pages/Wallet";
import { PaymentMethods, AddPaymentMethod } from "@pages/Account"; import { AccountPaymentAccounts } from "@pages/Account/AccountPaymentAccounts";
import { AccountNodeSettings } from "@pages/Account/AccountNodeSettings";
export const ROUTES = { import { AccountBackup } from "@pages/Account/AccountBackup";
Home: "/", import { AccountWallet } from "@pages/Account/AccountWallet";
Welcome: "/onboarding/welcome", import { AccountSecurity } from "@pages/Account/AccountSecurity";
RestoreBackup: "/onboarding/restore-backup", import { ROUTES } from "@constants/routes";
SetupAccount: "/onboarding/setup", import { PaymentMethods } from "@pages/Account";
Wallet: "/wallet", import { AddPaymentMethod } from "@organisms/AddPaymentMethod";
AccountPaymentMethods: "/account/payment-methods",
AccountAddPaymentMethod: "/account/payment-methods/add",
};
export function AppRoutes() { export function AppRoutes() {
return ( return (
@ -35,11 +32,27 @@ export function AppRoutes() {
<Route path={ROUTES.Home} element={<Home />} /> <Route path={ROUTES.Home} element={<Home />} />
<Route path={ROUTES.Welcome} element={<Welcome />} /> <Route path={ROUTES.Welcome} element={<Welcome />} />
<Route path={ROUTES.Wallet} element={<Wallet />} /> <Route path={ROUTES.Wallet} element={<Wallet />} />
<Route path={ROUTES.AccountPaymentMethods} element={<PaymentMethods />} /> <Route path={ROUTES.Account}>
<Route <Route
path={ROUTES.AccountAddPaymentMethod} path={ROUTES.AccountPaymentAccounts}
element={<AddPaymentMethod />} element={<AccountPaymentAccounts />}
/> />
<Route
path={ROUTES.AccountNodeSettings}
element={<AccountNodeSettings />}
/>
<Route path={ROUTES.AccountBackup} element={<AccountBackup />} />
<Route path={ROUTES.AccountWallet} element={<AccountWallet />} />
<Route path={ROUTES.AccountSecurity} element={<AccountSecurity />} />
<Route
path={ROUTES.AccountPaymentMethods}
element={<PaymentMethods />}
/>
<Route
path={ROUTES.AccountAddPaymentMethod}
element={<AddPaymentMethod />}
/>
</Route>
</Routes> </Routes>
); );
} }

View File

@ -16,16 +16,19 @@
import type { FC } from "react"; import type { FC } from "react";
import { RecoilRoot } from "recoil"; import { RecoilRoot } from "recoil";
import { HashRouter } from "react-router-dom";
import { QueryClientProvider } from "./QueryClientProvider"; import { QueryClientProvider } from "./QueryClientProvider";
import { IntlProvider } from "./IntlProvider"; import { IntlProvider } from "./IntlProvider";
import { ThemeProvider } from "./ThemeProvider"; import { ThemeProvider } from "./ThemeProvider";
export const AppProviders: FC = ({ children }) => ( export const AppProviders: FC = ({ children }) => (
<RecoilRoot> <HashRouter>
<IntlProvider> <RecoilRoot>
<QueryClientProvider> <IntlProvider>
<ThemeProvider>{children}</ThemeProvider> <QueryClientProvider>
</QueryClientProvider> <ThemeProvider>{children}</ThemeProvider>
</IntlProvider> </QueryClientProvider>
</RecoilRoot> </IntlProvider>
</RecoilRoot>
</HashRouter>
); );

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 { AccountSidebar } from "./AccountSidebar";
export default {
title: "molecules/AccountSidebar",
component: AccountSidebar,
} as ComponentMeta<typeof AccountSidebar>;
const Template: ComponentStory<typeof AccountSidebar> = () => {
return <AccountSidebar />;
};
export const Default = Template.bind({});
Default.args = {};

View 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.
// =============================================================================
import { describe, expect, it } from "vitest";
import { render } from "@testing-library/react";
import { Routes, Route } from "react-router-dom";
import { AppProviders } from "@atoms/AppProviders";
import { AccountSidebar } from "./AccountSidebar";
describe("molecules::AccountSidebar", () => {
it("renders without exploding", () => {
const { asFragment } = render(
<AppProviders>
<Routes>
<Route path={"/"} element={<AccountSidebar />} />
</Routes>
</AppProviders>
);
expect(asFragment()).toMatchSnapshot();
});
});

View File

@ -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.
// =============================================================================
import { createStyles, Box, Title } from "@mantine/core";
import { FormattedMessage } from "react-intl";
import { SecondarySidebar } from "@molecules/SecondarySidebar";
import { LangKeys } from "@constants/lang";
import { WIDTH } from "./_constants";
import { useGetAccountSidebarMenu } from "./_hooks";
import { AccountSidebarItem } from "./AccountSidebarItem";
export function AccountSidebar() {
const { classes } = useStyles();
const menu = useGetAccountSidebarMenu();
return (
<Box className={classes.accountSidebar}>
<Title className={classes.title} order={3}>
<FormattedMessage id={LangKeys.AccountTitle} defaultMessage="Account" />
</Title>
<SecondarySidebar>
{menu.map((item) => (
<AccountSidebarItem
key={item.label}
label={item.label}
route={item.route}
/>
))}
</SecondarySidebar>
</Box>
);
}
const useStyles = createStyles((theme) => ({
accountSidebar: {
paddingLeft: theme.spacing.lg,
paddingRight: theme.spacing.lg,
paddingTop: theme.spacing.md,
minWidth: WIDTH,
},
title: {
marginBottom: theme.spacing.md,
},
}));

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 { useNavigate } from "react-router-dom";
import { SecondarySidebarItem } from "@molecules/SecondarySidebar";
import { useNavLinkActive } from "@src/hooks/useNavLinkActive";
interface AccountSidebarItemProps {
label: string;
route: string;
}
export function AccountSidebarItem({ label, route }: AccountSidebarItemProps) {
const isActive = useNavLinkActive({ to: route });
const navigate = useNavigate();
return (
<SecondarySidebarItem
key={label}
label={label}
isActive={isActive}
onClick={() => {
return navigate(route);
}}
/>
);
}

View File

@ -0,0 +1,93 @@
// Vitest Snapshot v1
exports[`molecules::AccountSidebar > renders without exploding 1`] = `
<DocumentFragment>
<div
class="mantine-37r7j5"
>
<h3
class="mantine-Title-root mantine-19bfor4"
>
Account
</h3>
<div
class="mantine-Stack-root mantine-lfk3cq"
>
<nav
class="mantine-Navbar-root mantine-vf15cs"
>
<button
class="mantine-UnstyledButton-root mantine-1us1eqc"
type="button"
>
<div
class="mantine-Group-root mantine-2jlg77"
>
<div
class="mantine-Text-root mantine-Group-child mantine-17vcv21"
>
Payment Accounts
</div>
</div>
</button>
<button
class="mantine-UnstyledButton-root mantine-1us1eqc"
type="button"
>
<div
class="mantine-Group-root mantine-2jlg77"
>
<div
class="mantine-Text-root mantine-Group-child mantine-17vcv21"
>
Node Settings
</div>
</div>
</button>
<button
class="mantine-UnstyledButton-root mantine-1us1eqc"
type="button"
>
<div
class="mantine-Group-root mantine-2jlg77"
>
<div
class="mantine-Text-root mantine-Group-child mantine-17vcv21"
>
Security
</div>
</div>
</button>
<button
class="mantine-UnstyledButton-root mantine-1us1eqc"
type="button"
>
<div
class="mantine-Group-root mantine-2jlg77"
>
<div
class="mantine-Text-root mantine-Group-child mantine-17vcv21"
>
Wallet
</div>
</div>
</button>
<button
class="mantine-UnstyledButton-root mantine-1us1eqc"
type="button"
>
<div
class="mantine-Group-root mantine-2jlg77"
>
<div
class="mantine-Text-root mantine-Group-child mantine-17vcv21"
>
Backup
</div>
</div>
</button>
</nav>
</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 const WIDTH = 210;

View 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 { LangKeys } from "@constants/lang";
import { ROUTES } from "@constants/routes";
import React from "react";
import { useIntl } from "react-intl";
export interface AccountSidebarItem {
label: string;
route: string;
}
export const useGetAccountSidebarMenu = () => {
const intl = useIntl();
return React.useMemo(
() => [
{
label: intl.formatMessage({
id: LangKeys.AccountSidebarPaymentAccounts,
defaultMessage: "Payment Accounts",
}),
route: ROUTES.AccountPaymentAccounts,
},
{
label: intl.formatMessage({
id: LangKeys.AccountSidebarNodeSettings,
defaultMessage: "Node Settings",
}),
route: ROUTES.AccountNodeSettings,
},
{
label: intl.formatMessage({
id: LangKeys.AccountSidebarSecurity,
defaultMessage: "Security",
}),
route: ROUTES.AccountSecurity,
},
{
label: intl.formatMessage({
id: LangKeys.AccountSidebarWallet,
defaultMessage: "Wallet",
}),
route: ROUTES.AccountWallet,
},
{
label: intl.formatMessage({
id: LangKeys.AccountSidebarBackup,
defaultMessage: "Backup",
}),
route: ROUTES.AccountBackup,
},
],
[]
);
};

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

View File

@ -14,7 +14,7 @@
// limitations under the License. // limitations under the License.
// ============================================================================= // =============================================================================
import { Stack, Navbar } from "@mantine/core"; import { Stack, Navbar, createStyles } from "@mantine/core";
interface SecondarySidebarProps { interface SecondarySidebarProps {
children: Array<JSX.Element>; children: Array<JSX.Element>;
@ -26,9 +26,18 @@ interface SecondarySidebarProps {
* @returns {JSX.Element} * @returns {JSX.Element}
*/ */
export function SecondarySidebar({ children }: SecondarySidebarProps) { export function SecondarySidebar({ children }: SecondarySidebarProps) {
const { classes } = useStyles();
return ( return (
<Stack> <Stack>
<Navbar>{children}</Navbar> <Navbar className={classes.navbar}>{children}</Navbar>
</Stack> </Stack>
); );
} }
const useStyles = createStyles<string>(() => ({
navbar: {
background: "transparent",
border: 0,
},
}));

View File

@ -19,6 +19,7 @@ import { UnstyledButton, Group, Text, createStyles } from "@mantine/core";
interface SecondarySidebarItemProps { interface SecondarySidebarItemProps {
isActive?: boolean; isActive?: boolean;
label: string; label: string;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
} }
/** /**
@ -29,10 +30,12 @@ interface SecondarySidebarItemProps {
export function SecondarySidebarItem({ export function SecondarySidebarItem({
isActive = false, isActive = false,
label, label,
onClick,
}: SecondarySidebarItemProps) { }: SecondarySidebarItemProps) {
const { classes } = useStyles({ isActive }); const { classes } = useStyles({ isActive });
return ( return (
<UnstyledButton className={classes.button}> <UnstyledButton className={classes.button} onClick={onClick}>
<Group className={classes.group}> <Group className={classes.group}>
<Text className={classes.text}>{label}</Text> <Text className={classes.text}>{label}</Text>
</Group> </Group>
@ -56,19 +59,19 @@ const useStyles = createStyles<string, { isActive: boolean }>(
}, },
text: { text: {
textTransform: "uppercase", textTransform: "uppercase",
fontSize: theme.fontSizes.xs, fontSize: theme.fontSizes.sm,
fontWeight: 700, fontWeight: 700,
paddingLeft: isActive ? 20 : "0", paddingLeft: isActive ? 20 : 0,
"&:before": { "&:before": {
content: '""', content: '""',
display: "inline-block", display: "inline-block",
width: isActive ? "12px" : "0", width: isActive ? 12 : 0,
height: 3, height: 3,
backgroundColor: theme.colors.blue[6], backgroundColor: theme.colors.blue[6],
position: "absolute", position: "absolute",
top: "50%", top: "50%",
marginTop: "-1.5px", marginTop: -1.5,
left: 0, left: 0,
borderRadius: 3, borderRadius: 3,
}, },

View File

@ -6,7 +6,7 @@ exports[`molecules::SecondarySidebar > renders without exploding 1`] = `
class="mantine-Stack-root mantine-lfk3cq" class="mantine-Stack-root mantine-lfk3cq"
> >
<nav <nav
class="mantine-Navbar-root mantine-1tps18r" class="mantine-Navbar-root mantine-vf15cs"
> >
<button <button
class="mantine-UnstyledButton-root mantine-8x6xy1" class="mantine-UnstyledButton-root mantine-8x6xy1"
@ -16,7 +16,7 @@ exports[`molecules::SecondarySidebar > renders without exploding 1`] = `
class="mantine-Group-root mantine-2jlg77" class="mantine-Group-root mantine-2jlg77"
> >
<div <div
class="mantine-Text-root mantine-Group-child mantine-1ga3coc" class="mantine-Text-root mantine-Group-child mantine-1c1t80j"
> >
Active item Active item
</div> </div>
@ -30,7 +30,7 @@ exports[`molecules::SecondarySidebar > renders without exploding 1`] = `
class="mantine-Group-root mantine-2jlg77" class="mantine-Group-root mantine-2jlg77"
> >
<div <div
class="mantine-Text-root mantine-Group-child mantine-govdvw" class="mantine-Text-root mantine-Group-child mantine-17vcv21"
> >
Inactive item Inactive item
</div> </div>
@ -44,7 +44,7 @@ exports[`molecules::SecondarySidebar > renders without exploding 1`] = `
class="mantine-Group-root mantine-2jlg77" class="mantine-Group-root mantine-2jlg77"
> >
<div <div
class="mantine-Text-root mantine-Group-child mantine-1ga3coc" class="mantine-Text-root mantine-Group-child mantine-1c1t80j"
> >
Active item Active item
</div> </div>

View File

@ -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 { Group, createStyles, Box } from "@mantine/core";
import { NavbarLayout } from "@templates/NavbarLayout";
import { AccountSidebar } from "@molecules/AccountSidebar";
interface AccountContentProps {
children: JSX.Element | JSX.Element[];
}
function AccountContent({ children }: AccountContentProps) {
const { classes } = useStyles();
return (
<Group className={classes.container} spacing={0}>
<AccountSidebar />
<Box className={classes.contentArea}>{children}</Box>
</Group>
);
}
interface AccountLayoutProps {
children: JSX.Element | JSX.Element[];
}
export function AccountLayout({ children }: AccountLayoutProps) {
return (
<NavbarLayout>
<AccountContent>{children}</AccountContent>
</NavbarLayout>
);
}
const useStyles = createStyles((theme) => ({
container: {
flex: 1,
alignItems: "stretch",
},
contentArea: {
display: "flex",
flex: 1,
padding: theme.spacing.sm,
},
}));

View File

@ -20,4 +20,10 @@ export enum LangKeys {
Header = "app.header", Header = "app.header",
ConnectingToNetwork = "app.connectingToNetwork", ConnectingToNetwork = "app.connectingToNetwork",
WelcomeToHaveno = "app.welcomeToHaveno", WelcomeToHaveno = "app.welcomeToHaveno",
AccountTitle = "account.title",
AccountSidebarPaymentAccounts = "account.sidebar.paymentAccounts",
AccountSidebarSecurity = "account.sidebar.security",
AccountSidebarWallet = "account.sidebar.wallet",
AccountSidebarBackup = "account.sidebar.backup",
AccountSidebarNodeSettings = "account.sidebar.nodeSettings",
} }

View File

@ -23,6 +23,12 @@ const LangPackEN: { [key in LangKeys]: string } = {
[LangKeys.Header]: "Haveno", [LangKeys.Header]: "Haveno",
[LangKeys.WelcomeToHaveno]: [LangKeys.WelcomeToHaveno]:
"Welcome to Haveno. The worlds first Monero based decentralised exchange.", "Welcome to Haveno. The worlds first Monero based decentralised exchange.",
[LangKeys.AccountTitle]: "Account",
[LangKeys.AccountSidebarPaymentAccounts]: "Payment Accounts",
[LangKeys.AccountSidebarSecurity]: "Security",
[LangKeys.AccountSidebarWallet]: "Wallet",
[LangKeys.AccountSidebarBackup]: "Backup",
[LangKeys.AccountSidebarNodeSettings]: "Settings",
}; };
export default LangPackEN; export default LangPackEN;

View File

@ -23,6 +23,12 @@ const LangPackES: { [key in LangKeys]: string } = {
[LangKeys.Header]: "Haveno", [LangKeys.Header]: "Haveno",
[LangKeys.WelcomeToHaveno]: [LangKeys.WelcomeToHaveno]:
"Bienvenido a Haveno. El primer intercambio descentralizado basado en Monero del mundo.", "Bienvenido a Haveno. El primer intercambio descentralizado basado en Monero del mundo.",
[LangKeys.AccountTitle]: "Cuenta",
[LangKeys.AccountSidebarPaymentAccounts]: "Cuentas de pago",
[LangKeys.AccountSidebarSecurity]: "Seguridad",
[LangKeys.AccountSidebarWallet]: "Cartera",
[LangKeys.AccountSidebarBackup]: "Respaldo",
[LangKeys.AccountSidebarNodeSettings]: "Ajustes",
}; };
export default LangPackES; export default LangPackES;

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.
// =============================================================================
export const ROUTES = {
Home: "/",
Welcome: "/onboarding/welcome",
RestoreBackup: "/onboarding/restore-backup",
SetupAccount: "/onboarding/setup",
Wallet: "/wallet",
// Account routes.
Account: "/account",
AccountPaymentAccounts: "/account/payment-accounts",
AccountNodeSettings: "/account/node-settings",
AccountBackup: "/account/backup",
AccountWallet: "/account/wallet",
AccountSecurity: "/account/security",
AccountPaymentMethods: "/account/payment-methods",
AccountAddPaymentMethod: "/account/payment-methods/add",
};

View 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 { useResolvedPath, useLocation } from "react-router-dom";
interface LinkItemActiveProps {
to: string;
caseSensitive?: boolean;
end?: boolean;
}
/**
* Determines whether the given route is active.
* @param {LinkItemActiveProps} - Hook props.
* @returns {boolean}
*/
export const useNavLinkActive = ({
caseSensitive = false,
end = false,
to,
}: LinkItemActiveProps) => {
const location = useLocation();
const path = useResolvedPath(to);
let locationPathname = location.pathname;
let toPathname = path.pathname;
if (!caseSensitive) {
locationPathname = locationPathname.toLowerCase();
toPathname = toPathname.toLowerCase();
}
return (
locationPathname === toPathname ||
(!end &&
locationPathname.startsWith(toPathname) &&
locationPathname.charAt(toPathname.length) === "/")
);
};

View File

@ -16,17 +16,14 @@
import { StrictMode } from "react"; import { StrictMode } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { HashRouter } from "react-router-dom";
import { AppRoutes } from "./Routes"; import { AppRoutes } from "./Routes";
import { AppProviders } from "@atoms/AppProviders"; import { AppProviders } from "@atoms/AppProviders";
ReactDOM.render( ReactDOM.render(
<HashRouter> <StrictMode>
<StrictMode> <AppProviders>
<AppProviders> <AppRoutes />
<AppRoutes /> </AppProviders>
</AppProviders> </StrictMode>,
</StrictMode>
</HashRouter>,
document.getElementById("app") document.getElementById("app")
); );

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function Account() {
return (
<AccountLayout>
<h1>Payment accounts</h1>
</AccountLayout>
);
}

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function AccountBackup() {
return (
<AccountLayout>
<h1>Account Backup</h1>
</AccountLayout>
);
}

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function AccountNodeSettings() {
return (
<AccountLayout>
<h1>Account Node Settings</h1>
</AccountLayout>
);
}

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function AccountPaymentAccounts() {
return (
<AccountLayout>
<h1>Payment accounts</h1>
</AccountLayout>
);
}

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function AccountSecurity() {
return (
<AccountLayout>
<h1>Account Security</h1>
</AccountLayout>
);
}

View File

@ -0,0 +1,25 @@
// =============================================================================
// 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 { AccountLayout } from "@templates/AccountLayout";
export function AccountWallet() {
return (
<AccountLayout>
<h1>Account Wallet</h1>
</AccountLayout>
);
}

View File

@ -17,7 +17,7 @@
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { PaymentMethodList } from "@organisms/PaymentMethodList"; import { PaymentMethodList } from "@organisms/PaymentMethodList";
import { NavbarLayout } from "@templates/NavbarLayout"; import { NavbarLayout } from "@templates/NavbarLayout";
import { ROUTES } from "@src/Routes"; import { ROUTES } from "@constants/routes";
export function PaymentMethods() { export function PaymentMethods() {
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -17,7 +17,7 @@
import type { FormEvent } from "react"; import type { FormEvent } from "react";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { ROUTES } from "@src/Routes"; import { ROUTES } from "@constants/routes";
export function Page2() { export function Page2() {
const txtUserRef = useRef<HTMLInputElement>(null); const txtUserRef = useRef<HTMLInputElement>(null);