Prepare app for production (#145)

Prepare data importer for production

---------

Co-authored-by: Lukasz Ostrowski <lukasz.ostrowski@saleor.io>

Fix prod resolving

Add icon

Improve styles
This commit is contained in:
Lukasz Ostrowski 2023-02-23 08:22:37 +01:00 committed by GitHub
parent f628461028
commit 1da5be3be1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 436 additions and 77 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-app-data-importer": minor
---
Add dark mode styling

View file

@ -0,0 +1,5 @@
---
"saleor-app-data-importer": patch
---
Add valid icon and color

View file

@ -0,0 +1,9 @@
---
"saleor-app-data-importer": minor
---
Enable origins check for installation via env variables
Update app-sdk
Set Nuvo development mode based on env variables

View file

@ -1,2 +1,3 @@
# https://getnuvo.com/ # https://getnuvo.com/
NEXT_PUBLIC_NUVO_LICENSE_KEY= NEXT_PUBLIC_NUVO_LICENSE_KEY=
NEXT_PUBLIC_NUVO_PROD_MODE=false

View file

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.3333 4.5C26.53 4.5 27.5 5.47005 27.5 6.66667V10.6667C27.5 11.1269 27.1269 11.5 26.6667 11.5C26.2064 11.5 25.8333 11.1269 25.8333 10.6667V6.66667C25.8333 6.39052 25.6095 6.16667 25.3333 6.16667H6.66667C6.39053 6.16667 6.16667 6.39052 6.16667 6.66667V21.3333C6.16667 21.6095 6.39053 21.8333 6.66667 21.8333H8.66667C9.1269 21.8333 9.5 22.2064 9.5 22.6667C9.5 23.1269 9.1269 23.5 8.66667 23.5H6.66667C5.47005 23.5 4.5 22.53 4.5 21.3333V6.66667C4.5 5.47005 5.47005 4.5 6.66667 4.5H25.3333Z" fill="white"/>
<path d="M9.33333 10.6667C10.0697 10.6667 10.6667 10.0697 10.6667 9.33333C10.6667 8.59695 10.0697 8 9.33333 8C8.59695 8 8 8.59695 8 9.33333C8 10.0697 8.59695 10.6667 9.33333 10.6667Z" fill="white"/>
<path d="M14.6667 9.33333C14.6667 10.0697 14.0697 10.6667 13.3333 10.6667C12.597 10.6667 12 10.0697 12 9.33333C12 8.59695 12.597 8 13.3333 8C14.0697 8 14.6667 8.59695 14.6667 9.33333Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.3333 13.8333C12.1367 13.8333 11.1667 14.8034 11.1667 16V25.3333C11.1667 26.53 12.1367 27.5 13.3333 27.5H26.6667C27.8633 27.5 28.8333 26.53 28.8333 25.3333V16C28.8333 14.8034 27.8633 13.8333 26.6667 13.8333H13.3333ZM12.8333 25.3333V19.5H16.5V25.8333H13.3333C13.0572 25.8333 12.8333 25.6095 12.8333 25.3333ZM12.8333 16V17.8333H16.5V15.5H13.3333C13.0572 15.5 12.8333 15.7239 12.8333 16ZM26.6667 25.8333H18.1667V19.5H27.1667V25.3333C27.1667 25.6095 26.9428 25.8333 26.6667 25.8333ZM18.1667 17.8333H27.1667V16C27.1667 15.7239 26.9428 15.5 26.6667 15.5H18.1667V17.8333Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -11,9 +11,6 @@ import { APL, FileAPL, SaleorCloudAPL, UpstashAPL, VercelAPL } from "@saleor/app
export let apl: APL; export let apl: APL;
switch (process.env.APL) { switch (process.env.APL) {
case "vercel":
apl = new VercelAPL();
break;
case "upstash": case "upstash":
// Require `UPSTASH_URL` and `UPSTASH_TOKEN` environment variables // Require `UPSTASH_URL` and `UPSTASH_TOKEN` environment variables
apl = new UpstashAPL(); apl = new UpstashAPL();

View file

@ -3,8 +3,7 @@ import { useEffect, useState } from "react";
import * as jose from "jose"; import * as jose from "jose";
/** /**
* TODO test * use app-sdk
* TODO extract to app-sdk
*/ */
export function useAuthorizedToken(requirePermission: string) { export function useAuthorizedToken(requirePermission: string) {
const [authorized, setAuthorized] = useState<boolean | undefined>(); const [authorized, setAuthorized] = useState<boolean | undefined>();

View file

@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react"; import React, { useCallback, useMemo, useState } from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { ConfigureAPI, OnResults, SettingsAPI } from "nuvo-react"; import { ConfigureAPI, OnResults, SettingsAPI } from "nuvo-react";
import { import {
@ -8,10 +8,11 @@ import {
} from "./customers-columns-model"; } from "./customers-columns-model";
import dotObject from "dot-object"; import dotObject from "dot-object";
import { useAuthorizedToken } from "../../authorization/use-authorized-token"; import { useAuthorizedToken } from "../../authorization/use-authorized-token";
import { Alert, Button } from "@saleor/macaw-ui"; import { Alert, Button, SaleorTheme, useTheme } from "@saleor/macaw-ui";
import { CustomersImportingResults } from "../customers-results/customers-importing-results"; import { CustomersImportingResults } from "../customers-results/customers-importing-results";
import { LinearProgress } from "@material-ui/core"; import { lighten, LinearProgress } from "@material-ui/core";
import { CloudUpload } from "@material-ui/icons"; import { CloudUpload } from "@material-ui/icons";
import { Theme } from "@material-ui/core/styles";
let PassSubmitResult: any; let PassSubmitResult: any;
let RejectSubmitResult: any; let RejectSubmitResult: any;
@ -34,57 +35,190 @@ const NuvoImporter = dynamic<ConfigureAPI>(
const columns = getCustomersModelColumns(); const columns = getCustomersModelColumns();
const nuvoSettings: SettingsAPI = { const getNuvoSettings = (theme: SaleorTheme): SettingsAPI => {
columns, const dropdownStyles = {
developerMode: true, //todo option: {
identifier: "customers", color: theme.palette.text.primary,
modal: false, ":hover": {
style: { background: `${lighten(theme.palette.background.default, 0.1)}`,
buttons: {
primary: {
background: "black",
color: "#fff",
}, },
}, },
loader: {
loadAnimationColor: "#000",
},
header: { header: {
description: { background: theme.palette.background.default,
display: "none", color: theme.palette.text.primary,
}, },
root: {
border: `1px solid ${lighten(theme.palette.background.default, 0.1)}`,
background: theme.palette.background.default,
color: theme.palette.text.primary,
},
search: {
root: { root: {
// display: "none", background: theme.palette.background.default,
color: theme.palette.text.primary,
}, },
}, },
progressBar: { button: {
root: { root: {
display: "none", background: theme.palette.background.default,
color: theme.palette.text.primary,
maxHeight: "20px",
border: `1px solid ${lighten(theme.palette.background.default, 0.1)}`,
}, },
}, },
dropzone: { } as const;
icon: {
box: { return {
filter: "grayscale(1)", columns,
enableExamples: false,
developerMode: process.env.NEXT_PUBLIC_NUVO_PROD_MODE !== "true",
identifier: "customers",
modal: false,
style: {
footer: {
root: {
background: theme.palette.background.default,
}, },
}, },
root: { buttons: {
background: "#fff", primary: {
border: "1px dashed #ddd", background: "black",
color: "#fff",
":hover": {
backgroundColor: "black",
color: "#fff",
},
},
secondary: {
background: "#444",
color: "#fff",
border: "none",
":hover": {
background: "#444",
color: "#fff",
},
},
},
loader: {
loadAnimationColor: theme.palette.type === "light" ? "#000" : "#fff",
},
headerSelect: {
root: {
background: theme.palette.background.default,
border: "none",
},
table: {
selectRowColor: lighten(theme.palette.background.default, 0.3),
th: {
color: theme.palette.text.primary,
background: theme.palette.background.default,
},
td: {
color: theme.palette.text.primary,
background: theme.palette.background.default,
},
hoverRowColor: lighten(theme.palette.background.default, 0.1),
},
sheetName: {
root: {
display: "none",
},
},
},
columnMatch: {
notMatchingValue: {
root: {
background: lighten(theme.palette.background.default, 0.1),
},
},
buttonJoined: {
root: {
background: lighten(theme.palette.background.default, 0.1),
},
},
root: {
background: theme.palette.background.default,
border: `1px solid ${lighten(theme.palette.background.default, 0.1)}`,
},
columnMatchHeader: {
dropdown: dropdownStyles,
root: {
background: theme.palette.background.default,
border: `1px solid ${lighten(theme.palette.background.default, 0.1)}`,
},
},
columnMatchValue: {
emptyValue: {
background: theme.palette.background.default,
color: theme.palette.text.primary,
},
dropdown: dropdownStyles,
root: {
border: `1px solid ${lighten(theme.palette.background.default, 0.1)}`,
background: theme.palette.background.default,
},
},
},
header: {
description: {
display: "none",
},
root: {
// display: "none",
},
},
progressBar: {
root: {
display: "none",
},
},
dropzone: {
icon: {
box: {
filter: "grayscale(1)",
},
},
root: {
background: theme.palette.background.default,
border: "1px dashed #ddd",
},
},
reviewEntries: {
root: {
backgroundColor: "transparent",
},
table: {
th: {
backgroundColor: "transparent",
},
td: {
normal: {
backgroundColor: "transparent",
},
root: {
backgroundColor: "transparent",
},
},
},
},
globals: {
fontFamily: "Inter",
backgroundColor: "transparent",
textColor: "inherit",
}, },
}, },
globals: { fontFamily: "Inter", backgroundColor: "transparent" }, title: "Upload customers to Saleor",
}, disableExcelTemplate: true,
title: "Upload customers to Saleor", disableTemplates: true,
disableExcelTemplate: true, allowManualInput: true,
disableTemplates: true, };
allowManualInput: true,
}; };
const licenseKey = process.env.NEXT_PUBLIC_NUVO_LICENSE_KEY as string; const licenseKey = process.env.NEXT_PUBLIC_NUVO_LICENSE_KEY as string;
export const CustomersImporterView = () => { export const CustomersImporterView = () => {
const authorized = useAuthorizedToken("MANAGE_USERS"); const authorized = useAuthorizedToken("MANAGE_USERS");
const saleorTheme = useTheme();
const [importedLines, setImportedLines] = useState<CustomerColumnSchema[] | null>(null); const [importedLines, setImportedLines] = useState<CustomerColumnSchema[] | null>(null);
@ -96,6 +230,10 @@ export const CustomersImporterView = () => {
setImportedLines(parsedResult); setImportedLines(parsedResult);
}, []); }, []);
const nuvoSettings = useMemo(() => {
return getNuvoSettings(saleorTheme);
}, [saleorTheme]);
if (authorized === undefined) { if (authorized === undefined) {
return <div>Authorizing</div>; return <div>Authorizing</div>;
} }

View file

@ -12,9 +12,9 @@ export const CustomersImportingResults = ({
const [importingStarted, setImportingStarted] = useState(false); const [importingStarted, setImportingStarted] = useState(false);
return ( return (
<div> <div style={{ marginTop: 20 }}>
<Typography paragraph variant="h3"> <Typography paragraph variant="h3">
Customers rows from imported file Customers rows from the imported file
</Typography> </Typography>
<Typography paragraph> <Typography paragraph>

View file

@ -1,12 +1,42 @@
import "@saleor/apps-shared/src/globals.css"; import "@saleor/apps-shared/src/globals.css";
import "../styles/globals.css";
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 React, { useEffect } from "react"; import {
dark,
light,
SaleorThemeColors,
ThemeProvider as MacawUIThemeProvider,
} from "@saleor/macaw-ui";
import React, { PropsWithChildren, useEffect } from "react";
import { AppProps } from "next/app"; import { AppProps } from "next/app";
import { ThemeSynchronizer } from "../lib/theme-synchronizer"; import { ThemeSynchronizer } from "../lib/theme-synchronizer";
import { NoSSRWrapper } from "../no-ssr-wrapper"; import { NoSSRWrapper } from "../no-ssr-wrapper";
import { MacawThemeProvider } from "@saleor/apps-shared"; 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)",
},
},
};
/** /**
* Ensure instance is a singleton. * Ensure instance is a singleton.
@ -14,6 +44,13 @@ import { MacawThemeProvider } from "@saleor/apps-shared";
*/ */
const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined; const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined;
/**
* 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 }>
>;
function NextApp({ Component, pageProps }: AppProps) { function NextApp({ Component, pageProps }: AppProps) {
/** /**
* Configure JSS (used by MacawUI) for SSR. If Macaw is not used, can be removed. * Configure JSS (used by MacawUI) for SSR. If Macaw is not used, can be removed.
@ -28,11 +65,11 @@ function NextApp({ Component, pageProps }: AppProps) {
return ( return (
<NoSSRWrapper> <NoSSRWrapper>
<AppBridgeProvider appBridgeInstance={appBridgeInstance}> <AppBridgeProvider appBridgeInstance={appBridgeInstance}>
<MacawThemeProvider> <ThemeProvider ssr palettes={palettes}>
<ThemeSynchronizer /> <ThemeSynchronizer />
<RoutePropagator /> <RoutePropagator />
<Component {...pageProps} /> <Component {...pageProps} />
</MacawThemeProvider> </ThemeProvider>
</AppBridgeProvider> </AppBridgeProvider>
</NoSSRWrapper> </NoSSRWrapper>
); );

View file

@ -2,8 +2,27 @@ import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next";
import { saleorApp } from "../../../saleor-app"; import { saleorApp } from "../../../saleor-app";
const allowedUrlsPattern = process.env.ALLOWED_DOMAIN_PATTERN;
/** /**
* Required endpoint, called by Saleor to install app. * Required endpoint, called by Saleor to install app.
* It will exchange tokens with app, so saleorApp.apl will contain token * It will exchange tokens with app, so saleorApp.apl will contain token
*/ */
export default createAppRegisterHandler(saleorApp); export default createAppRegisterHandler({
apl: saleorApp.apl,
/**
* Prohibit installation from Saleors other than specified by the regex.
* Regex source is ENV so if ENV is not set, all installations will be allowed.
*/
allowedSaleorUrls: [
(url) => {
if (allowedUrlsPattern) {
const regex = new RegExp(allowedUrlsPattern);
return regex.test(url);
}
return true;
},
],
});

View file

@ -1,6 +1,6 @@
import { NextPage } from "next"; import { NextPage } from "next";
import React, { ComponentProps } from "react"; import React, { ComponentProps } from "react";
import { Container, Divider, Typography } from "@material-ui/core"; import { Container, Divider } from "@material-ui/core";
import { Button, makeStyles, PageTab, PageTabs, SaleorTheme } from "@saleor/macaw-ui"; import { Button, makeStyles, PageTab, PageTabs, SaleorTheme } from "@saleor/macaw-ui";
import { CustomersImporterView } from "../modules/customers/customers-importer-nuvo/customers-importer-view"; import { CustomersImporterView } from "../modules/customers/customers-importer-nuvo/customers-importer-view";
import GraphQLProvider from "../providers/GraphQLProvider"; import GraphQLProvider from "../providers/GraphQLProvider";
@ -11,7 +11,7 @@ type Tab = "customers";
const useStyles = makeStyles((theme: SaleorTheme) => ({ const useStyles = makeStyles((theme: SaleorTheme) => ({
wrapper: { wrapper: {
minHeight: '100vh', minHeight: `100vh`,
}, },
})); }));
@ -34,7 +34,7 @@ const ImporterPage: NextPage = () => {
<div className={styles.wrapper}> <div className={styles.wrapper}>
<TitleBar <TitleBar
bottomMargin bottomMargin
icon={<AppIcon theme="rgb(58, 86, 199)" text="DI" />} icon={<AppIcon theme="#3BD579" icon={<img src="/logo.svg" />} />}
name="Data Importer" name="Data Importer"
author="By Saleor Commerce" author="By Saleor Commerce"
rightColumnContent={ rightColumnContent={
@ -58,7 +58,7 @@ const ImporterPage: NextPage = () => {
</div> </div>
} }
/> />
<Container> <Container style={{ maxWidth: "unset" }}>
<PageTabs <PageTabs
style={{ marginBottom: 20, marginTop: 20 }} style={{ marginBottom: 20, marginTop: 20 }}
value={activeTab} value={activeTab}
@ -68,7 +68,7 @@ const ImporterPage: NextPage = () => {
<PageTab disabled value="orders" label="Orders (coming soon)" /> <PageTab disabled value="orders" label="Orders (coming soon)" />
<PageTab disabled value="products" label="Products (coming soon)" /> <PageTab disabled value="products" label="Products (coming soon)" />
</PageTabs> </PageTabs>
<Divider style={{ marginBottom: 50 }} /> <Divider />
{activeTab === "customers" && <CustomersImporterView />} {activeTab === "customers" && <CustomersImporterView />}
</Container> </Container>
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -1,4 +0,0 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -2,7 +2,6 @@ body {
font-family: Inter, -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, font-family: Inter, -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
color: #111; color: #111;
padding: 1rem 2rem;
} }
code { code {

View file

@ -5,36 +5,45 @@
## Local development ## Local development
### Start Monitoring backend ### Start Monitoring backend
Run: Run:
```shell
```shell
docker-compose up docker-compose up
``` ```
It is beneficial to run this command in a separate terminal tab to observe backend logs easily. It is beneficial to run this command in a separate terminal tab to observe backend logs easily.
By default, backend will run at `localhost:5001` with: By default, backend will run at `localhost:5001` with:
- Manifest at `/manifest`
- Graphql Playground at `/graphql` - Manifest at `/manifest`
- OpenApi viewer at `/docs` - Graphql Playground at `/graphql`
- OpenApi viewer at `/docs`
### Develop frontend: ### Develop frontend:
Installing dependencies with: Installing dependencies with:
```shell ```shell
pnpm i pnpm i
``` ```
Running dev server Running dev server
```shell ```shell
pnpm dev pnpm dev
``` ```
The frontend app will run at `localhost:3000`. The frontend app will run at `localhost:3000`.
By default, it acts as a proxy and redirects all unhandled requests to the backend (configured by `MONITORING_APP_API_URL` env). By default, it acts as a proxy and redirects all unhandled requests to the backend (configured by `MONITORING_APP_API_URL` env).
This way, all frontend and backend endpoints are accessible at `http://localhost:3000` This way, all frontend and backend endpoints are accessible at `http://localhost:3000`
### Test with Saleor ### Test with Saleor
Expose `http://localhost:3000` using a tunnel and use `https://your.tunnel/manifest` manifest URL to install `Monitoring` app Expose `http://localhost:3000` using a tunnel and use `https://your.tunnel/manifest` manifest URL to install `Monitoring` app
### Graphql Playground ### Graphql Playground
To use Graphql Playground, `Monitoring` app needs to be installed in Saleor, and HTTP headers must be set: To use Graphql Playground, `Monitoring` app needs to be installed in Saleor, and HTTP headers must be set:
```json ```json
@ -45,9 +54,11 @@ To use Graphql Playground, `Monitoring` app needs to be installed in Saleor, and
``` ```
### Testing DataDog integration ### Testing DataDog integration
Use these credentials sets to test DataDog integration: Use these credentials sets to test DataDog integration:
Working credentials: Working credentials:
```json ```json
{ {
"site": "US1", "site": "US1",
@ -56,6 +67,7 @@ Working credentials:
``` ```
Credentials that validate but generate an error while sending events Credentials that validate but generate an error while sending events
```json ```json
{ {
"site": "EU1", "site": "EU1",

View file

@ -3,4 +3,4 @@
* *
* https://vitest.dev/config/#setupfiles * https://vitest.dev/config/#setupfiles
*/ */
export {} export {};

View file

@ -3,7 +3,12 @@ import "../styles/globals.css";
import { Theme } from "@material-ui/core/styles"; import { Theme } from "@material-ui/core/styles";
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 { dark, light, SaleorThemeColors, ThemeProvider as MacawUIThemeProvider } from "@saleor/macaw-ui"; import {
dark,
light,
SaleorThemeColors,
ThemeProvider as MacawUIThemeProvider,
} from "@saleor/macaw-ui";
import React, { PropsWithChildren, useEffect } from "react"; import React, { PropsWithChildren, useEffect } from "react";
import { AppLayoutProps } from "../../types"; import { AppLayoutProps } from "../../types";
@ -63,7 +68,6 @@ const ThemeProvider = MacawUIThemeProvider as React.FC<
PropsWithChildren<{ overrides?: Partial<Theme>; ssr: boolean; palettes: PalettesOverride }> PropsWithChildren<{ overrides?: Partial<Theme>; ssr: boolean; palettes: PalettesOverride }>
>; >;
function SaleorApp({ Component, pageProps }: AppLayoutProps) { function SaleorApp({ Component, pageProps }: AppLayoutProps) {
const getLayout = Component.getLayout ?? ((page) => page); const getLayout = Component.getLayout ?? ((page) => page);

View file

@ -23,7 +23,8 @@
"prettier": "^2.8.3", "prettier": "^2.8.3",
"turbo": "^1.7.4", "turbo": "^1.7.4",
"eslint": "^8.33.0", "eslint": "^8.33.0",
"husky": "^8.0.3" "husky": "^8.0.3",
"lint-staged": "^13.1.2"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"

View file

@ -5,7 +5,6 @@ import clsx from "clsx";
const useStyles = makeStyles({ const useStyles = makeStyles({
appIconContainer: { appIconContainer: {
background: "rgb(58, 86, 199)",
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
justifyContent: "center", justifyContent: "center",
@ -23,11 +22,17 @@ type Props = HTMLProps<HTMLDivElement> & {
icon?: ReactNode; icon?: ReactNode;
}; };
export function AppIcon({ className, children, text, icon, ...props }: Props) { export function AppIcon({ className, children, text, icon, theme, ...props }: Props) {
const styles = useStyles(); const styles = useStyles();
return ( return (
<div className={clsx(styles.appIconContainer, className)} {...props}> <div
className={clsx(styles.appIconContainer, className)}
style={{
background: theme,
}}
{...props}
>
{text && <Typography variant="h2">{text}</Typography>} {text && <Typography variant="h2">{text}</Typography>}
{icon && icon} {icon && icon}
</div> </div>

View file

@ -22,12 +22,14 @@ export function TitleBar({
bottomMargin, bottomMargin,
}: Props) { }: Props) {
return ( return (
<div <div className={styles.container}>
className={clsx(styles.container, className, { <Paper
[styles.bottomMargin]: bottomMargin, square
})} elevation={0}
> className={clsx(styles.root, className, {
<Paper variant="outlined" square elevation={0} className={styles.root}> [styles.bottomMargin]: bottomMargin,
})}
>
{icon && <div className={styles.iconColumn}>{icon}</div>} {icon && <div className={styles.iconColumn}>{icon}</div>}
<div className={styles.leftColumn}> <div className={styles.leftColumn}>
<h1 className={styles.appName}>{name}</h1> <h1 className={styles.appName}>{name}</h1>

View file

@ -8,6 +8,7 @@ importers:
eslint: ^8.33.0 eslint: ^8.33.0
eslint-config-saleor: workspace:* eslint-config-saleor: workspace:*
husky: ^8.0.3 husky: ^8.0.3
lint-staged: ^13.1.2
prettier: ^2.8.3 prettier: ^2.8.3
turbo: ^1.7.4 turbo: ^1.7.4
devDependencies: devDependencies:
@ -15,6 +16,7 @@ importers:
eslint: 8.33.0 eslint: 8.33.0
eslint-config-saleor: link:packages/eslint-config-saleor eslint-config-saleor: link:packages/eslint-config-saleor
husky: 8.0.3 husky: 8.0.3
lint-staged: 13.1.2
prettier: 2.8.3 prettier: 2.8.3
turbo: 1.7.4 turbo: 1.7.4
@ -6245,6 +6247,11 @@ packages:
engines: {node: '>= 6'} engines: {node: '>= 6'}
dev: false dev: false
/commander/9.5.0:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14}
dev: true
/common-tags/1.8.2: /common-tags/1.8.2:
resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@ -7816,6 +7823,21 @@ packages:
strip-final-newline: 2.0.0 strip-final-newline: 2.0.0
dev: true dev: true
/execa/6.1.0:
resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
cross-spawn: 7.0.3
get-stream: 6.0.1
human-signals: 3.0.1
is-stream: 3.0.0
merge-stream: 2.0.0
npm-run-path: 5.1.0
onetime: 6.0.0
signal-exit: 3.0.7
strip-final-newline: 3.0.0
dev: true
/exenv/1.2.2: /exenv/1.2.2:
resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==} resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==}
@ -8221,6 +8243,11 @@ packages:
pump: 3.0.0 pump: 3.0.0
dev: true dev: true
/get-stream/6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
dev: true
/get-symbol-description/1.0.0: /get-symbol-description/1.0.0:
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -8635,6 +8662,11 @@ packages:
engines: {node: '>=8.12.0'} engines: {node: '>=8.12.0'}
dev: true dev: true
/human-signals/3.0.1:
resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
engines: {node: '>=12.20.0'}
dev: true
/husky/8.0.3: /husky/8.0.3:
resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -8994,6 +9026,11 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/is-stream/3.0.0:
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: true
/is-string/1.0.7: /is-string/1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -9426,6 +9463,29 @@ packages:
/lines-and-columns/1.2.4: /lines-and-columns/1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
/lint-staged/13.1.2:
resolution: {integrity: sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==}
engines: {node: ^14.13.1 || >=16.0.0}
hasBin: true
dependencies:
cli-truncate: 3.1.0
colorette: 2.0.19
commander: 9.5.0
debug: 4.3.4
execa: 6.1.0
lilconfig: 2.0.6
listr2: 5.0.7
micromatch: 4.0.5
normalize-path: 3.0.0
object-inspect: 1.12.3
pidtree: 0.6.0
string-argv: 0.3.1
yaml: 2.2.1
transitivePeerDependencies:
- enquirer
- supports-color
dev: true
/listenercount/1.0.1: /listenercount/1.0.1:
resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
dev: false dev: false
@ -9499,6 +9559,25 @@ packages:
wrap-ansi: 7.0.0 wrap-ansi: 7.0.0
dev: true dev: true
/listr2/5.0.7:
resolution: {integrity: sha512-MD+qXHPmtivrHIDRwPYdfNkrzqDiuaKU/rfBcec3WMyMF3xylQj3jMq344OtvQxz7zaCFViRAeqlr2AFhPvXHw==}
engines: {node: ^14.13.1 || >=16.0.0}
peerDependencies:
enquirer: '>= 2.3.0 < 3'
peerDependenciesMeta:
enquirer:
optional: true
dependencies:
cli-truncate: 2.1.0
colorette: 2.0.19
log-update: 4.0.0
p-map: 4.0.0
rfdc: 1.3.0
rxjs: 7.8.0
through: 2.3.8
wrap-ansi: 7.0.0
dev: true
/load-yaml-file/0.2.0: /load-yaml-file/0.2.0:
resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -9853,6 +9932,11 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/mimic-fn/4.0.0:
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
engines: {node: '>=12'}
dev: true
/mimic-response/1.0.1: /mimic-response/1.0.1:
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -10234,6 +10318,13 @@ packages:
path-key: 3.1.1 path-key: 3.1.1
dev: true dev: true
/npm-run-path/5.1.0:
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
path-key: 4.0.0
dev: true
/npmlog/4.1.2: /npmlog/4.1.2:
resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==}
dependencies: dependencies:
@ -10405,6 +10496,13 @@ packages:
mimic-fn: 2.1.0 mimic-fn: 2.1.0
dev: true dev: true
/onetime/6.0.0:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
dependencies:
mimic-fn: 4.0.0
dev: true
/open/8.4.0: /open/8.4.0:
resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -10593,6 +10691,11 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
/path-key/4.0.0:
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
engines: {node: '>=12'}
dev: true
/path-parse/1.0.7: /path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@ -10638,6 +10741,12 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
/pidtree/0.6.0:
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
engines: {node: '>=0.10'}
hasBin: true
dev: true
/pify/4.0.1: /pify/4.0.1:
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -11894,6 +12003,11 @@ packages:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
/string-argv/0.3.1:
resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
engines: {node: '>=0.6.19'}
dev: true
/string-env-interpolation/1.0.1: /string-env-interpolation/1.0.1:
resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==}
dev: true dev: true
@ -12008,6 +12122,11 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/strip-final-newline/3.0.0:
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
engines: {node: '>=12'}
dev: true
/strip-indent/3.0.0: /strip-indent/3.0.0:
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -13179,6 +13298,11 @@ packages:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
/yaml/2.2.1:
resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==}
engines: {node: '>= 14'}
dev: true
/yargs-parser/18.1.3: /yargs-parser/18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
engines: {node: '>=6'} engines: {node: '>=6'}

View file

@ -9,7 +9,7 @@
"outputs": ["dist/**", ".next/**"] "outputs": ["dist/**", ".next/**"]
}, },
"build#saleor-app-data-importer": { "build#saleor-app-data-importer": {
"env": ["APL", "NEXT_PUBLIC_NUVO_LICENSE_KEY", "NEXT_PUBLIC_VERCEL_ENV"] "env": ["APL", "NEXT_PUBLIC_NUVO_LICENSE_KEY", "NEXT_PUBLIC_VERCEL_ENV", "NEXT_PUBLIC_NUVO_PROD_MODE"]
}, },
"build#saleor-app-invoices": { "build#saleor-app-invoices": {
"env": [ "env": [