Extract shared packages (#948)
* [skip ci] tRPC shared package * [skip ci] tRPC shared package - fix * [skip ci] shared package - app sections * [skip ci] segment - implement shared components * [skip ci] extract theme synchronizer * extract components and implement them in apps * cms - extract shared packages * Fix imports * remove urql from peer deps
This commit is contained in:
parent
ceddcf96eb
commit
e8660e8bb9
78 changed files with 679 additions and 670 deletions
5
.changeset/afraid-eagles-joke.md
Normal file
5
.changeset/afraid-eagles-joke.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-cms-v2": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Extracted UI components and use shared package
|
5
.changeset/afraid-wolves-shave.md
Normal file
5
.changeset/afraid-wolves-shave.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-search": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Implemented shared getApBaseUrl
|
5
.changeset/dull-lobsters-relate.md
Normal file
5
.changeset/dull-lobsters-relate.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-shared": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added GraphQL Provider component that build client side graphql context
|
5
.changeset/dull-mayflies-arrive.md
Normal file
5
.changeset/dull-mayflies-arrive.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-segment": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Implemented ButtonsBox and SkeletonLayout from shared package. This should not have visual effect other than better looking Skeleton animation
|
6
.changeset/fast-bobcats-sip.md
Normal file
6
.changeset/fast-bobcats-sip.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
"saleor-app-segment": patch
|
||||||
|
"saleor-app-search": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Replaced GraphQL provider with shared package
|
5
.changeset/flat-apples-divide.md
Normal file
5
.changeset/flat-apples-divide.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-search": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Replaced AppSections implementation with the shared package
|
5
.changeset/late-cherries-sparkle.md
Normal file
5
.changeset/late-cherries-sparkle.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-ui": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added two new components: ButtonsBox, which is a simple grid wrapper for horizontal buttons and SkeletonSection which composes Macaw Skeletons and make them looking more like a layout
|
5
.changeset/olive-ducks-grow.md
Normal file
5
.changeset/olive-ducks-grow.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-shared": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Removed ThemeProvider that was legacy for the older Macaw/Material UI
|
6
.changeset/polite-cobras-relax.md
Normal file
6
.changeset/polite-cobras-relax.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
"saleor-app-segment": patch
|
||||||
|
"saleor-app-search": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Implemented ThemeSynchronizer from shared package
|
5
.changeset/sharp-ducks-peel.md
Normal file
5
.changeset/sharp-ducks-peel.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/trpc": major
|
||||||
|
---
|
||||||
|
|
||||||
|
Introduced shared tRPC package. It contains reusable utils that are shared among apps
|
5
.changeset/silly-geese-shout.md
Normal file
5
.changeset/silly-geese-shout.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-ui": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added Layout.AppSection and Layout.AppSectionCard components to build standard app layouts
|
5
.changeset/slow-bears-own.md
Normal file
5
.changeset/slow-bears-own.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-shared": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added getAppBaseUrl function that infers apps own URL
|
5
.changeset/small-vans-rhyme.md
Normal file
5
.changeset/small-vans-rhyme.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-segment": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Extracted some tRPC utilities to shared package
|
5
.changeset/tender-cycles-float.md
Normal file
5
.changeset/tender-cycles-float.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@saleor/apps-shared": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added ThemeSynchronizer component that automatically syncs AppBridge and Macaw theme
|
5
.changeset/wet-moles-admire.md
Normal file
5
.changeset/wet-moles-admire.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-search": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed broken configuration form when legacy metadata was fetched
|
7
.github/dependabot.yaml
vendored
7
.github/dependabot.yaml
vendored
|
@ -37,6 +37,13 @@ updates:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "[skip ci]"
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/trpc"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
|
||||||
# Apps
|
# Apps
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
|
|
|
@ -6,26 +6,20 @@ import { Select } from "@saleor/react-hook-form-macaw";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ButtonsBox } from "../ui/buttons-box";
|
|
||||||
import { ProvidersResolver } from "../providers/providers-resolver";
|
import { ProvidersResolver } from "../providers/providers-resolver";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
connectionId: z.string().min(7),
|
connectionId: z.string().min(7),
|
||||||
});
|
});
|
||||||
|
|
||||||
const EmptyState = () => (
|
const EmptyState = () => (
|
||||||
<Box
|
<Layout.AppSectionCard>
|
||||||
display="flex"
|
<Box display="flex" flexDirection={"column"} gap={4} justifyContent={"center"}>
|
||||||
paddingY={4}
|
<Text variant="heading">Bulk products synchronization</Text>
|
||||||
flexDirection={"column"}
|
<Text>Create a channel connection above to enable bulk synchronization.</Text>
|
||||||
gap={4}
|
</Box>
|
||||||
alignItems={"center"}
|
</Layout.AppSectionCard>
|
||||||
justifyContent={"center"}
|
|
||||||
>
|
|
||||||
<Text variant="heading">No connections configured</Text>
|
|
||||||
<Text>Create a channel connection above to enable bulk synchronization.</Text>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const BulkSyncSection = () => {
|
export const BulkSyncSection = () => {
|
||||||
|
@ -42,7 +36,7 @@ export const BulkSyncSection = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!connections || !providers) {
|
if (!connections || !providers) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connections.length === 0) {
|
if (connections.length === 0) {
|
||||||
|
@ -50,7 +44,7 @@ export const BulkSyncSection = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard>
|
||||||
<Text as="h2" marginBottom={6} variant="heading">
|
<Text as="h2" marginBottom={6} variant="heading">
|
||||||
Bulk products synchronization
|
Bulk products synchronization
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -88,6 +82,6 @@ export const BulkSyncSection = () => {
|
||||||
<Button type="submit">Start sync</Button>
|
<Button type="submit">Start sync</Button>
|
||||||
</ButtonsBox>
|
</ButtonsBox>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Layout.AppSectionCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { Breadcrumbs } from "@saleor/apps-ui";
|
import { Breadcrumbs, ButtonsBox, Layout } from "@saleor/apps-ui";
|
||||||
import { ArrowRightIcon, Box, Button, Text } from "@saleor/macaw-ui/next";
|
import { ArrowRightIcon, Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { ChannelProviderConnectionConfig, ProvidersConfig } from "../configuration";
|
import { ChannelProviderConnectionConfig, ProvidersConfig } from "../configuration";
|
||||||
import { AppHeader } from "../ui/app-header";
|
import { AppHeader } from "../ui/app-header";
|
||||||
import { AppSection } from "../ui/app-section";
|
|
||||||
import { ButtonsBox } from "../ui/buttons-box";
|
|
||||||
|
|
||||||
import { useBulkSyncProductsState } from "./use-bulk-sync-products-state";
|
import { useBulkSyncProductsState } from "./use-bulk-sync-products-state";
|
||||||
import { useFetchAllProducts } from "./use-fetch-all-products";
|
import { useFetchAllProducts } from "./use-fetch-all-products";
|
||||||
|
@ -14,16 +12,19 @@ import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
|
||||||
const FetchProductsStep = (props: { onButtonClick(): void }) => {
|
const FetchProductsStep = (props: { onButtonClick(): void }) => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
<ButtonsBox>
|
||||||
|
<Button onClick={props.onButtonClick}>Prefetch products</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Text variant="heading" as="h2" marginBottom={4}>
|
<Text variant="heading" as="h2" marginBottom={4}>
|
||||||
Saleor products fetch
|
Saleor products fetch
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="p">Click the button to start fetching products from Saleor API</Text>
|
<Text as="p">Click the button to start fetching products from Saleor API</Text>
|
||||||
<Text as="p">After products are fetched, you will be able to upload them to the CMS</Text>
|
<Text as="p">After products are fetched, you will be able to upload them to the CMS</Text>
|
||||||
<ButtonsBox>
|
</Layout.AppSectionCard>
|
||||||
<Button onClick={props.onButtonClick}>Prefetch products</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ export const BulkSyncView = ({
|
||||||
|
|
||||||
const { products, finished: saleorProductsFetchFinished } = useFetchAllProducts(
|
const { products, finished: saleorProductsFetchFinished } = useFetchAllProducts(
|
||||||
state === "fetching",
|
state === "fetching",
|
||||||
connection.channelSlug
|
connection.channelSlug,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { productsStatusList, setInitialProducts, setItemStatus, finished } =
|
const { productsStatusList, setInitialProducts, setItemStatus, finished } =
|
||||||
|
@ -121,9 +122,14 @@ export const BulkSyncView = ({
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
marginBottom={8}
|
marginBottom={8}
|
||||||
mainContent={(() => {
|
heading="1. Fetch products"
|
||||||
|
sideContent={
|
||||||
|
<Text>First pre-fetch all Product Variants from Saleor. Do not close the app.</Text>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{(() => {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case "initial": {
|
case "initial": {
|
||||||
return (
|
return (
|
||||||
|
@ -149,33 +155,29 @@ export const BulkSyncView = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
heading="1. Fetch products"
|
</Layout.AppSection>
|
||||||
sideContent={
|
|
||||||
<Text>First pre-fetch all Product Variants from Saleor. Do not close the app.</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{(state === "fetched" || state === "uploading") && productsStatusList && (
|
{(state === "fetched" || state === "uploading") && productsStatusList && (
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
|
marginTop={14}
|
||||||
heading="2. Upload to the CMS"
|
heading="2. Upload to the CMS"
|
||||||
sideContent={<Text>Send listed variants to the CMS</Text>}
|
sideContent={<Text>Send listed variants to the CMS</Text>}
|
||||||
mainContent={
|
>
|
||||||
<Box>
|
<Layout.AppSectionCard>
|
||||||
<Text as="h2" marginBottom={4} variant="heading">
|
<Text as="h2" marginBottom={4} variant="heading">
|
||||||
Upload products
|
Upload products
|
||||||
</Text>
|
</Text>
|
||||||
{state === "fetched" && (
|
{state === "fetched" && (
|
||||||
<Box marginBottom={4}>
|
<Box marginBottom={4}>
|
||||||
<Text as="p" marginBottom={2}>
|
<Text as="p" marginBottom={2}>
|
||||||
Verify products below and click the button to start uploading.
|
Verify products below and click the button to start uploading.
|
||||||
</Text>
|
</Text>
|
||||||
<Button onClick={() => setState("uploading")}>Start uploading</Button>
|
<Button onClick={() => setState("uploading")}>Start uploading</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<VariantsSyncStatusList marginTop={8} variants={productsStatusList} />
|
<VariantsSyncStatusList marginTop={8} variants={productsStatusList} />
|
||||||
</Box>
|
</Layout.AppSectionCard>
|
||||||
}
|
</Layout.AppSection>
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Button, Text } from "@saleor/macaw-ui/next";
|
import { Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import { ButtonsBox } from "../ui/buttons-box";
|
|
||||||
import { Modal } from "../ui/modal";
|
import { Modal } from "../ui/modal";
|
||||||
import {
|
import {
|
||||||
AddConnectionForm,
|
AddConnectionForm,
|
||||||
|
@ -7,7 +6,7 @@ import {
|
||||||
AddConnectionFormSchema,
|
AddConnectionFormSchema,
|
||||||
} from "./add-connection-form";
|
} from "./add-connection-form";
|
||||||
import { trpcClient } from "../trpc/trpc-client";
|
import { trpcClient } from "../trpc/trpc-client";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { ButtonsBox, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
const defaultValues: AddConnectionFormSchema = { channelSlug: "", providerId: "" };
|
const defaultValues: AddConnectionFormSchema = { channelSlug: "", providerId: "" };
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ export const AddConnectionModal = (props: { onSuccess(): void; onClose(): void }
|
||||||
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
||||||
|
|
||||||
if (!providers) {
|
if (!providers) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mutateAsync: addProviderMutate, isLoading } =
|
const { mutateAsync: addProviderMutate, isLoading } =
|
||||||
|
|
|
@ -1,35 +1,31 @@
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
import { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { trpcClient } from "../trpc/trpc-client";
|
import { trpcClient } from "../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../ui/buttons-box";
|
|
||||||
import { AddConnectionFormSchema } from "./add-connection-form";
|
|
||||||
import { AddConnectionModal } from "./add-connection-modal";
|
import { AddConnectionModal } from "./add-connection-modal";
|
||||||
import { ChanelProviderConnectionsSectionHeader } from "./channel-provider-connections-section-header";
|
import { ChanelProviderConnectionsSectionHeader } from "./channel-provider-connections-section-header";
|
||||||
import { ConnectionsList } from "./connections-list";
|
import { ConnectionsList } from "./connections-list";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
|
||||||
|
|
||||||
const NoConnections = (props: { onCreate(): void; enabled: boolean }) => (
|
const NoConnections = (props: { onCreate(): void; enabled: boolean }) => (
|
||||||
<Box>
|
<Box>
|
||||||
<ChanelProviderConnectionsSectionHeader />
|
<ChanelProviderConnectionsSectionHeader />
|
||||||
<Text marginBottom={4} as="p">
|
<Text as="p">
|
||||||
No channels connected yet.{" "}
|
No channels connected yet.{" "}
|
||||||
{!props.enabled &&
|
{!props.enabled &&
|
||||||
"Ensure you have created a provider configuration that can be connected first."}
|
"Ensure you have created a provider configuration that can be connected first."}
|
||||||
</Text>
|
</Text>
|
||||||
{props.enabled && (
|
|
||||||
<ButtonsBox>
|
|
||||||
<Button onClick={props.onCreate}>Create first connection</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ChannelProviderConnectionList = () => {
|
export const ChannelProviderConnectionList = () => {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
|
||||||
const { data: connectionsData, refetch: refetchConnections } =
|
const {
|
||||||
trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
data: connectionsData,
|
||||||
|
refetch: refetchConnections,
|
||||||
|
isLoading,
|
||||||
|
} = trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
||||||
|
|
||||||
const { mutate: removeConnection } =
|
const { mutate: removeConnection } =
|
||||||
trpcClient.channelsProvidersConnection.removeConnection.useMutation({
|
trpcClient.channelsProvidersConnection.removeConnection.useMutation({
|
||||||
|
@ -45,19 +41,33 @@ export const ChannelProviderConnectionList = () => {
|
||||||
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
||||||
|
|
||||||
if (!providers) {
|
if (!providers) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = (connectionId: string) => {
|
const handleDelete = (connectionId: string) => {
|
||||||
removeConnection({ id: connectionId });
|
removeConnection({ id: connectionId });
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!connectionsData) {
|
if (isLoading || !connectionsData) {
|
||||||
return <Text>Loading</Text>;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
providers.length > 0 && (
|
||||||
|
<ButtonsBox>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setDialogOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add connection
|
||||||
|
</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
{dialogOpen && (
|
{dialogOpen && (
|
||||||
<AddConnectionModal
|
<AddConnectionModal
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
@ -79,17 +89,6 @@ export const ChannelProviderConnectionList = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{connectionsData.length > 0 && <ConnectionsList onRemove={handleDelete} />}
|
{connectionsData.length > 0 && <ConnectionsList onRemove={handleDelete} />}
|
||||||
{connectionsData.length > 0 && (
|
</Layout.AppSectionCard>
|
||||||
<ButtonsBox marginTop={6}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setDialogOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add connection
|
|
||||||
</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React from "react";
|
||||||
import { trpcClient } from "../trpc/trpc-client";
|
import { trpcClient } from "../trpc/trpc-client";
|
||||||
import { ChanelProviderConnectionsSectionHeader } from "./channel-provider-connections-section-header";
|
import { ChanelProviderConnectionsSectionHeader } from "./channel-provider-connections-section-header";
|
||||||
import { ProvidersResolver } from "../providers/providers-resolver";
|
import { ProvidersResolver } from "../providers/providers-resolver";
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { SkeletonLayout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
export const ConnectionsList = (props: { onRemove(connectionId: string): void }) => {
|
export const ConnectionsList = (props: { onRemove(connectionId: string): void }) => {
|
||||||
const { data } = trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
const { data } = trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
||||||
|
@ -12,7 +12,7 @@ export const ConnectionsList = (props: { onRemove(connectionId: string): void })
|
||||||
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
||||||
|
|
||||||
if (!data || !providers) {
|
if (!data || !providers) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { ProvidersConfig } from "../configuration";
|
||||||
|
|
||||||
import { ProvidersResolver } from "../providers/providers-resolver";
|
import { ProvidersResolver } from "../providers/providers-resolver";
|
||||||
import { trpcClient } from "../trpc/trpc-client";
|
import { trpcClient } from "../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../ui/buttons-box";
|
|
||||||
import { Skeleton } from "../ui/skeleton";
|
import { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
const ProvidersTable = (props: { providers: ProvidersConfig.AnyFullShape[] }) => {
|
const ProvidersTable = (props: { providers: ProvidersConfig.AnyFullShape[] }) => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
@ -47,30 +47,45 @@ export const ProvidersList = () => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
<ButtonsBox>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
push("/add-provider");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add first CMS configuration
|
||||||
|
</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Text as="p" marginBottom={4}>
|
<Text as="p" marginBottom={4}>
|
||||||
No configurations yet
|
No configurations yet
|
||||||
</Text>
|
</Text>
|
||||||
|
</Layout.AppSectionCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
<ButtonsBox>
|
<ButtonsBox>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
push("/add-provider");
|
push("/add-provider");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add first CMS configuration
|
Add CMS configuration
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonsBox>
|
</ButtonsBox>
|
||||||
</Box>
|
}
|
||||||
);
|
>
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
{data.length && (
|
{data.length && (
|
||||||
<Box>
|
<Box>
|
||||||
<Text variant="heading" as="h2" marginBottom={4}>
|
<Text variant="heading" as="h2" marginBottom={4}>
|
||||||
|
@ -79,15 +94,6 @@ export const ProvidersList = () => {
|
||||||
<ProvidersTable providers={data} />
|
<ProvidersTable providers={data} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<ButtonsBox marginTop={8}>
|
</Layout.AppSectionCard>
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
push("/add-provider");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add CMS configuration
|
|
||||||
</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,9 +7,7 @@ import { useForm } from "react-hook-form";
|
||||||
import { BuilderIoProviderConfig, SaleorProviderFieldsMappingKeys } from "../../configuration";
|
import { BuilderIoProviderConfig, SaleorProviderFieldsMappingKeys } from "../../configuration";
|
||||||
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../../ui/buttons-box";
|
import { ButtonsBox, SkeletonLayout, TextLink } from "@saleor/apps-ui";
|
||||||
import { TextLink } from "@saleor/apps-ui";
|
|
||||||
import { Skeleton } from "@/modules/ui/skeleton";
|
|
||||||
|
|
||||||
type FormShape = Omit<BuilderIoProviderConfig.InputShape, "type">;
|
type FormShape = Omit<BuilderIoProviderConfig.InputShape, "type">;
|
||||||
const FormSchema = BuilderIoProviderConfig.Schema.Input.omit({ type: true });
|
const FormSchema = BuilderIoProviderConfig.Schema.Input.omit({ type: true });
|
||||||
|
@ -207,7 +205,7 @@ const EditFormVariant = (props: { configId: string }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type !== "builder.io") {
|
if (data.type !== "builder.io") {
|
||||||
|
|
|
@ -9,8 +9,7 @@ import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
import { ContentfulProviderConfig } from "../../configuration/schemas/contentful-provider.schema";
|
import { ContentfulProviderConfig } from "../../configuration/schemas/contentful-provider.schema";
|
||||||
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { ButtonsBox } from "../../ui/buttons-box";
|
import { ButtonsBox, TextLink } from "@saleor/apps-ui";
|
||||||
import { TextLink } from "@saleor/apps-ui";
|
|
||||||
import { SaleorProviderFieldsMappingKeys } from "@/modules/configuration";
|
import { SaleorProviderFieldsMappingKeys } from "@/modules/configuration";
|
||||||
|
|
||||||
type FormSchema = Omit<ContentfulProviderConfig.InputShape, "type">;
|
type FormSchema = Omit<ContentfulProviderConfig.InputShape, "type">;
|
||||||
|
|
|
@ -8,8 +8,8 @@ import React, { useEffect, useMemo } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../../ui/buttons-box";
|
|
||||||
import { DatocmsProviderConfig } from "@/modules/configuration/schemas/datocms-provider.schema";
|
import { DatocmsProviderConfig } from "@/modules/configuration/schemas/datocms-provider.schema";
|
||||||
|
import { ButtonsBox } from "@saleor/apps-ui";
|
||||||
|
|
||||||
type FormShape = Omit<DatocmsProviderConfig.InputShape, "type">;
|
type FormShape = Omit<DatocmsProviderConfig.InputShape, "type">;
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@ import React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../../ui/buttons-box";
|
import { ButtonsBox, TextLink } from "@saleor/apps-ui";
|
||||||
import { TextLink } from "@saleor/apps-ui";
|
|
||||||
|
|
||||||
type FormShape = Omit<PayloadCmsProviderConfig.InputShape, "type">;
|
type FormShape = Omit<PayloadCmsProviderConfig.InputShape, "type">;
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@ import { useForm } from "react-hook-form";
|
||||||
import { SaleorProviderFieldsMappingKeys, StrapiProviderConfig } from "../../configuration";
|
import { SaleorProviderFieldsMappingKeys, StrapiProviderConfig } from "../../configuration";
|
||||||
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
import { printSaleorProductFields } from "../../configuration/print-saleor-product-fields";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { ButtonsBox } from "../../ui/buttons-box";
|
import { ButtonsBox, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
import { Skeleton } from "@/modules/ui/skeleton";
|
|
||||||
|
|
||||||
type FormShape = Omit<StrapiProviderConfig.InputShape, "type">;
|
type FormShape = Omit<StrapiProviderConfig.InputShape, "type">;
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ const EditFormVariant = (props: { configId: string }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type !== "strapi") {
|
if (data.type !== "strapi") {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as trpcNext from "@trpc/server/adapters/next";
|
import * as trpcNext from "@trpc/server/adapters/next";
|
||||||
import { SALEOR_AUTHORIZATION_BEARER_HEADER, SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
|
import { SALEOR_AUTHORIZATION_BEARER_HEADER, SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
|
||||||
import { inferAsyncReturnType } from "@trpc/server";
|
import { inferAsyncReturnType } from "@trpc/server";
|
||||||
import { getBaseUrl } from "@/modules/shared/get-base-url";
|
import { getAppBaseUrl } from "@saleor/apps-shared";
|
||||||
|
|
||||||
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
||||||
const baseUrl = getBaseUrl(req.headers);
|
const baseUrl = getAppBaseUrl(req.headers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { Box, PropsWithBox, Text } from "@saleor/macaw-ui/next";
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
// todo move to shared
|
|
||||||
export const AppSection = ({
|
|
||||||
heading,
|
|
||||||
sideContent,
|
|
||||||
mainContent,
|
|
||||||
includePadding = true,
|
|
||||||
...props
|
|
||||||
}: PropsWithBox<{
|
|
||||||
heading: string;
|
|
||||||
sideContent?: ReactNode;
|
|
||||||
mainContent: ReactNode;
|
|
||||||
includePadding?: boolean;
|
|
||||||
}>) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
as="section"
|
|
||||||
__gridTemplateColumns={"400px auto"}
|
|
||||||
display={"grid"}
|
|
||||||
gap={10}
|
|
||||||
__maxWidth={"1200px"}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Text as="h2" variant={"heading"} size={"large"} marginBottom={1.5}>
|
|
||||||
{heading}
|
|
||||||
</Text>
|
|
||||||
{sideContent}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
borderStyle={"solid"}
|
|
||||||
borderColor={"neutralPlain"}
|
|
||||||
borderWidth={1}
|
|
||||||
padding={includePadding ? 5 : 0}
|
|
||||||
borderRadius={4}
|
|
||||||
>
|
|
||||||
{mainContent}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { Box, BoxProps } from "@saleor/macaw-ui/next";
|
|
||||||
|
|
||||||
// TODO: Make it more generic, move to shared or contribute to macaw
|
|
||||||
const Section = (props: BoxProps) => {
|
|
||||||
return (
|
|
||||||
<Box display="grid" gap={2} {...props}>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="50%"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="70%"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="60%"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Skeleton = { Section };
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { CMSProviders } from "@/modules/providers/providers-registry";
|
import { CMSProviders } from "@/modules/providers/providers-registry";
|
||||||
import { AppHeader } from "@/modules/ui/app-header";
|
import { AppHeader } from "@/modules/ui/app-header";
|
||||||
import { AppSection } from "@/modules/ui/app-section";
|
import { Breadcrumbs, Layout } from "@saleor/apps-ui";
|
||||||
import { Breadcrumbs } from "@saleor/apps-ui";
|
|
||||||
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
@ -16,14 +15,15 @@ const AddProviderPage: NextPage = () => {
|
||||||
text="Connect CMS platforms to the App."
|
text="Connect CMS platforms to the App."
|
||||||
breadcrumbs={[<Breadcrumbs.Item key="provider">Add Provider</Breadcrumbs.Item>]}
|
breadcrumbs={[<Breadcrumbs.Item key="provider">Add Provider</Breadcrumbs.Item>]}
|
||||||
/>
|
/>
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
heading="Select CMS provider"
|
heading="Select CMS provider"
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text>App allows to connect one or more CMS platforms. You can add more later.</Text>
|
<Text>App allows to connect one or more CMS platforms. You can add more later.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
mainContent={
|
>
|
||||||
|
<Layout.AppSectionCard>
|
||||||
<Box
|
<Box
|
||||||
display="grid"
|
display="grid"
|
||||||
__gridTemplateColumns="auto auto auto"
|
__gridTemplateColumns="auto auto auto"
|
||||||
|
@ -54,8 +54,8 @@ const AddProviderPage: NextPage = () => {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
}
|
</Layout.AppSectionCard>
|
||||||
/>
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,7 @@ import { CMSType } from "@/modules/providers/providers-registry";
|
||||||
import { ProvidersResolver } from "@/modules/providers/providers-resolver";
|
import { ProvidersResolver } from "@/modules/providers/providers-resolver";
|
||||||
|
|
||||||
import { AppHeader } from "@/modules/ui/app-header";
|
import { AppHeader } from "@/modules/ui/app-header";
|
||||||
import { AppSection } from "@/modules/ui/app-section";
|
import { Breadcrumbs, Layout } from "@saleor/apps-ui";
|
||||||
import { Breadcrumbs } from "@saleor/apps-ui";
|
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
@ -32,7 +31,7 @@ const AddProviderPage: NextPage = () => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
heading={`Set up ${provider.displayName}`}
|
heading={`Set up ${provider.displayName}`}
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -40,8 +39,11 @@ const AddProviderPage: NextPage = () => {
|
||||||
{provider.formSideInfo && <Box marginTop={6}>{provider.formSideInfo}</Box>}
|
{provider.formSideInfo && <Box marginTop={6}>{provider.formSideInfo}</Box>}
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
mainContent={<FormComponent />}
|
>
|
||||||
/>
|
<Layout.AppSectionCard>
|
||||||
|
<FormComponent />
|
||||||
|
</Layout.AppSectionCard>
|
||||||
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { BulkSyncView } from "@/modules/bulk-sync/bulk-sync-view";
|
import { BulkSyncView } from "@/modules/bulk-sync/bulk-sync-view";
|
||||||
import { trpcClient } from "@/modules/trpc/trpc-client";
|
import { trpcClient } from "@/modules/trpc/trpc-client";
|
||||||
|
import { SkeletonLayout } from "@saleor/apps-ui";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { Text } from "@saleor/macaw-ui/next";
|
|
||||||
import { Skeleton } from "@/modules/ui/skeleton";
|
|
||||||
|
|
||||||
const BulkSyncPage: NextPage = () => {
|
const BulkSyncPage: NextPage = () => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
|
@ -23,7 +22,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!parsedID,
|
enabled: !!parsedID,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -36,7 +35,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!connection,
|
enabled: !!connection,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((providerFetched && !provider) || (connectionFetched && !connection)) {
|
if ((providerFetched && !provider) || (connectionFetched && !connection)) {
|
||||||
|
@ -45,7 +44,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionLoading || providerLoading) {
|
if (connectionLoading || providerLoading) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(provider && connection)) {
|
if (!(provider && connection)) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { BulkSyncSection } from "@/modules/bulk-sync/bulk-sync-section";
|
||||||
import { ChannelProviderConnectionList } from "@/modules/channel-provider-connection/channels-provider-connection-list";
|
import { ChannelProviderConnectionList } from "@/modules/channel-provider-connection/channels-provider-connection-list";
|
||||||
import { ProvidersList } from "@/modules/providers-listing/providers-list";
|
import { ProvidersList } from "@/modules/providers-listing/providers-list";
|
||||||
import { AppHeader } from "@/modules/ui/app-header";
|
import { AppHeader } from "@/modules/ui/app-header";
|
||||||
import { AppSection } from "@/modules/ui/app-section";
|
import { Layout } from "@saleor/apps-ui";
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ const ConfigurationPage: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
marginBottom={14}
|
marginBottom={14}
|
||||||
heading="Providers configuration"
|
heading="Providers configuration"
|
||||||
sideContent={
|
sideContent={
|
||||||
|
@ -18,9 +18,10 @@ const ConfigurationPage: NextPage = () => {
|
||||||
<Text>Configure one or more CMS providers to synchronize Saleor products.</Text>
|
<Text>Configure one or more CMS providers to synchronize Saleor products.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
mainContent={<ProvidersList />}
|
>
|
||||||
/>
|
<ProvidersList />
|
||||||
<AppSection
|
</Layout.AppSection>
|
||||||
|
<Layout.AppSection
|
||||||
marginBottom={14}
|
marginBottom={14}
|
||||||
heading="Automatic synchronization"
|
heading="Automatic synchronization"
|
||||||
sideContent={
|
sideContent={
|
||||||
|
@ -31,9 +32,10 @@ const ConfigurationPage: NextPage = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
mainContent={<ChannelProviderConnectionList />}
|
>
|
||||||
/>
|
<ChannelProviderConnectionList />
|
||||||
<AppSection
|
</Layout.AppSection>
|
||||||
|
<Layout.AppSection
|
||||||
heading="Initial sync"
|
heading="Initial sync"
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -44,8 +46,9 @@ const ConfigurationPage: NextPage = () => {
|
||||||
<Text as="p">Its recommended to run this flow initially, once app is configured.</Text>
|
<Text as="p">Its recommended to run this flow initially, once app is configured.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
mainContent={<BulkSyncSection />}
|
>
|
||||||
/>
|
<BulkSyncSection />
|
||||||
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,9 +2,7 @@ import { ProvidersResolver } from "@/modules/providers/providers-resolver";
|
||||||
|
|
||||||
import { trpcClient } from "@/modules/trpc/trpc-client";
|
import { trpcClient } from "@/modules/trpc/trpc-client";
|
||||||
import { AppHeader } from "@/modules/ui/app-header";
|
import { AppHeader } from "@/modules/ui/app-header";
|
||||||
import { AppSection } from "@/modules/ui/app-section";
|
import { Breadcrumbs, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
||||||
import { Skeleton } from "@/modules/ui/skeleton";
|
|
||||||
import { Breadcrumbs } from "@saleor/apps-ui";
|
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
@ -20,7 +18,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!configId,
|
enabled: !!configId,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
|
@ -28,7 +26,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFetched && !data) {
|
if (isFetched && !data) {
|
||||||
|
@ -38,7 +36,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditForm = ProvidersResolver.getEditProviderFormComponent(provider.type);
|
const EditForm = ProvidersResolver.getEditProviderFormComponent(provider.type);
|
||||||
|
@ -53,13 +51,16 @@ const EditProviderPage: NextPage = () => {
|
||||||
<Breadcrumbs.Item key="configname">{data?.configName}</Breadcrumbs.Item>,
|
<Breadcrumbs.Item key="configname">{data?.configName}</Breadcrumbs.Item>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
heading="Edit CMS configuration"
|
heading="Edit CMS configuration"
|
||||||
mainContent={<EditForm configId={configId} />}
|
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>{provider.formSideInfo && <Box marginTop={6}>{provider.formSideInfo}</Box>}</Box>
|
<Box>{provider.formSideInfo && <Box marginTop={6}>{provider.formSideInfo}</Box>}</Box>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<Layout.AppSectionCard>
|
||||||
|
<EditForm configId={configId} />
|
||||||
|
</Layout.AppSectionCard>
|
||||||
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from "../modules/configuration/configuration";
|
} from "../modules/configuration/configuration";
|
||||||
import { AlgoliaSearchProvider } from "../lib/algolia/algoliaSearchProvider";
|
import { AlgoliaSearchProvider } from "../lib/algolia/algoliaSearchProvider";
|
||||||
import { trpcClient } from "../modules/trpc/trpc-client";
|
import { trpcClient } from "../modules/trpc/trpc-client";
|
||||||
|
import { Layout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
export const AlgoliaConfigurationForm = () => {
|
export const AlgoliaConfigurationForm = () => {
|
||||||
const { notifyError, notifySuccess } = useDashboardNotification();
|
const { notifyError, notifySuccess } = useDashboardNotification();
|
||||||
|
@ -76,56 +77,53 @@ export const AlgoliaConfigurationForm = () => {
|
||||||
const isFormDisabled = isMutationLoading || isQueryLoading;
|
const isFormDisabled = isMutationLoading || isQueryLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
<form onSubmit={onFormSubmit}>
|
as="form"
|
||||||
<Box padding={5}>
|
onSubmit={onFormSubmit}
|
||||||
<Box marginBottom={5}>
|
footer={
|
||||||
<Input
|
<Box display={"flex"} justifyContent={"flex-end"}>
|
||||||
control={control}
|
|
||||||
name="appId"
|
|
||||||
disabled={isFormDisabled}
|
|
||||||
required
|
|
||||||
label="Application ID"
|
|
||||||
/* cspell:disable-next-line */
|
|
||||||
helperText="Usually 10 characters, e.g. XYZAAABB00"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box marginBottom={5} key={"secret"} /* todo why is this "key" here? */>
|
|
||||||
<Input
|
|
||||||
control={control}
|
|
||||||
name="secretKey"
|
|
||||||
disabled={isFormDisabled}
|
|
||||||
required
|
|
||||||
label="Admin API Key"
|
|
||||||
helperText="In Algolia dashboard it's a masked field"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
control={control}
|
|
||||||
name="indexNamePrefix"
|
|
||||||
disabled={isFormDisabled}
|
|
||||||
label="Index name prefix"
|
|
||||||
helperText='Optional prefix, you can add "test" or "staging" to test the app'
|
|
||||||
/>
|
|
||||||
|
|
||||||
{credentialsValidationError && (
|
|
||||||
<Box marginTop={5}>
|
|
||||||
<Text color={"textCriticalDefault"}>
|
|
||||||
Could not connect to Algolia. Please verify your credentials
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Divider margin={0} marginTop={5} />
|
|
||||||
|
|
||||||
<Box paddingX={5} paddingY={3} display={"flex"} justifyContent={"flex-end"}>
|
|
||||||
<Button disabled={isFormDisabled} type="submit" variant="primary">
|
<Button disabled={isFormDisabled} type="submit" variant="primary">
|
||||||
{isFormDisabled ? "Loading..." : "Save"}
|
{isFormDisabled ? "Loading..." : "Save"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</form>
|
}
|
||||||
</Box>
|
>
|
||||||
|
<Box>
|
||||||
|
<Input
|
||||||
|
control={control}
|
||||||
|
name="appId"
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
required
|
||||||
|
label="Application ID"
|
||||||
|
helperText="Usually 10 characters, e.g. XYZAAABB00"
|
||||||
|
/>
|
||||||
|
<Box marginBottom={5} key={"secret"} /* todo why is this "key" here? */>
|
||||||
|
<Input
|
||||||
|
control={control}
|
||||||
|
name="secretKey"
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
required
|
||||||
|
label="Admin API Key"
|
||||||
|
helperText="In Algolia dashboard it's a masked field"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
control={control}
|
||||||
|
name="indexNamePrefix"
|
||||||
|
disabled={isFormDisabled}
|
||||||
|
label="Index name prefix"
|
||||||
|
helperText='Optional prefix, you can add "test" or "staging" to test the app'
|
||||||
|
/>
|
||||||
|
|
||||||
|
{credentialsValidationError && (
|
||||||
|
<Box marginTop={5}>
|
||||||
|
<Text color={"textCriticalDefault"}>
|
||||||
|
Could not connect to Algolia. Please verify your credentials
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Layout.AppSectionCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
import { ButtonsBox, Layout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
export const AlgoliaFieldsSelectionForm = () => {
|
export const AlgoliaFieldsSelectionForm = () => {
|
||||||
const { notifySuccess } = useDashboardNotification();
|
const { notifySuccess } = useDashboardNotification();
|
||||||
|
@ -35,46 +36,46 @@ export const AlgoliaFieldsSelectionForm = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
<form
|
as="form"
|
||||||
onSubmit={handleSubmit((values) => {
|
onSubmit={handleSubmit((values) => {
|
||||||
const selectedValues = Object.entries(values)
|
const selectedValues = Object.entries(values)
|
||||||
.filter(([key, selected]) => selected)
|
.filter(([key, selected]) => selected)
|
||||||
.map(([key]) => key);
|
.map(([key]) => key);
|
||||||
|
|
||||||
mutate({
|
mutate({
|
||||||
enabledAlgoliaFields: selectedValues,
|
enabledAlgoliaFields: selectedValues,
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
>
|
footer={
|
||||||
<Box padding={5}>
|
<ButtonsBox>
|
||||||
{AlgoliaRootFieldsKeys.map((field) => (
|
|
||||||
<Box key={field} marginBottom={5}>
|
|
||||||
<Controller
|
|
||||||
name={field}
|
|
||||||
control={control}
|
|
||||||
render={({ field: { value, onChange } }) => {
|
|
||||||
return (
|
|
||||||
<Checkbox
|
|
||||||
onCheckedChange={(v) => {
|
|
||||||
onChange(v);
|
|
||||||
}}
|
|
||||||
checked={value}
|
|
||||||
name={field}
|
|
||||||
>
|
|
||||||
{AlgoliaRootFieldsLabelsMap[field]}
|
|
||||||
</Checkbox>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
<Divider margin={0} marginTop={5} />
|
|
||||||
<Box padding={5} display="flex" justifyContent="flex-end">
|
|
||||||
<Button type="submit">Save</Button>
|
<Button type="submit">Save</Button>
|
||||||
</Box>
|
</ButtonsBox>
|
||||||
</form>
|
}
|
||||||
</Box>
|
>
|
||||||
|
<Box>
|
||||||
|
{AlgoliaRootFieldsKeys.map((field) => (
|
||||||
|
<Box key={field} marginBottom={5}>
|
||||||
|
<Controller
|
||||||
|
name={field}
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange } }) => {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
onCheckedChange={(v) => {
|
||||||
|
onChange(v);
|
||||||
|
}}
|
||||||
|
checked={value}
|
||||||
|
name={field}
|
||||||
|
>
|
||||||
|
{AlgoliaRootFieldsLabelsMap[field]}
|
||||||
|
</Checkbox>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Layout.AppSectionCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { Box, PropsWithBox, Text } from "@saleor/macaw-ui/next";
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
// todo move to shared
|
|
||||||
export const AppSection = ({
|
|
||||||
heading,
|
|
||||||
sideContent,
|
|
||||||
mainContent,
|
|
||||||
includePadding = false,
|
|
||||||
...props
|
|
||||||
}: PropsWithBox<{
|
|
||||||
heading: string;
|
|
||||||
sideContent?: ReactNode;
|
|
||||||
mainContent: ReactNode;
|
|
||||||
includePadding?: boolean;
|
|
||||||
}>) => {
|
|
||||||
return (
|
|
||||||
<Box as="section" __gridTemplateColumns={"400px auto"} display={"grid"} gap={10} {...props}>
|
|
||||||
<Box>
|
|
||||||
<Text as="h2" variant={"heading"} size={"large"} marginBottom={1.5}>
|
|
||||||
{heading}
|
|
||||||
</Text>
|
|
||||||
{sideContent}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
borderStyle={"solid"}
|
|
||||||
borderColor={"neutralPlain"}
|
|
||||||
borderWidth={1}
|
|
||||||
padding={includePadding ? 5 : 0}
|
|
||||||
borderRadius={4}
|
|
||||||
>
|
|
||||||
{mainContent}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { AlgoliaSearchProvider } from "../lib/algolia/algoliaSearchProvider";
|
import { AlgoliaSearchProvider } from "../lib/algolia/algoliaSearchProvider";
|
||||||
import { Products, useQueryAllProducts } from "./useQueryAllProducts";
|
import { Products, useQueryAllProducts } from "./useQueryAllProducts";
|
||||||
import { trpcClient } from "../modules/trpc/trpc-client";
|
import { trpcClient } from "../modules/trpc/trpc-client";
|
||||||
|
import { Layout } from "@saleor/apps-ui";
|
||||||
|
|
||||||
const BATCH_SIZE = 100;
|
const BATCH_SIZE = 100;
|
||||||
|
|
||||||
|
@ -63,7 +64,19 @@ export const ImportProductsToAlgolia = () => {
|
||||||
}, [searchProvider, currentProductIndex, isAlgoliaImporting, products]);
|
}, [searchProvider, currentProductIndex, isAlgoliaImporting, products]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box __cursor={started ? "wait" : "auto"}>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
searchProvider &&
|
||||||
|
algoliaConfigured && (
|
||||||
|
<Box display={"flex"} justifyContent={"flex-end"}>
|
||||||
|
<Button disabled={started || !searchProvider} onClick={importProducts}>
|
||||||
|
Start importing
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
__cursor={started ? "wait" : "auto"}
|
||||||
|
>
|
||||||
{searchProvider && algoliaConfigured ? (
|
{searchProvider && algoliaConfigured ? (
|
||||||
<Box>
|
<Box>
|
||||||
<Text variant={"heading"} as={"p"} marginBottom={1.5}>
|
<Text variant={"heading"} as={"p"} marginBottom={1.5}>
|
||||||
|
@ -75,11 +88,6 @@ export const ImportProductsToAlgolia = () => {
|
||||||
<Text marginBottom={5} variant={"bodyStrong"}>
|
<Text marginBottom={5} variant={"bodyStrong"}>
|
||||||
Do not close the app - its running client-side
|
Do not close the app - its running client-side
|
||||||
</Text>
|
</Text>
|
||||||
<Box display={"flex"} justifyContent={"flex-end"}>
|
|
||||||
<Button disabled={started || !searchProvider} onClick={importProducts}>
|
|
||||||
Start importing
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -112,7 +120,7 @@ export const ImportProductsToAlgolia = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Layout.AppSectionCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { TextLink } from "@saleor/apps-ui";
|
import { Layout, TextLink } from "@saleor/apps-ui";
|
||||||
import { useIndicesSetupMutation } from "../lib/useIndicesSetup";
|
import { useIndicesSetupMutation } from "../lib/useIndicesSetup";
|
||||||
import { trpcClient } from "../modules/trpc/trpc-client";
|
import { trpcClient } from "../modules/trpc/trpc-client";
|
||||||
|
|
||||||
|
@ -12,7 +12,19 @@ export const IndicesSettings = () => {
|
||||||
algoliaConfiguration?.appConfig?.appId && algoliaConfiguration?.appConfig?.secretKey;
|
algoliaConfiguration?.appConfig?.appId && algoliaConfiguration?.appConfig?.secretKey;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
<Box display={"flex"} justifyContent={"flex-end"}>
|
||||||
|
<Button
|
||||||
|
disabled={!isConfigured}
|
||||||
|
onClick={() => updateWebhooksMutation.mutate()}
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
Update indices configuration
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Text variant={"heading"} as={"p"} marginBottom={1.5}>
|
<Text variant={"heading"} as={"p"} marginBottom={1.5}>
|
||||||
Performing this operation will update indices to use recommended settings:
|
Performing this operation will update indices to use recommended settings:
|
||||||
|
@ -60,16 +72,7 @@ export const IndicesSettings = () => {
|
||||||
Please note - if indices are already configured, this operation will overwrite settings
|
Please note - if indices are already configured, this operation will overwrite settings
|
||||||
mentioned above.
|
mentioned above.
|
||||||
</Text>
|
</Text>
|
||||||
<Box display={"flex"} justifyContent={"flex-end"}>
|
|
||||||
<Button
|
|
||||||
disabled={!isConfigured}
|
|
||||||
onClick={() => updateWebhooksMutation.mutate()}
|
|
||||||
variant="primary"
|
|
||||||
>
|
|
||||||
Update indices configuration
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Layout.AppSectionCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,5 +2,6 @@ import debugPkg from "debug";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* todo rewrite to pino logger
|
* todo rewrite to pino logger
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
export const createDebug = (namespace: string) => debugPkg.debug(`app-search:${namespace}`);
|
export const createDebug = (namespace: string) => debugPkg.debug(`app-search:${namespace}`);
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
/**
|
|
||||||
* Extracts the app's url from headers from the response.
|
|
||||||
*/
|
|
||||||
export const getBaseUrl = (headers: { [name: string]: string | string[] | undefined }): string => {
|
|
||||||
const { host, "x-forwarded-proto": xForwardedProto = "http" } = headers;
|
|
||||||
|
|
||||||
const xForwardedProtos = Array.isArray(xForwardedProto)
|
|
||||||
? xForwardedProto.join(",")
|
|
||||||
: xForwardedProto;
|
|
||||||
const protocols = xForwardedProtos.split(",");
|
|
||||||
// prefer https over other protocols
|
|
||||||
const protocol = protocols.find((el) => el === "https") || protocols[0];
|
|
||||||
|
|
||||||
return `${protocol}://${host}`;
|
|
||||||
};
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
|
||||||
import { useTheme } from "@saleor/macaw-ui/next";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
// todo move to shared
|
|
||||||
export function ThemeSynchronizer() {
|
|
||||||
const { appBridgeState } = useAppBridge();
|
|
||||||
const { setTheme } = useTheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!setTheme || !appBridgeState?.theme) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appBridgeState.theme === "light") {
|
|
||||||
setTheme("defaultLight");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appBridgeState.theme === "dark") {
|
|
||||||
setTheme("defaultDark");
|
|
||||||
}
|
|
||||||
}, [appBridgeState?.theme, setTheme]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
|
@ -34,7 +34,9 @@ export const configurationRouter = router({
|
||||||
*/
|
*/
|
||||||
const data = await fetchLegacyConfiguration(settingsManager, domain);
|
const data = await fetchLegacyConfiguration(settingsManager, domain);
|
||||||
|
|
||||||
config.setAlgoliaSettings(data);
|
if (data) {
|
||||||
|
config.setAlgoliaSettings(data);
|
||||||
|
}
|
||||||
|
|
||||||
return config.getConfig();
|
return config.getConfig();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,18 @@ import { AppConfigurationFields } from "./configuration";
|
||||||
export const fetchLegacyConfiguration = async (
|
export const fetchLegacyConfiguration = async (
|
||||||
settingsManager: SettingsManager,
|
settingsManager: SettingsManager,
|
||||||
domain: string,
|
domain: string,
|
||||||
) => {
|
): Promise<AppConfigurationFields | null> => {
|
||||||
const data: AppConfigurationFields = {
|
const secretKey = await settingsManager.get("secretKey", domain);
|
||||||
secretKey: (await settingsManager.get("secretKey", domain)) || "",
|
const appId = await settingsManager.get("appId", domain);
|
||||||
appId: (await settingsManager.get("appId", domain)) || "",
|
const indexNamePrefix = await settingsManager.get("indexNamePrefix", domain);
|
||||||
indexNamePrefix: (await settingsManager.get("indexNamePrefix", domain)) || "",
|
|
||||||
};
|
|
||||||
|
|
||||||
return data;
|
if (secretKey && appId) {
|
||||||
|
return {
|
||||||
|
appId,
|
||||||
|
secretKey,
|
||||||
|
indexNamePrefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,12 +3,10 @@ import "@saleor/macaw-ui/next/style";
|
||||||
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
import { GraphQLProvider } from "../providers/GraphQLProvider";
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
||||||
import { ThemeSynchronizer } from "../lib/theme-synchronizer";
|
|
||||||
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
||||||
import { NoSSRWrapper } from "@saleor/apps-shared";
|
import { GraphQLProvider, NoSSRWrapper, ThemeSynchronizer } from "@saleor/apps-shared";
|
||||||
import { trpcClient } from "../modules/trpc/trpc-client";
|
import { trpcClient } from "../modules/trpc/trpc-client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,9 +7,8 @@ import {
|
||||||
} from "../../domain/WebhookActivityToggler.service";
|
} from "../../domain/WebhookActivityToggler.service";
|
||||||
import { createLogger } from "../../lib/logger";
|
import { createLogger } from "../../lib/logger";
|
||||||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
import { createGraphQLClient, getAppBaseUrl } from "@saleor/apps-shared";
|
||||||
import { Client } from "urql";
|
import { Client } from "urql";
|
||||||
import { getBaseUrl } from "../../lib/getBaseUrl";
|
|
||||||
import { isConfigured } from "../../lib/algolia/is-configured";
|
import { isConfigured } from "../../lib/algolia/is-configured";
|
||||||
|
|
||||||
const logger = createLogger({
|
const logger = createLogger({
|
||||||
|
@ -53,7 +52,7 @@ export const recreateWebhooksHandlerFactory =
|
||||||
|
|
||||||
logger.debug(settings, "fetched settings");
|
logger.debug(settings, "fetched settings");
|
||||||
|
|
||||||
const baseUrl = getBaseUrl(req.headers);
|
const baseUrl = getAppBaseUrl(req.headers);
|
||||||
const enableWebhooks = isConfigured({
|
const enableWebhooks = isConfigured({
|
||||||
configuration: {
|
configuration: {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
|
||||||
import { PropsWithChildren } from "react";
|
|
||||||
import { Provider } from "urql";
|
|
||||||
|
|
||||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
|
||||||
|
|
||||||
export function GraphQLProvider(props: PropsWithChildren<{}>) {
|
|
||||||
const { appBridgeState } = useAppBridge();
|
|
||||||
const saleorApiUrl = appBridgeState?.saleorApiUrl!;
|
|
||||||
|
|
||||||
if (!appBridgeState?.saleorApiUrl) {
|
|
||||||
return <div {...props}></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = createGraphQLClient({
|
|
||||||
saleorApiUrl,
|
|
||||||
token: appBridgeState.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
return <Provider value={client} {...props} />;
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { AppSection } from "../../components/AppSection";
|
import { Layout } from "@saleor/apps-ui";
|
||||||
import { AlgoliaConfigurationForm } from "../../components/AlgoliaConfigurationForm";
|
import { AlgoliaConfigurationForm } from "../../components/AlgoliaConfigurationForm";
|
||||||
import { ImportProductsToAlgolia } from "../../components/ImportProductsToAlgolia";
|
import { ImportProductsToAlgolia } from "../../components/ImportProductsToAlgolia";
|
||||||
import { WebhooksStatus } from "../../components/WebhooksStatus";
|
import { WebhooksStatus } from "../../components/WebhooksStatus";
|
||||||
|
@ -20,17 +20,20 @@ export const ConfigurationView = () => {
|
||||||
</Text>
|
</Text>
|
||||||
<MainInstructions marginTop={1.5} />
|
<MainInstructions marginTop={1.5} />
|
||||||
</Box>
|
</Box>
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
includePadding
|
includePadding
|
||||||
heading="Webhooks status"
|
heading="Webhooks status"
|
||||||
sideContent={<WebhooksStatusInstructions />}
|
sideContent={<WebhooksStatusInstructions />}
|
||||||
mainContent={<WebhooksStatus />}
|
>
|
||||||
/>
|
<Layout.AppSectionCard>
|
||||||
|
<WebhooksStatus />
|
||||||
|
</Layout.AppSectionCard>
|
||||||
|
</Layout.AppSection>
|
||||||
|
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
|
includePadding={false}
|
||||||
marginTop={14}
|
marginTop={14}
|
||||||
heading="Algolia settings"
|
heading="Algolia settings"
|
||||||
mainContent={<AlgoliaConfigurationForm />}
|
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text as="p" marginBottom={1.5}>
|
<Text as="p" marginBottom={1.5}>
|
||||||
|
@ -44,11 +47,14 @@ export const ConfigurationView = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<AppSection
|
<AlgoliaConfigurationForm />
|
||||||
|
</Layout.AppSection>
|
||||||
|
|
||||||
|
<Layout.AppSection
|
||||||
|
includePadding={false}
|
||||||
marginTop={14}
|
marginTop={14}
|
||||||
heading="Fields filtering"
|
heading="Algolia fields filtering"
|
||||||
mainContent={<AlgoliaFieldsSelectionForm />}
|
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text as="p" marginBottom={1.5}>
|
<Text as="p" marginBottom={1.5}>
|
||||||
|
@ -60,30 +66,35 @@ export const ConfigurationView = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
<AppSection
|
<AlgoliaFieldsSelectionForm />
|
||||||
|
</Layout.AppSection>
|
||||||
|
|
||||||
|
<Layout.AppSection
|
||||||
includePadding
|
includePadding
|
||||||
marginTop={14}
|
marginTop={14}
|
||||||
heading="Index products"
|
heading="Index products"
|
||||||
mainContent={<ImportProductsToAlgolia />}
|
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text>Perform initial index of all products in your Saleor database</Text>
|
<Text>Perform initial index of all products in your Saleor database</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<ImportProductsToAlgolia />
|
||||||
|
</Layout.AppSection>
|
||||||
|
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
includePadding
|
includePadding
|
||||||
marginTop={14}
|
marginTop={14}
|
||||||
heading="Set indices settings"
|
heading="Set indices settings"
|
||||||
mainContent={<IndicesSettings />}
|
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text>Sets up indices with recommended settings.</Text>
|
<Text>Sets up indices with recommended settings.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<IndicesSettings />
|
||||||
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,8 @@ const nextConfig = () => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw"],
|
// TODO Infer names dynamically from disk
|
||||||
|
transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw", "@saleor/trpc"],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/apps-ui": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.127",
|
||||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||||
|
"@saleor/trpc": "workspace:*",
|
||||||
"@segment/analytics-node": "^1.1.0",
|
"@segment/analytics-node": "^1.1.0",
|
||||||
"@sentry/nextjs": "7.55.2",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@tanstack/react-query": "^4.29.19",
|
"@tanstack/react-query": "^4.29.19",
|
||||||
|
|
|
@ -4,11 +4,9 @@ import { RootConfig } from "../schemas/root-config.schema";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { TextLink } from "@saleor/apps-ui";
|
import { ButtonsBox, Layout, SkeletonLayout, TextLink } from "@saleor/apps-ui";
|
||||||
import { Text } from "@saleor/macaw-ui/next";
|
import { Text } from "@saleor/macaw-ui/next";
|
||||||
import { ButtonsBox } from "@/modules/ui/buttons-box";
|
|
||||||
import { trpcClient } from "@/modules/trpc/trpc-client";
|
import { trpcClient } from "@/modules/trpc/trpc-client";
|
||||||
import { Skeleton } from "@/modules/ui/skeleton";
|
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
|
||||||
const Schema = RootConfig.Schema.unwrap();
|
const Schema = RootConfig.Schema.unwrap();
|
||||||
|
@ -22,7 +20,15 @@ const SegmentConfigFormBase = (props: { values: Shape; onSubmit(values: Shape):
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box as="form" onSubmit={handleSubmit(props.onSubmit)}>
|
<Layout.AppSectionCard
|
||||||
|
footer={
|
||||||
|
<ButtonsBox>
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
}
|
||||||
|
as="form"
|
||||||
|
onSubmit={handleSubmit(props.onSubmit)}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
control={control}
|
control={control}
|
||||||
name="segmentWriteKey"
|
name="segmentWriteKey"
|
||||||
|
@ -41,10 +47,7 @@ const SegmentConfigFormBase = (props: { values: Shape; onSubmit(values: Shape):
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ButtonsBox marginTop={6}>
|
</Layout.AppSectionCard>
|
||||||
<Button type="submit">Save</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,7 +66,7 @@ export const SegmentConfigForm = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <Skeleton.Section />;
|
return <SkeletonLayout.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,42 +1,13 @@
|
||||||
import { httpBatchLink } from "@trpc/client";
|
|
||||||
import { createTRPCNext } from "@trpc/next";
|
import { createTRPCNext } from "@trpc/next";
|
||||||
|
|
||||||
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
import { createHttpBatchLink } from "@saleor/trpc";
|
||||||
import { appBridgeInstance } from "../../pages/_app";
|
import { appBridgeInstance } from "../../pages/_app";
|
||||||
import { AppRouter } from "./trpc-app-router";
|
import { AppRouter } from "./trpc-app-router";
|
||||||
|
|
||||||
function getBaseUrl() {
|
|
||||||
if (typeof window !== "undefined") return "";
|
|
||||||
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
|
|
||||||
|
|
||||||
return `http://localhost:${process.env.PORT ?? 3000}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const trpcClient = createTRPCNext<AppRouter>({
|
export const trpcClient = createTRPCNext<AppRouter>({
|
||||||
config({ ctx }) {
|
config() {
|
||||||
return {
|
return {
|
||||||
links: [
|
links: [createHttpBatchLink(appBridgeInstance)],
|
||||||
httpBatchLink({
|
|
||||||
url: `${getBaseUrl()}/api/trpc`,
|
|
||||||
headers() {
|
|
||||||
const { token, saleorApiUrl } = appBridgeInstance?.getState() || {};
|
|
||||||
|
|
||||||
if (!token || !saleorApiUrl) {
|
|
||||||
console.error(
|
|
||||||
"Can't initialize tRPC client before establishing the App Bridge connection",
|
|
||||||
);
|
|
||||||
throw new Error("Token and Saleor API URL unknown");
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Attach headers from app to client requests, so tRPC can add them to context
|
|
||||||
*/
|
|
||||||
[SALEOR_AUTHORIZATION_BEARER_HEADER]: appBridgeInstance?.getState().token,
|
|
||||||
[SALEOR_API_URL_HEADER]: appBridgeInstance?.getState().saleorApiUrl,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
queryClientConfig: { defaultOptions: { queries: { refetchOnWindowFocus: false } } },
|
queryClientConfig: { defaultOptions: { queries: { refetchOnWindowFocus: false } } },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { initTRPC } from "@trpc/server";
|
import { initTRPC } from "@trpc/server";
|
||||||
import { TrpcContext } from "./trpc-context";
|
import { TrpcContext } from "@saleor/trpc";
|
||||||
import { Permission } from "@saleor/app-sdk/types";
|
import { Permission } from "@saleor/app-sdk/types";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { Box, PropsWithBox, Text } from "@saleor/macaw-ui/next";
|
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
// todo move to shared
|
|
||||||
export const AppSection = ({
|
|
||||||
heading,
|
|
||||||
sideContent,
|
|
||||||
mainContent,
|
|
||||||
includePadding = true,
|
|
||||||
...props
|
|
||||||
}: PropsWithBox<{
|
|
||||||
heading: string;
|
|
||||||
sideContent?: ReactNode;
|
|
||||||
mainContent: ReactNode;
|
|
||||||
includePadding?: boolean;
|
|
||||||
}>) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
as="section"
|
|
||||||
__gridTemplateColumns={"400px auto"}
|
|
||||||
display={"grid"}
|
|
||||||
gap={10}
|
|
||||||
__maxWidth={"1200px"}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Text as="h2" variant={"heading"} size={"large"} marginBottom={1.5}>
|
|
||||||
{heading}
|
|
||||||
</Text>
|
|
||||||
{sideContent}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
borderStyle={"solid"}
|
|
||||||
borderColor={"neutralPlain"}
|
|
||||||
borderWidth={1}
|
|
||||||
padding={includePadding ? 5 : 0}
|
|
||||||
borderRadius={4}
|
|
||||||
>
|
|
||||||
{mainContent}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { BoxProps, Box } from "@saleor/macaw-ui/next";
|
|
||||||
|
|
||||||
export const ButtonsBox = (props: BoxProps) => {
|
|
||||||
return <Box display={"flex"} justifyContent="flex-end" gap={4} {...props} />;
|
|
||||||
};
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { Box, BoxProps } from "@saleor/macaw-ui/next";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: Make it more generic, move to shared or contribute to macaw
|
|
||||||
* todo use macaw skeleton
|
|
||||||
*/
|
|
||||||
const Section = (props: BoxProps) => {
|
|
||||||
return (
|
|
||||||
<Box display="grid" gap={2} {...props}>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="50%"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="70%"
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
__height="10px"
|
|
||||||
backgroundColor="surfaceNeutralHighlight"
|
|
||||||
borderRadius={2}
|
|
||||||
__width="60%"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Skeleton = { Section };
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { GraphQLProvider } from "@/modules/graphql/GraphQLProvider";
|
import "@saleor/macaw-ui/next/style";
|
||||||
import { ThemeSynchronizer } from "@/modules/theme/theme-synchronizer";
|
|
||||||
import { trpcClient } from "@/modules/trpc/trpc-client";
|
import { trpcClient } from "@/modules/trpc/trpc-client";
|
||||||
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
||||||
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
||||||
import { NoSSRWrapper } from "@saleor/apps-shared";
|
import { GraphQLProvider, NoSSRWrapper, ThemeSynchronizer } from "@saleor/apps-shared";
|
||||||
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
||||||
import "@saleor/macaw-ui/next/style";
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as trpcNext from "@trpc/server/adapters/next";
|
import * as trpcNext from "@trpc/server/adapters/next";
|
||||||
import { createTrpcContext } from "../../../modules/trpc/trpc-context";
|
|
||||||
import { appRouter } from "../../../modules/trpc/trpc-app-router";
|
import { appRouter } from "../../../modules/trpc/trpc-app-router";
|
||||||
|
import { createTrpcContext } from "@saleor/trpc";
|
||||||
|
|
||||||
export default trpcNext.createNextApiHandler({
|
export default trpcNext.createNextApiHandler({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { SegmentConfigForm } from "@/modules/configuration/segment-config-form/segment-config-form";
|
import { SegmentConfigForm } from "@/modules/configuration/segment-config-form/segment-config-form";
|
||||||
import { AppHeader } from "@/modules/ui/app-header";
|
import { AppHeader } from "@/modules/ui/app-header";
|
||||||
import { AppSection } from "@/modules/ui/app-section";
|
|
||||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
|
import { Layout } from "@saleor/apps-ui";
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
|
|
||||||
|
@ -19,12 +19,13 @@ const ConfigurationPage: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
<AppSection
|
<Layout.AppSection
|
||||||
marginBottom={14}
|
marginBottom={14}
|
||||||
heading="Segment.io configuration"
|
heading="Segment.io configuration"
|
||||||
sideContent={<Text>Provide Segment credentials to allow sending events.</Text>}
|
sideContent={<Text>Provide Segment credentials to allow sending events.</Text>}
|
||||||
mainContent={<SegmentConfigForm />}
|
>
|
||||||
/>
|
<SegmentConfigForm />
|
||||||
|
</Layout.AppSection>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export * from "./src/is-in-iframe";
|
export * from "./src/is-in-iframe";
|
||||||
export * from "./src/macaw-theme-provider/macaw-theme-provider";
|
|
||||||
export * from "./src/no-ssr-wrapper";
|
export * from "./src/no-ssr-wrapper";
|
||||||
export * from "./src/use-dashboard-notification";
|
export * from "./src/use-dashboard-notification";
|
||||||
export * from "./src/logger";
|
export * from "./src/logger";
|
||||||
|
@ -7,3 +6,6 @@ export * from "./src/saleor-version-compatibility-validator";
|
||||||
export * from "./src/create-graphql-client";
|
export * from "./src/create-graphql-client";
|
||||||
export * from "./src/metadata-manager";
|
export * from "./src/metadata-manager";
|
||||||
export * from "./src/editor-js/editor-js-plaintext-renderer";
|
export * from "./src/editor-js/editor-js-plaintext-renderer";
|
||||||
|
export * from "./src/theme-synchronizer";
|
||||||
|
export * from "./src/GraphQLProvider";
|
||||||
|
export * from "./src/get-app-base-url";
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.1.6",
|
||||||
"urql": "^4.0.4",
|
"urql": "^4.0.4",
|
||||||
"vite": "4.4.8",
|
"vite": "4.4.8",
|
||||||
"vitest": "0.34.1"
|
"vitest": "0.34.1",
|
||||||
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
"pino-pretty": "^10.0.0",
|
"pino-pretty": "^10.0.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"urql": "^4.0.4"
|
"urql": "^4.0.4",
|
||||||
|
"zod": "3.21.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
import { Provider } from "urql";
|
import { Provider } from "urql";
|
||||||
|
import { createGraphQLClient } from "./create-graphql-client";
|
||||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
|
||||||
|
|
||||||
export function GraphQLProvider(props: PropsWithChildren<{}>) {
|
export function GraphQLProvider(props: PropsWithChildren<{}>) {
|
||||||
const { appBridgeState } = useAppBridge();
|
const { appBridgeState } = useAppBridge();
|
|
@ -1,5 +1,9 @@
|
||||||
// todo move to app sdk
|
/**
|
||||||
export const getBaseUrl = (headers: { [name: string]: string | string[] | undefined }): string => {
|
* TODO Consume this from App SDK
|
||||||
|
*/
|
||||||
|
export const getAppBaseUrl = (headers: {
|
||||||
|
[name: string]: string | string[] | undefined;
|
||||||
|
}): string => {
|
||||||
const { host, "x-forwarded-proto": xForwardedProto = "http" } = headers;
|
const { host, "x-forwarded-proto": xForwardedProto = "http" } = headers;
|
||||||
|
|
||||||
const xForwardedProtos = Array.isArray(xForwardedProto)
|
const xForwardedProtos = Array.isArray(xForwardedProto)
|
|
@ -1,47 +0,0 @@
|
||||||
import {
|
|
||||||
dark,
|
|
||||||
light,
|
|
||||||
SaleorThemeColors,
|
|
||||||
ThemeProvider as MacawUIThemeProvider,
|
|
||||||
} from "@saleor/macaw-ui";
|
|
||||||
import { PropsWithChildren } from "react";
|
|
||||||
import { Theme } from "@material-ui/core/styles";
|
|
||||||
|
|
||||||
type PalettesOverride = Record<"light" | "dark", SaleorThemeColors>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary override of colors, to match new dashboard palette.
|
|
||||||
* Long term this will be replaced with Macaw UI 2.x with up to date design tokens
|
|
||||||
*/
|
|
||||||
const palettes: PalettesOverride = {
|
|
||||||
light: {
|
|
||||||
...light,
|
|
||||||
background: {
|
|
||||||
default: "#fff",
|
|
||||||
paper: "#fff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
...dark,
|
|
||||||
background: {
|
|
||||||
default: "hsla(211, 42%, 14%, 1)",
|
|
||||||
paper: "hsla(211, 42%, 14%, 1)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* That's a hack required by Macaw-UI incompatibility with React@18
|
|
||||||
*/
|
|
||||||
const ThemeProvider = MacawUIThemeProvider as React.FC<
|
|
||||||
PropsWithChildren<{ overrides?: Partial<Theme>; ssr: boolean; palettes: PalettesOverride }>
|
|
||||||
>;
|
|
||||||
|
|
||||||
type OuterProps = PropsWithChildren<{ themeOverrides?: Partial<Theme> }>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is theme provider for old Macaw. Will be removed with Macaw/next
|
|
||||||
*/
|
|
||||||
export const MacawThemeProvider = (props: OuterProps) => {
|
|
||||||
return <ThemeProvider {...props} ssr palettes={palettes} />;
|
|
||||||
};
|
|
4
packages/trpc/.eslintrc
Normal file
4
packages/trpc/.eslintrc
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"extends": ["saleor"]
|
||||||
|
}
|
2
packages/trpc/index.ts
Normal file
2
packages/trpc/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./src/context";
|
||||||
|
export * from "./src/http-batch-link";
|
29
packages/trpc/package.json
Normal file
29
packages/trpc/package.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "@saleor/trpc",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"lint:fix": "eslint --fix ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@saleor/app-sdk": "0.41.1",
|
||||||
|
"eslint": "8.46.0",
|
||||||
|
"eslint-config-saleor": "workspace:*",
|
||||||
|
"next": "13.4.8",
|
||||||
|
"typescript": "5.1.6"
|
||||||
|
},
|
||||||
|
"main": "index.ts",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@saleor/app-sdk": "0.41.1",
|
||||||
|
"@saleor/apps-shared": "workspace:*",
|
||||||
|
"@tanstack/react-query": "^4.29.19",
|
||||||
|
"@trpc/client": "10.34.0",
|
||||||
|
"@trpc/next": "10.34.0",
|
||||||
|
"@trpc/react-query": "10.34.0",
|
||||||
|
"@trpc/server": "10.34.0",
|
||||||
|
"next": "13.3.0",
|
||||||
|
"pino": "^8.14.1",
|
||||||
|
"pino-pretty": "^10.0.0",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,10 @@
|
||||||
import * as trpcNext from "@trpc/server/adapters/next";
|
import * as trpcNext from "@trpc/server/adapters/next";
|
||||||
import { SALEOR_AUTHORIZATION_BEARER_HEADER, SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
|
import { SALEOR_AUTHORIZATION_BEARER_HEADER, SALEOR_API_URL_HEADER } from "@saleor/app-sdk/const";
|
||||||
import { inferAsyncReturnType } from "@trpc/server";
|
import { inferAsyncReturnType } from "@trpc/server";
|
||||||
|
import { getAppBaseUrl } from "@saleor/apps-shared";
|
||||||
const getBaseUrl = (headers: { [name: string]: string | string[] | undefined }): string => {
|
|
||||||
const { host, "x-forwarded-proto": xForwardedProto = "http" } = headers;
|
|
||||||
|
|
||||||
const xForwardedProtos = Array.isArray(xForwardedProto)
|
|
||||||
? xForwardedProto.join(",")
|
|
||||||
: xForwardedProto;
|
|
||||||
const protocols = xForwardedProtos.split(",");
|
|
||||||
// prefer https over other protocols
|
|
||||||
const protocol = protocols.find((el) => el === "https") || protocols[0];
|
|
||||||
|
|
||||||
return `${protocol}://${host}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
||||||
const baseUrl = getBaseUrl(req.headers);
|
const baseUrl = getAppBaseUrl(req.headers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
33
packages/trpc/src/http-batch-link.ts
Normal file
33
packages/trpc/src/http-batch-link.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { AppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
|
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
||||||
|
import { httpBatchLink } from "@trpc/client";
|
||||||
|
|
||||||
|
function getBaseUrl() {
|
||||||
|
if (typeof window !== "undefined") return "";
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
|
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
|
return `http://localhost:${process.env.PORT ?? 3000}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createHttpBatchLink = (appBridgeInstance?: AppBridge) => {
|
||||||
|
return httpBatchLink({
|
||||||
|
url: `${getBaseUrl()}/api/trpc`,
|
||||||
|
headers() {
|
||||||
|
const { token, saleorApiUrl } = appBridgeInstance?.getState() || {};
|
||||||
|
|
||||||
|
if (!token || !saleorApiUrl) {
|
||||||
|
console.error("Can't initialize tRPC client before establishing the App Bridge connection");
|
||||||
|
throw new Error("Token and Saleor API URL unknown");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Attach headers from app to client requests, so tRPC can add them to context
|
||||||
|
*/
|
||||||
|
[SALEOR_AUTHORIZATION_BEARER_HEADER]: appBridgeInstance?.getState().token,
|
||||||
|
[SALEOR_API_URL_HEADER]: appBridgeInstance?.getState().saleorApiUrl,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
9
packages/trpc/turbo.json
Normal file
9
packages/trpc/turbo.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": ["//"],
|
||||||
|
"$schema": "https://turbo.build/schema.json",
|
||||||
|
"pipeline": {
|
||||||
|
"build": {
|
||||||
|
"env": ["VERCEL_URL", "PORT"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
export * from "./src/text-link";
|
export * from "./src/text-link";
|
||||||
export * from "./src/semantic-chip";
|
export * from "./src/semantic-chip";
|
||||||
export * from "./src/breadcrumbs";
|
export * from "./src/breadcrumbs";
|
||||||
|
export * from "./src/layout";
|
||||||
|
export * from "./src/buttons-box";
|
||||||
|
export * from "./src/skeleton-layout";
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { Box } from "@saleor/macaw-ui/next";
|
|
||||||
import { PropsWithChildren } from "react";
|
|
||||||
|
|
||||||
type ExampleProps = PropsWithChildren<{}>;
|
|
||||||
|
|
||||||
export const Example = ({ children }: ExampleProps) => {
|
|
||||||
return <Box>{children}</Box>;
|
|
||||||
};
|
|
78
packages/ui/src/layout.tsx
Normal file
78
packages/ui/src/layout.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { Box, PropsWithBox, Text } from "@saleor/macaw-ui/next";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
type AppSectionProps = PropsWithBox<{
|
||||||
|
heading: ReactNode;
|
||||||
|
sideContent?: ReactNode;
|
||||||
|
includePadding?: boolean;
|
||||||
|
leftColumnWidthPx?: number;
|
||||||
|
maxWidthPx?: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const AppSectionCard = ({
|
||||||
|
children,
|
||||||
|
footer,
|
||||||
|
includePadding = true,
|
||||||
|
...props
|
||||||
|
}: PropsWithBox<{
|
||||||
|
footer?: ReactNode;
|
||||||
|
includePadding?: boolean;
|
||||||
|
}>) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
borderStyle={"solid"}
|
||||||
|
borderColor={"neutralPlain"}
|
||||||
|
borderWidth={1}
|
||||||
|
borderRadius={4}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Box padding={includePadding ? 5 : 0}>{children}</Box>
|
||||||
|
{footer && (
|
||||||
|
<Box
|
||||||
|
borderTopStyle="solid"
|
||||||
|
borderTopWidth={1}
|
||||||
|
borderColor="neutralHighlight"
|
||||||
|
padding={includePadding ? 5 : 0}
|
||||||
|
marginTop={5}
|
||||||
|
>
|
||||||
|
{footer}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppSection = ({
|
||||||
|
heading,
|
||||||
|
sideContent,
|
||||||
|
includePadding = true,
|
||||||
|
leftColumnWidthPx = 400,
|
||||||
|
maxWidthPx = 1200,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: AppSectionProps) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="section"
|
||||||
|
__gridTemplateColumns={`${leftColumnWidthPx}px auto`}
|
||||||
|
display={"grid"}
|
||||||
|
gap={10}
|
||||||
|
__maxWidth={`${maxWidthPx}px`}
|
||||||
|
alignItems="start"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<Text as="h2" variant={"heading"} size={"large"} marginBottom={1.5}>
|
||||||
|
{heading}
|
||||||
|
</Text>
|
||||||
|
{sideContent}
|
||||||
|
</Box>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Layout = {
|
||||||
|
AppSection,
|
||||||
|
AppSectionCard,
|
||||||
|
};
|
17
packages/ui/src/skeleton-layout.tsx
Normal file
17
packages/ui/src/skeleton-layout.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Box, BoxProps, Skeleton as MacawSkeleton, SkeletonProps } from "@saleor/macaw-ui/next";
|
||||||
|
|
||||||
|
const Section = (props: BoxProps) => {
|
||||||
|
return (
|
||||||
|
<Box display="grid" gap={2} {...props}>
|
||||||
|
<MacawSkeleton __height="16px" __width="50%" />
|
||||||
|
<MacawSkeleton __height="16px" __width="70%" />
|
||||||
|
<MacawSkeleton __height="16px" __width="60%" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Line = (props: SkeletonProps) => {
|
||||||
|
return <MacawSkeleton __height="16px" __width="60%" {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SkeletonLayout = { Section, Line };
|
|
@ -1188,6 +1188,9 @@ importers:
|
||||||
'@saleor/react-hook-form-macaw':
|
'@saleor/react-hook-form-macaw':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/react-hook-form-macaw
|
version: link:../../packages/react-hook-form-macaw
|
||||||
|
'@saleor/trpc':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/trpc
|
||||||
'@segment/analytics-node':
|
'@segment/analytics-node':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
|
@ -1760,6 +1763,58 @@ importers:
|
||||||
vitest:
|
vitest:
|
||||||
specifier: 0.34.1
|
specifier: 0.34.1
|
||||||
version: 0.34.1(jsdom@20.0.3)
|
version: 0.34.1(jsdom@20.0.3)
|
||||||
|
zod:
|
||||||
|
specifier: 3.21.4
|
||||||
|
version: 3.21.4
|
||||||
|
|
||||||
|
packages/trpc:
|
||||||
|
dependencies:
|
||||||
|
'@saleor/apps-shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared
|
||||||
|
'@tanstack/react-query':
|
||||||
|
specifier: ^4.29.19
|
||||||
|
version: 4.29.19(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@trpc/client':
|
||||||
|
specifier: 10.34.0
|
||||||
|
version: 10.34.0(@trpc/server@10.34.0)
|
||||||
|
'@trpc/next':
|
||||||
|
specifier: 10.34.0
|
||||||
|
version: 10.34.0(@tanstack/react-query@4.29.19)(@trpc/client@10.34.0)(@trpc/react-query@10.34.0)(@trpc/server@10.34.0)(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@trpc/react-query':
|
||||||
|
specifier: 10.34.0
|
||||||
|
version: 10.34.0(@tanstack/react-query@4.29.19)(@trpc/client@10.34.0)(@trpc/server@10.34.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@trpc/server':
|
||||||
|
specifier: 10.34.0
|
||||||
|
version: 10.34.0
|
||||||
|
pino:
|
||||||
|
specifier: ^8.14.1
|
||||||
|
version: 8.14.1
|
||||||
|
pino-pretty:
|
||||||
|
specifier: ^10.0.0
|
||||||
|
version: 10.0.0
|
||||||
|
react:
|
||||||
|
specifier: 18.2.0
|
||||||
|
version: 18.2.0
|
||||||
|
react-dom:
|
||||||
|
specifier: 18.2.0
|
||||||
|
version: 18.2.0(react@18.2.0)
|
||||||
|
devDependencies:
|
||||||
|
'@saleor/app-sdk':
|
||||||
|
specifier: 0.41.1
|
||||||
|
version: 0.41.1(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
eslint:
|
||||||
|
specifier: 8.46.0
|
||||||
|
version: 8.46.0
|
||||||
|
eslint-config-saleor:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../eslint-config-saleor
|
||||||
|
next:
|
||||||
|
specifier: 13.4.8
|
||||||
|
version: 13.4.8(@babel/core@7.22.11)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
typescript:
|
||||||
|
specifier: 5.1.6
|
||||||
|
version: 5.1.6
|
||||||
|
|
||||||
packages/ui:
|
packages/ui:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2939,7 +2994,7 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.2.1
|
'@ampproject/remapping': 2.2.1
|
||||||
'@babel/code-frame': 7.22.5
|
'@babel/code-frame': 7.22.10
|
||||||
'@babel/generator': 7.22.9
|
'@babel/generator': 7.22.9
|
||||||
'@babel/helper-compilation-targets': 7.22.9(@babel/core@7.21.8)
|
'@babel/helper-compilation-targets': 7.22.9(@babel/core@7.21.8)
|
||||||
'@babel/helper-module-transforms': 7.22.9(@babel/core@7.21.8)
|
'@babel/helper-module-transforms': 7.22.9(@babel/core@7.21.8)
|
||||||
|
@ -5704,7 +5759,7 @@ packages:
|
||||||
resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==}
|
resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.22.5
|
'@babel/code-frame': 7.22.10
|
||||||
'@babel/generator': 7.22.9
|
'@babel/generator': 7.22.9
|
||||||
'@babel/helper-environment-visitor': 7.22.5
|
'@babel/helper-environment-visitor': 7.22.5
|
||||||
'@babel/helper-function-name': 7.22.5
|
'@babel/helper-function-name': 7.22.5
|
||||||
|
@ -5757,7 +5812,7 @@ packages:
|
||||||
resolution: {integrity: sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==}
|
resolution: {integrity: sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.22.5
|
'@babel/code-frame': 7.22.10
|
||||||
'@babel/generator': 7.22.9
|
'@babel/generator': 7.22.9
|
||||||
'@babel/helper-environment-visitor': 7.22.5
|
'@babel/helper-environment-visitor': 7.22.5
|
||||||
'@babel/helper-function-name': 7.22.5
|
'@babel/helper-function-name': 7.22.5
|
||||||
|
@ -10358,7 +10413,7 @@ packages:
|
||||||
/@swc/helpers@0.5.1:
|
/@swc/helpers@0.5.1:
|
||||||
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
|
resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.6.1
|
tslib: 2.6.2
|
||||||
|
|
||||||
/@tanstack/query-core@4.29.19:
|
/@tanstack/query-core@4.29.19:
|
||||||
resolution: {integrity: sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw==}
|
resolution: {integrity: sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw==}
|
||||||
|
@ -12260,7 +12315,7 @@ packages:
|
||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001503
|
caniuse-lite: 1.0.30001519
|
||||||
electron-to-chromium: 1.4.482
|
electron-to-chromium: 1.4.482
|
||||||
node-releases: 2.0.13
|
node-releases: 2.0.13
|
||||||
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
||||||
|
|
Loading…
Reference in a new issue