feat: ✨ add AppBreadcrumbs and new provider pages
This commit is contained in:
parent
bac7d970b8
commit
4a34a4b0c6
9 changed files with 231 additions and 47 deletions
|
@ -7,3 +7,4 @@ export const providersSchema = z.array(providerSchema);
|
||||||
|
|
||||||
export type ProvidersConfig = z.infer<typeof providersSchema>;
|
export type ProvidersConfig = z.infer<typeof providersSchema>;
|
||||||
export type ProviderConfig = z.infer<typeof providerSchema>;
|
export type ProviderConfig = z.infer<typeof providerSchema>;
|
||||||
|
export type ProviderName = ProviderConfig["provider"];
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { AvataxIcon, TaxJarIcon } from "../../assets";
|
|
||||||
|
|
||||||
export const providerConfig = {
|
|
||||||
taxjar: {
|
|
||||||
label: "TaxJar",
|
|
||||||
icon: TaxJarIcon,
|
|
||||||
},
|
|
||||||
avatax: {
|
|
||||||
label: "Avatax",
|
|
||||||
icon: AvataxIcon,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TaxProviderName = keyof typeof providerConfig;
|
|
61
apps/taxes/src/modules/ui/app-breadcrumbs.tsx
Normal file
61
apps/taxes/src/modules/ui/app-breadcrumbs.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { Breadcrumbs } from "@saleor/apps-ui";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
type Breadcrumb = {
|
||||||
|
label: string;
|
||||||
|
href?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const newProviderBreadcrumbs = [
|
||||||
|
{
|
||||||
|
href: "/configuration",
|
||||||
|
label: "Configuration",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Add provider",
|
||||||
|
href: "/providers",
|
||||||
|
},
|
||||||
|
] as Breadcrumb[];
|
||||||
|
|
||||||
|
const breadcrumbsForRoute = {
|
||||||
|
"/configuration": [
|
||||||
|
{
|
||||||
|
href: "/configuration",
|
||||||
|
label: "Configuration",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"/providers": [...newProviderBreadcrumbs],
|
||||||
|
"/providers/taxjar": [
|
||||||
|
...newProviderBreadcrumbs,
|
||||||
|
{
|
||||||
|
label: "TaxJar",
|
||||||
|
href: "/providers/taxjar",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"/providers/avatax": [
|
||||||
|
...newProviderBreadcrumbs,
|
||||||
|
{
|
||||||
|
label: "Avatax",
|
||||||
|
href: "/providers/avatax",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as Record<string, Breadcrumb[]>;
|
||||||
|
|
||||||
|
const useBreadcrumbs = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const breadcrumbs = breadcrumbsForRoute[router.pathname];
|
||||||
|
|
||||||
|
return breadcrumbs;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AppBreadcrumbs = () => {
|
||||||
|
const breadcrumbs = useBreadcrumbs();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Breadcrumbs>
|
||||||
|
{breadcrumbs?.map((breadcrumb) => (
|
||||||
|
<Breadcrumbs.Item href={breadcrumb.href}>{breadcrumb.label}</Breadcrumbs.Item>
|
||||||
|
))}
|
||||||
|
</Breadcrumbs>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,17 +1,7 @@
|
||||||
import { Box, BoxProps, Button, Text } from "@saleor/macaw-ui/next";
|
import { Box, BoxProps, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { trpcClient } from "../trpc/trpc-client";
|
import { trpcClient } from "../trpc/trpc-client";
|
||||||
|
import { AppCard } from "./app-card";
|
||||||
const Table = {
|
|
||||||
Container: (props: BoxProps) => <Box __textAlign={"left"} width="100%" {...props} as="table" />,
|
|
||||||
THead: (props: BoxProps) => <Box {...props} as="thead" />,
|
|
||||||
TR: (props: BoxProps) => <Box {...props} as="tr" />,
|
|
||||||
TH: (props: BoxProps) => (
|
|
||||||
<Box fontWeight={"captionSmall"} fontSize={"captionSmall"} {...props} as="th" />
|
|
||||||
),
|
|
||||||
TBody: (props: BoxProps) => <Box {...props} as="tbody" />,
|
|
||||||
TD: (props: BoxProps) => <Box fontSize="bodyMedium" {...props} as="td" />,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AddProvider = () => {
|
const AddProvider = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -28,19 +18,31 @@ const AddProvider = () => {
|
||||||
<Text variant="body" __fontWeight={"400"}>
|
<Text variant="body" __fontWeight={"400"}>
|
||||||
No providers configured yet
|
No providers configured yet
|
||||||
</Text>
|
</Text>
|
||||||
<Button onClick={() => router.push("/providers/new")}>Add first provider</Button>
|
<Button onClick={() => router.push("/providers")}>Add first provider</Button>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Skeleton = () => {
|
const Skeleton = () => {
|
||||||
|
// todo: replace with skeleton
|
||||||
return (
|
return (
|
||||||
<Box height={"100%"} display={"flex"} alignItems={"center"} justifyContent={"center"}>
|
<Box height={"100%"} display={"flex"} alignItems={"center"} justifyContent={"center"}>
|
||||||
Skeleton...
|
Loading...
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Table = {
|
||||||
|
Container: (props: BoxProps) => <Box __textAlign={"left"} width="100%" {...props} as="table" />,
|
||||||
|
THead: (props: BoxProps) => <Box {...props} as="thead" />,
|
||||||
|
TR: (props: BoxProps) => <Box {...props} as="tr" />,
|
||||||
|
TH: (props: BoxProps) => (
|
||||||
|
<Box fontWeight={"captionSmall"} fontSize={"captionSmall"} {...props} as="th" />
|
||||||
|
),
|
||||||
|
TBody: (props: BoxProps) => <Box {...props} as="tbody" />,
|
||||||
|
TD: (props: BoxProps) => <Box fontSize="bodyMedium" {...props} as="td" />,
|
||||||
|
};
|
||||||
|
|
||||||
const ProvidersTable = () => {
|
const ProvidersTable = () => {
|
||||||
const { data } = trpcClient.providersConfiguration.getAll.useQuery();
|
const { data } = trpcClient.providersConfiguration.getAll.useQuery();
|
||||||
|
|
||||||
|
@ -75,25 +77,17 @@ export const Providers = () => {
|
||||||
const isNoResult = isFetched && !isProvider;
|
const isNoResult = isFetched && !isProvider;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<AppCard __minHeight={"320px"} height="100%">
|
||||||
borderRadius={4}
|
|
||||||
borderWidth={1}
|
|
||||||
borderColor={"neutralPlain"}
|
|
||||||
borderStyle={"solid"}
|
|
||||||
padding={8}
|
|
||||||
__minHeight={"320px"}
|
|
||||||
height="100%"
|
|
||||||
>
|
|
||||||
{isFetching && <Skeleton />}
|
{isFetching && <Skeleton />}
|
||||||
{isNoResult && <AddProvider />}
|
{isNoResult && <AddProvider />}
|
||||||
{isResult && (
|
{isResult && (
|
||||||
<>
|
<>
|
||||||
<ProvidersTable />
|
<ProvidersTable />
|
||||||
<Box>
|
<Box>
|
||||||
<Button onClick={() => router.push("/providers/new")}>Add new</Button>
|
<Button onClick={() => router.push("/providers")}>Add new</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</AppCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,10 +4,7 @@ import { Providers } from "../modules/ui/providers";
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
return (
|
return (
|
||||||
<Box as="header" display="flex" flexDirection={"column"} gap={6}>
|
<Box>
|
||||||
<Text as="h1" variant="hero">
|
|
||||||
Configuration
|
|
||||||
</Text>
|
|
||||||
<Text as="p" variant="body" __fontWeight={"400"}>
|
<Text as="p" variant="body" __fontWeight={"400"}>
|
||||||
Please configure the app by connecting one of the supported tax providers.
|
Please configure the app by connecting one of the supported tax providers.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
11
apps/taxes/src/pages/providers/avatax.tsx
Normal file
11
apps/taxes/src/pages/providers/avatax.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* * placeholder
|
||||||
|
* // todo: add new avatax config view
|
||||||
|
*/
|
||||||
|
const NewAvataxPage = () => {
|
||||||
|
return <main>Avatax</main>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewAvataxPage;
|
128
apps/taxes/src/pages/providers/index.tsx
Normal file
128
apps/taxes/src/pages/providers/index.tsx
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { AvataxIcon, StripeTaxIcon, TaxJarIcon } from "../../assets";
|
||||||
|
import { AppCard } from "../../modules/ui/app-card";
|
||||||
|
import { AppColumns } from "../../modules/ui/app-columns";
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Text __maxWidth={"360px"} __fontWeight={"400"} variant="body">
|
||||||
|
Select and configure providers to connect Saleor with selected services.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Intro = () => {
|
||||||
|
return (
|
||||||
|
<Box gap={6} display="flex" flexDirection={"column"}>
|
||||||
|
<Text variant="heading" as="h3">
|
||||||
|
Choose provider
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProviderProps = {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
description: React.ReactNode;
|
||||||
|
isComingSoon?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const providerConfig = {
|
||||||
|
taxjar: {
|
||||||
|
label: "TaxJar",
|
||||||
|
icon: TaxJarIcon,
|
||||||
|
description: (
|
||||||
|
<p>
|
||||||
|
TaxJar is a cloud-based tax automation platform designed to simplify and streamline sales
|
||||||
|
tax management for online sellers.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
avatax: {
|
||||||
|
label: "Avatax",
|
||||||
|
icon: AvataxIcon,
|
||||||
|
description: (
|
||||||
|
<p>
|
||||||
|
Avatax is a comprehensive tax automation software service that helps businesses calculate
|
||||||
|
and manage sales tax accurately and efficiently.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
stripeTax: {
|
||||||
|
label: "Stripe Tax",
|
||||||
|
icon: StripeTaxIcon,
|
||||||
|
isComingSoon: true,
|
||||||
|
description: (
|
||||||
|
<p>
|
||||||
|
Stripe Tax lets you calculate, collect, and report tax on global payments with a single
|
||||||
|
integration.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
} satisfies Record<string, ProviderProps>;
|
||||||
|
|
||||||
|
const ProviderCard = ({
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
description,
|
||||||
|
provider,
|
||||||
|
isComingSoon,
|
||||||
|
}: ProviderProps & { provider: string }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppCard>
|
||||||
|
<Box display={"flex"} flexDirection={"column"} gap={8}>
|
||||||
|
<Box display={"flex"} justifyContent={"space-between"}>
|
||||||
|
<Box alignItems={"center"} display={"flex"} gap={6}>
|
||||||
|
<Image src={icon} width={20} height={20} alt={`provider icon`} />
|
||||||
|
<Text variant="bodyStrong">{label}</Text>
|
||||||
|
</Box>
|
||||||
|
{isComingSoon && (
|
||||||
|
<Text
|
||||||
|
variant="body"
|
||||||
|
fontSize={"headingSmall"}
|
||||||
|
color={"textNeutralSubdued"}
|
||||||
|
textTransform={"uppercase"}
|
||||||
|
>
|
||||||
|
Coming soon
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Text __fontWeight={"400"} variant="body" __maxWidth={"480px"}>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box display={"flex"} justifyContent={"flex-end"} marginTop={12}>
|
||||||
|
{!isComingSoon && (
|
||||||
|
<Button onClick={() => router.push(`/providers/${provider}`)}>Choose</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</AppCard>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ChooseProvider = () => {
|
||||||
|
return (
|
||||||
|
<Box gap={6} display="flex" flexDirection={"column"}>
|
||||||
|
{Object.entries(providerConfig).map(([provider, description]) => {
|
||||||
|
return <ProviderCard {...description} provider={provider} />;
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NewProviderPage = () => {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<AppColumns top={<Header />} bottomLeft={<Intro />} bottomRight={<ChooseProvider />} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewProviderPage;
|
|
@ -1,5 +0,0 @@
|
||||||
const NewProviderPage = () => {
|
|
||||||
return <div>new</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NewProviderPage;
|
|
11
apps/taxes/src/pages/providers/taxjar.tsx
Normal file
11
apps/taxes/src/pages/providers/taxjar.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* * placeholder
|
||||||
|
* // todo: add new taxjar config view
|
||||||
|
*/
|
||||||
|
const NewTaxJarPage = () => {
|
||||||
|
return <main>TaxJar</main>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewTaxJarPage;
|
Loading…
Reference in a new issue