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 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 { useRouter } from "next/router";
|
||||
import { trpcClient } from "../trpc/trpc-client";
|
||||
|
||||
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" />,
|
||||
};
|
||||
import { AppCard } from "./app-card";
|
||||
|
||||
const AddProvider = () => {
|
||||
const router = useRouter();
|
||||
|
@ -28,19 +18,31 @@ const AddProvider = () => {
|
|||
<Text variant="body" __fontWeight={"400"}>
|
||||
No providers configured yet
|
||||
</Text>
|
||||
<Button onClick={() => router.push("/providers/new")}>Add first provider</Button>
|
||||
<Button onClick={() => router.push("/providers")}>Add first provider</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const Skeleton = () => {
|
||||
// todo: replace with skeleton
|
||||
return (
|
||||
<Box height={"100%"} display={"flex"} alignItems={"center"} justifyContent={"center"}>
|
||||
Skeleton...
|
||||
Loading...
|
||||
</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 { data } = trpcClient.providersConfiguration.getAll.useQuery();
|
||||
|
||||
|
@ -75,25 +77,17 @@ export const Providers = () => {
|
|||
const isNoResult = isFetched && !isProvider;
|
||||
|
||||
return (
|
||||
<Box
|
||||
borderRadius={4}
|
||||
borderWidth={1}
|
||||
borderColor={"neutralPlain"}
|
||||
borderStyle={"solid"}
|
||||
padding={8}
|
||||
__minHeight={"320px"}
|
||||
height="100%"
|
||||
>
|
||||
<AppCard __minHeight={"320px"} height="100%">
|
||||
{isFetching && <Skeleton />}
|
||||
{isNoResult && <AddProvider />}
|
||||
{isResult && (
|
||||
<>
|
||||
<ProvidersTable />
|
||||
<Box>
|
||||
<Button onClick={() => router.push("/providers/new")}>Add new</Button>
|
||||
<Button onClick={() => router.push("/providers")}>Add new</Button>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</AppCard>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,10 +4,7 @@ import { Providers } from "../modules/ui/providers";
|
|||
|
||||
const Header = () => {
|
||||
return (
|
||||
<Box as="header" display="flex" flexDirection={"column"} gap={6}>
|
||||
<Text as="h1" variant="hero">
|
||||
Configuration
|
||||
</Text>
|
||||
<Box>
|
||||
<Text as="p" variant="body" __fontWeight={"400"}>
|
||||
Please configure the app by connecting one of the supported tax providers.
|
||||
</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