Compare commits
1 commit
main
...
peelar-pat
Author | SHA1 | Date | |
---|---|---|---|
![]() |
80b559f58d |
332 changed files with 4179 additions and 79924 deletions
5
.changeset/light-bobcats-prove.md
Normal file
5
.changeset/light-bobcats-prove.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-cms-v2": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix styling of modal in the dark mode
|
5
.changeset/nice-jobs-ring.md
Normal file
5
.changeset/nice-jobs-ring.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-taxes": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Removed all the code related to the deprecated OrderCreated & OrderFulfilled flow. The migration process began in version 1.13.0. All the cloud environments had been migrated automatically.
|
|
@ -1,8 +0,0 @@
|
||||||
#changelog
|
|
||||||
---
|
|
||||||
"apps": minor
|
|
||||||
---
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `apps/emails-and-messages/.env.template`: Described the new environment variable and how it works
|
|
||||||
- `apps/emails-and-messages/src/saleor-app.ts`: Added case "redis" for switch(AplType), which takes advantage of the [RedisAPL PR](https://github.com/saleor/app-sdk/pull/287) I submitted
|
|
5
.changeset/strong-pugs-cover.md
Normal file
5
.changeset/strong-pugs-cover.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-cms-v2": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added Payload CMS support.
|
5
.changeset/unlucky-tips-smash.md
Normal file
5
.changeset/unlucky-tips-smash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-taxes": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed the error when checkout couldn't calculate taxes when no customerCode was provided. In calculate taxes, the customerCode is now derived from issuingPrincipal's id.
|
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -8,4 +8,5 @@
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] `.github/dependabot.yaml` is up-to date.
|
||||||
- [ ] I added changesets and [read good practices](/.changeset/README.md).
|
- [ ] I added changesets and [read good practices](/.changeset/README.md).
|
||||||
|
|
120
.github/dependabot.yaml
vendored
120
.github/dependabot.yaml
vendored
|
@ -2,18 +2,118 @@ version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "[skip ci]"
|
prefix: "[skip ci]"
|
||||||
groups:
|
|
||||||
trpc:
|
|
||||||
patterns:
|
|
||||||
- "@trpc/*"
|
|
||||||
codegen:
|
|
||||||
patterns:
|
|
||||||
- "@graphql-codegen/*"
|
|
||||||
rtl:
|
|
||||||
patterns:
|
|
||||||
- "@testing-library/*"
|
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/eslint-config-saleor"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/shared"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/ui"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/packages/react-hook-form-macaw"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
|
||||||
|
# Apps
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/apps/cms-v2"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/crm
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/data-importer
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/emails-and-messages
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/invoices
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/klaviyo
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/products-feed
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/search
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: apps/slack
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
directory: apps/taxes
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
open-pull-requests-limit: 1
|
||||||
|
directory: apps/segment
|
||||||
|
commit-message:
|
||||||
|
prefix: "[skip ci]"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
|
9
.github/workflows/assign-pr.yml
vendored
9
.github/workflows/assign-pr.yml
vendored
|
@ -6,12 +6,9 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
assign_creator:
|
assign_creator:
|
||||||
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Assign PR to creator
|
- name: Assign PR to creator
|
||||||
env:
|
uses: thomaseizinger/assign-pr-creator-action@v1.0.0
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
with:
|
||||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
CREATOR: ${{ github.event.pull_request.user.login }}
|
|
||||||
run: gh pr edit "$PR_URL" --add-assignee "$CREATOR"
|
|
31
.github/workflows/changeset-checker.yml
vendored
31
.github/workflows/changeset-checker.yml
vendored
|
@ -1,31 +0,0 @@
|
||||||
name: Changesets
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- labeled
|
|
||||||
- edited
|
|
||||||
- synchronize
|
|
||||||
branches-ignore:
|
|
||||||
- 'changeset-release/**'
|
|
||||||
jobs:
|
|
||||||
changeset_check:
|
|
||||||
name: Changeset added to the PR
|
|
||||||
# Adding 'skip changesets' label to the PR will skip this job
|
|
||||||
if: ${{ !contains( github.event.pull_request.labels.*.name, 'skip changeset') }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
# check out full history
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18
|
|
||||||
cache: "pnpm"
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
- name: Changeset added
|
|
||||||
run: npx changeset status --since origin/main
|
|
2
.github/workflows/check-deps.yml
vendored
2
.github/workflows/check-deps.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- uses: JamieMason/syncpack-github-action@c145cec44b3731b3fe8e859679e240d6ae011f0f
|
- uses: JamieMason/syncpack-github-action@0.2.2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
package-manager: "pnpm"
|
package-manager: "pnpm"
|
||||||
|
|
10
.github/workflows/check-spelling.yml
vendored
10
.github/workflows/check-spelling.yml
vendored
|
@ -1,10 +0,0 @@
|
||||||
name: "Check spelling"
|
|
||||||
on: # rebuild any PRs and main branch changes
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
spellcheck: # run the action
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: streetsidesoftware/cspell-action@22e32eb3d70acf30e3fc09bd46edc1d30fb2d6db
|
|
4
.github/workflows/prepare-release.yml
vendored
4
.github/workflows/prepare-release.yml
vendored
|
@ -25,11 +25,11 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
- uses: pnpm/action-setup@d882d12c64e032187b2edb46d3a0d003b7a43598
|
- uses: pnpm/action-setup@v2
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
- name: Create Release Pull Request
|
- name: Create Release Pull Request
|
||||||
uses: changesets/action@f13b1baaa620fde937751f5d2c3572b9da32af23
|
uses: changesets/action@v1
|
||||||
id: changesets
|
id: changesets
|
||||||
with:
|
with:
|
||||||
title: 🚀 Release apps
|
title: 🚀 Release apps
|
||||||
|
|
2
.github/workflows/unit-tests.yml
vendored
2
.github/workflows/unit-tests.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
|
- uses: pnpm/action-setup@v2.2.4
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,5 +42,4 @@ apps/**/generated
|
||||||
|
|
||||||
.sentryclirc
|
.sentryclirc
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.cspellcache
|
|
|
@ -1 +1 @@
|
||||||
* @saleor/delivery-engineering-js
|
* @saleor/appstore
|
|
@ -59,11 +59,6 @@ In the `apps` folder, you will find the following applications:
|
||||||
- [slack](./apps/slack) - get notifications on Slack channel from Saleor events.
|
- [slack](./apps/slack) - get notifications on Slack channel from Saleor events.
|
||||||
- [taxes](https://docs.saleor.io/docs/3.x/developer/app-store/apps/taxes) - calculate order and checkout taxes using external services.
|
- [taxes](https://docs.saleor.io/docs/3.x/developer/app-store/apps/taxes) - calculate order and checkout taxes using external services.
|
||||||
|
|
||||||
## Another official apps
|
|
||||||
|
|
||||||
Some of Saleor apps are available in separate repositories:
|
|
||||||
- [Stripe](https://github.com/saleor/saleor-app-payment-stripe)
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
You can find the documentation for saleor/apps on [docs.saleor.io](https://docs.saleor.io/docs/3.x/developer/app-store/development).
|
You can find the documentation for saleor/apps on [docs.saleor.io](https://docs.saleor.io/docs/3.x/developer/app-store/development).
|
||||||
|
|
|
@ -1,54 +1,5 @@
|
||||||
# saleor-app-cms-v2
|
# saleor-app-cms-v2
|
||||||
|
|
||||||
## 2.3.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5d3d81d: Bumped @hookform/resolvers from 2.9.11 to 3.3.1
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 2.3.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- e8660e8: Extracted UI components and use shared package
|
|
||||||
- 30140ee: Improved some text typos.
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
- @saleor/apps-ui@1.2.0
|
|
||||||
|
|
||||||
## 2.3.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- d9d0f64: Fixed background color of the modal. Previously white background was missing and only blur was applied. Now it is blurred and white with an opacity (in the dark mode it is fixed with dark grey)
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
- @saleor/react-hook-form-macaw@0.2.5
|
|
||||||
- @saleor/apps-ui@1.1.8
|
|
||||||
|
|
||||||
## 2.3.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 6f1c5c9: Added Payload CMS support.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- e9378e7: Fix PayloadCMS form where two inputs were stuck together without a margin. Now, they are placed in two columns
|
|
||||||
- 6f1c5c9: Fix styling of modal in the dark mode
|
|
||||||
|
|
||||||
## 2.2.2
|
## 2.2.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-cms-v2",
|
"name": "saleor-app-cms-v2",
|
||||||
"version": "2.3.3",
|
"version": "2.2.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -13,18 +13,18 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@datocms/cma-client-browser": "2.0.0",
|
"@datocms/cma-client-browser": "2.0.0",
|
||||||
"@hookform/resolvers": "^3.3.1",
|
"@hookform/resolvers": "^3.1.0",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/apps-ui": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||||
"@sentry/nextjs": "7.67.0",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@tanstack/react-query": "^4.29.19",
|
"@tanstack/react-query": "^4.29.19",
|
||||||
"@trpc/client": "10.38.1",
|
"@trpc/client": "10.34.0",
|
||||||
"@trpc/next": "10.38.1",
|
"@trpc/next": "10.34.0",
|
||||||
"@trpc/react-query": "10.38.1",
|
"@trpc/react-query": "10.34.0",
|
||||||
"@trpc/server": "10.38.1",
|
"@trpc/server": "10.34.0",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.0.4",
|
||||||
"contentful-management": "10.38.3",
|
"contentful-management": "10.38.3",
|
||||||
|
@ -48,14 +48,14 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
"@graphql-codegen/typescript": "4.0.1",
|
"@graphql-codegen/typescript": "4.0.1",
|
||||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||||
"@graphql-typed-document-node/core": "3.2.0",
|
"@graphql-typed-document-node/core": "3.2.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react": "18.2.5",
|
"@types/react": "18.2.5",
|
||||||
|
|
|
@ -6,20 +6,26 @@ 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 { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
connectionId: z.string().min(7),
|
connectionId: z.string().min(7),
|
||||||
});
|
});
|
||||||
|
|
||||||
const EmptyState = () => (
|
const EmptyState = () => (
|
||||||
<Layout.AppSectionCard>
|
<Box
|
||||||
<Box display="flex" flexDirection={"column"} gap={4} justifyContent={"center"}>
|
display="flex"
|
||||||
<Text variant="heading">Bulk products synchronization</Text>
|
paddingY={4}
|
||||||
<Text>Create a channel connection above to enable bulk synchronization.</Text>
|
flexDirection={"column"}
|
||||||
</Box>
|
gap={4}
|
||||||
</Layout.AppSectionCard>
|
alignItems={"center"}
|
||||||
|
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 = () => {
|
||||||
|
@ -36,7 +42,7 @@ export const BulkSyncSection = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!connections || !providers) {
|
if (!connections || !providers) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connections.length === 0) {
|
if (connections.length === 0) {
|
||||||
|
@ -44,7 +50,7 @@ export const BulkSyncSection = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard>
|
<Box>
|
||||||
<Text as="h2" marginBottom={6} variant="heading">
|
<Text as="h2" marginBottom={6} variant="heading">
|
||||||
Bulk products synchronization
|
Bulk products synchronization
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -82,6 +88,6 @@ export const BulkSyncSection = () => {
|
||||||
<Button type="submit">Start sync</Button>
|
<Button type="submit">Start sync</Button>
|
||||||
</ButtonsBox>
|
</ButtonsBox>
|
||||||
</Box>
|
</Box>
|
||||||
</Layout.AppSectionCard>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { Breadcrumbs, ButtonsBox, Layout } from "@saleor/apps-ui";
|
import { Breadcrumbs } 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";
|
||||||
|
@ -12,19 +14,16 @@ import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
|
||||||
const FetchProductsStep = (props: { onButtonClick(): void }) => {
|
const FetchProductsStep = (props: { onButtonClick(): void }) => {
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard
|
<Box>
|
||||||
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>
|
||||||
</Layout.AppSectionCard>
|
<ButtonsBox>
|
||||||
|
<Button onClick={props.onButtonClick}>Prefetch products</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,7 +66,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 } =
|
||||||
|
@ -122,14 +121,9 @@ export const BulkSyncView = ({
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout.AppSection
|
<AppSection
|
||||||
marginBottom={8}
|
marginBottom={8}
|
||||||
heading="1. Fetch products"
|
mainContent={(() => {
|
||||||
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 (
|
||||||
|
@ -155,29 +149,33 @@ export const BulkSyncView = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
</Layout.AppSection>
|
heading="1. Fetch products"
|
||||||
|
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 && (
|
||||||
<Layout.AppSection
|
<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={
|
||||||
<Layout.AppSectionCard>
|
<Box>
|
||||||
<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} />
|
||||||
</Layout.AppSectionCard>
|
</Box>
|
||||||
</Layout.AppSection>
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
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,
|
||||||
|
@ -6,7 +7,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 { ButtonsBox, SkeletonLayout } from "@saleor/apps-ui";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
const defaultValues: AddConnectionFormSchema = { channelSlug: "", providerId: "" };
|
const defaultValues: AddConnectionFormSchema = { channelSlug: "", providerId: "" };
|
||||||
|
|
||||||
|
@ -14,7 +15,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 <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { mutateAsync: addProviderMutate, isLoading } =
|
const { mutateAsync: addProviderMutate, isLoading } =
|
||||||
|
|
|
@ -1,31 +1,35 @@
|
||||||
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 as="p">
|
<Text marginBottom={4} 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 {
|
const { data: connectionsData, refetch: refetchConnections } =
|
||||||
data: connectionsData,
|
trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
||||||
refetch: refetchConnections,
|
|
||||||
isLoading,
|
|
||||||
} = trpcClient.channelsProvidersConnection.fetchConnections.useQuery();
|
|
||||||
|
|
||||||
const { mutate: removeConnection } =
|
const { mutate: removeConnection } =
|
||||||
trpcClient.channelsProvidersConnection.removeConnection.useMutation({
|
trpcClient.channelsProvidersConnection.removeConnection.useMutation({
|
||||||
|
@ -41,33 +45,19 @@ export const ChannelProviderConnectionList = () => {
|
||||||
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
const { data: providers } = trpcClient.providersConfigs.getAll.useQuery();
|
||||||
|
|
||||||
if (!providers) {
|
if (!providers) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = (connectionId: string) => {
|
const handleDelete = (connectionId: string) => {
|
||||||
removeConnection({ id: connectionId });
|
removeConnection({ id: connectionId });
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading || !connectionsData) {
|
if (!connectionsData) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Text>Loading</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard
|
<Box>
|
||||||
footer={
|
|
||||||
providers.length > 0 && (
|
|
||||||
<ButtonsBox>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setDialogOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add connection
|
|
||||||
</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{dialogOpen && (
|
{dialogOpen && (
|
||||||
<AddConnectionModal
|
<AddConnectionModal
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
@ -89,6 +79,17 @@ export const ChannelProviderConnectionList = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{connectionsData.length > 0 && <ConnectionsList onRemove={handleDelete} />}
|
{connectionsData.length > 0 && <ConnectionsList onRemove={handleDelete} />}
|
||||||
</Layout.AppSectionCard>
|
{connectionsData.length > 0 && (
|
||||||
|
<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 { SkeletonLayout } from "@saleor/apps-ui";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
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 <SkeletonLayout.Section />;
|
return <Skeleton.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 { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
const ProvidersTable = (props: { providers: ProvidersConfig.AnyFullShape[] }) => {
|
const ProvidersTable = (props: { providers: ProvidersConfig.AnyFullShape[] }) => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
@ -47,45 +47,30 @@ export const ProvidersList = () => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard
|
<Box>
|
||||||
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 CMS configuration
|
Add first 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}>
|
||||||
|
@ -94,6 +79,15 @@ export const ProvidersList = () => {
|
||||||
<ProvidersTable providers={data} />
|
<ProvidersTable providers={data} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Layout.AppSectionCard>
|
<ButtonsBox marginTop={8}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
push("/add-provider");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add CMS configuration
|
||||||
|
</Button>
|
||||||
|
</ButtonsBox>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,9 @@ 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, SkeletonLayout, TextLink } from "@saleor/apps-ui";
|
import { ButtonsBox } from "../../ui/buttons-box";
|
||||||
|
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 });
|
||||||
|
@ -125,7 +127,7 @@ const PureForm = ({ defaultValues, onSubmit, onDelete }: PureFormProps) => {
|
||||||
size="small"
|
size="small"
|
||||||
control={control}
|
control={control}
|
||||||
name={`productVariantFieldsMapping.${saleorField}`}
|
name={`productVariantFieldsMapping.${saleorField}`}
|
||||||
label="Builder.io Field"
|
label="Builer.io Field"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
|
@ -205,11 +207,11 @@ const EditFormVariant = (props: { configId: string }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type !== "builder.io") {
|
if (data.type !== "builder.io") {
|
||||||
throw new Error("Trying to fill builder.io form with non builder.io data");
|
throw new Error("Trying to fill builer.io form with non builder.io data");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,7 +9,8 @@ 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, TextLink } from "@saleor/apps-ui";
|
import { ButtonsBox } from "../../ui/buttons-box";
|
||||||
|
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,7 +9,8 @@ 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, TextLink } from "@saleor/apps-ui";
|
import { ButtonsBox } from "../../ui/buttons-box";
|
||||||
|
import { TextLink } from "@saleor/apps-ui";
|
||||||
|
|
||||||
type FormShape = Omit<PayloadCmsProviderConfig.InputShape, "type">;
|
type FormShape = Omit<PayloadCmsProviderConfig.InputShape, "type">;
|
||||||
|
|
||||||
|
@ -81,19 +82,17 @@ const PureForm = ({ defaultValues, onSubmit, onDelete }: PureFormProps) => {
|
||||||
>
|
>
|
||||||
Read more in Payload docs
|
Read more in Payload docs
|
||||||
</TextLink>
|
</TextLink>
|
||||||
<Text as="p" marginBottom={6}>
|
<Text as="p" marginBottom={4}>
|
||||||
If your API is open (e.g. for development purposes) leave both fields empty.
|
If your API is open (e.g. for development purposes) leave both fields empty.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box display="grid" gap={4} gridTemplateColumns={2}>
|
<Input
|
||||||
<Input
|
control={control}
|
||||||
control={control}
|
name="authenticatedUserSlug"
|
||||||
name="authenticatedUserSlug"
|
label="Authenticated user slug"
|
||||||
label="Authenticated user slug"
|
placeholder="e.g. apps"
|
||||||
placeholder="e.g. apps"
|
/>
|
||||||
/>
|
<Input control={control} name="authToken" type="password" label="User API Key" />
|
||||||
<Input control={control} name="authToken" type="password" label="User API Key" />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display={"grid"} gap={4} marginY={4}>
|
<Box display={"grid"} gap={4} marginY={4}>
|
||||||
|
|
|
@ -7,7 +7,8 @@ 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, SkeletonLayout } from "@saleor/apps-ui";
|
import { ButtonsBox } from "../../ui/buttons-box";
|
||||||
|
import { Skeleton } from "@/modules/ui/skeleton";
|
||||||
|
|
||||||
type FormShape = Omit<StrapiProviderConfig.InputShape, "type">;
|
type FormShape = Omit<StrapiProviderConfig.InputShape, "type">;
|
||||||
|
|
||||||
|
@ -188,7 +189,7 @@ const EditFormVariant = (props: { configId: string }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type !== "strapi") {
|
if (data.type !== "strapi") {
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
/**
|
// todo move to app sdk
|
||||||
* TODO Consume this from App SDK
|
export const getBaseUrl = (headers: { [name: string]: string | string[] | undefined }): string => {
|
||||||
*/
|
|
||||||
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)
|
|
@ -9,7 +9,7 @@ dialog {
|
||||||
|
|
||||||
.dialog-overlay {
|
.dialog-overlay {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: color-mix(in srgb, var(--mu-colors-background-plain) 80%, transparent);
|
background: rgba(var(--mu-colors-background-plain), 0.8);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
content: "";
|
content: "";
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
@ -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 { getAppBaseUrl } from "@saleor/apps-shared";
|
import { getBaseUrl } from "@/modules/shared/get-base-url";
|
||||||
|
|
||||||
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
||||||
const baseUrl = getAppBaseUrl(req.headers);
|
const baseUrl = getBaseUrl(req.headers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
token: req.headers[SALEOR_AUTHORIZATION_BEARER_HEADER] as string | undefined,
|
||||||
|
|
43
apps/cms-v2/src/modules/ui/app-section.tsx
Normal file
43
apps/cms-v2/src/modules/ui/app-section.tsx
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
29
apps/cms-v2/src/modules/ui/skeleton.tsx
Normal file
29
apps/cms-v2/src/modules/ui/skeleton.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
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,6 +1,7 @@
|
||||||
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 { Breadcrumbs, Layout } from "@saleor/apps-ui";
|
import { AppSection } from "@/modules/ui/app-section";
|
||||||
|
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";
|
||||||
|
@ -15,15 +16,14 @@ 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>]}
|
||||||
/>
|
/>
|
||||||
<Layout.AppSection
|
<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,7 +2,8 @@ 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 { Breadcrumbs, Layout } from "@saleor/apps-ui";
|
import { AppSection } from "@/modules/ui/app-section";
|
||||||
|
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";
|
||||||
|
@ -31,7 +32,7 @@ const AddProviderPage: NextPage = () => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Layout.AppSection
|
<AppSection
|
||||||
heading={`Set up ${provider.displayName}`}
|
heading={`Set up ${provider.displayName}`}
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -39,11 +40,8 @@ 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,9 +1,10 @@
|
||||||
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();
|
||||||
|
@ -22,7 +23,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!parsedID,
|
enabled: !!parsedID,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -35,7 +36,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!connection,
|
enabled: !!connection,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((providerFetched && !provider) || (connectionFetched && !connection)) {
|
if ((providerFetched && !provider) || (connectionFetched && !connection)) {
|
||||||
|
@ -44,7 +45,7 @@ const BulkSyncPage: NextPage = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionLoading || providerLoading) {
|
if (connectionLoading || providerLoading) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.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 { Layout } from "@saleor/apps-ui";
|
import { AppSection } from "@/modules/ui/app-section";
|
||||||
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 />
|
||||||
<Layout.AppSection
|
<AppSection
|
||||||
marginBottom={14}
|
marginBottom={14}
|
||||||
heading="Providers configuration"
|
heading="Providers configuration"
|
||||||
sideContent={
|
sideContent={
|
||||||
|
@ -18,10 +18,9 @@ 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 />
|
/>
|
||||||
</Layout.AppSection>
|
<AppSection
|
||||||
<Layout.AppSection
|
|
||||||
marginBottom={14}
|
marginBottom={14}
|
||||||
heading="Automatic synchronization"
|
heading="Automatic synchronization"
|
||||||
sideContent={
|
sideContent={
|
||||||
|
@ -32,10 +31,9 @@ const ConfigurationPage: NextPage = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
mainContent={<ChannelProviderConnectionList />}
|
||||||
<ChannelProviderConnectionList />
|
/>
|
||||||
</Layout.AppSection>
|
<AppSection
|
||||||
<Layout.AppSection
|
|
||||||
heading="Initial sync"
|
heading="Initial sync"
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -46,9 +44,8 @@ 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,7 +2,9 @@ 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 { Breadcrumbs, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
import { AppSection } from "@/modules/ui/app-section";
|
||||||
|
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";
|
||||||
|
@ -18,7 +20,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!configId,
|
enabled: !!configId,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
|
@ -26,7 +28,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFetched && !data) {
|
if (isFetched && !data) {
|
||||||
|
@ -36,7 +38,7 @@ const EditProviderPage: NextPage = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Skeleton.Section />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditForm = ProvidersResolver.getEditProviderFormComponent(provider.type);
|
const EditForm = ProvidersResolver.getEditProviderFormComponent(provider.type);
|
||||||
|
@ -51,16 +53,13 @@ const EditProviderPage: NextPage = () => {
|
||||||
<Breadcrumbs.Item key="configname">{data?.configName}</Breadcrumbs.Item>,
|
<Breadcrumbs.Item key="configname">{data?.configName}</Breadcrumbs.Item>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Layout.AppSection
|
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,34 +1,5 @@
|
||||||
# saleor-app-crm
|
# saleor-app-crm
|
||||||
|
|
||||||
## 1.7.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 1.7.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
|
|
||||||
## 1.7.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
|
|
||||||
## 1.7.5
|
## 1.7.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-crm",
|
"name": "saleor-app-crm",
|
||||||
"version": "1.7.8",
|
"version": "1.7.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -13,15 +13,15 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mailchimp/mailchimp_marketing": "^3.0.80",
|
"@mailchimp/mailchimp_marketing": "^3.0.80",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@sentry/nextjs": "7.67.0",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@tanstack/react-query": "4.29.19",
|
"@tanstack/react-query": "4.29.19",
|
||||||
"@trpc/client": "10.38.1",
|
"@trpc/client": "10.34.0",
|
||||||
"@trpc/next": "10.38.1",
|
"@trpc/next": "10.34.0",
|
||||||
"@trpc/react-query": "10.38.1",
|
"@trpc/react-query": "10.34.0",
|
||||||
"@trpc/server": "10.38.1",
|
"@trpc/server": "10.34.0",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.0.4",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
|
@ -42,14 +42,14 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
"@graphql-codegen/typescript": "4.0.1",
|
"@graphql-codegen/typescript": "4.0.1",
|
||||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||||
"@graphql-typed-document-node/core": "3.2.0",
|
"@graphql-typed-document-node/core": "3.2.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@types/mailchimp__mailchimp_marketing": "^3.0.7",
|
"@types/mailchimp__mailchimp_marketing": "^3.0.7",
|
||||||
"@types/react": "18.2.5",
|
"@types/react": "18.2.5",
|
||||||
|
|
|
@ -1,34 +1,5 @@
|
||||||
# saleor-app-data-importer
|
# saleor-app-data-importer
|
||||||
|
|
||||||
## 1.9.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 1.9.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
|
|
||||||
## 1.9.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
|
|
||||||
## 1.9.4
|
## 1.9.4
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-data-importer",
|
"name": "saleor-app-data-importer",
|
||||||
"version": "1.9.7",
|
"version": "1.9.4",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -15,12 +15,13 @@
|
||||||
"@material-ui/core": "^4.12.4",
|
"@material-ui/core": "^4.12.4",
|
||||||
"@material-ui/icons": "^4.11.3",
|
"@material-ui/icons": "^4.11.3",
|
||||||
"@material-ui/lab": "4.0.0-alpha.61",
|
"@material-ui/lab": "4.0.0-alpha.61",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@sentry/nextjs": "7.67.0",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.0.4",
|
||||||
|
"clsx": "^1.2.1",
|
||||||
"dot-object": "^2.1.4",
|
"dot-object": "^2.1.4",
|
||||||
"graphql": "16.7.1",
|
"graphql": "16.7.1",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/schema-ast": "4.0.0",
|
"@graphql-codegen/schema-ast": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||||
"@graphql-typed-document-node/core": "3.2.0",
|
"@graphql-typed-document-node/core": "3.2.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@types/dot-object": "^2.1.2",
|
"@types/dot-object": "^2.1.2",
|
||||||
"@types/react": "18.2.5",
|
"@types/react": "18.2.5",
|
||||||
|
|
|
@ -7,12 +7,6 @@ APL=
|
||||||
REST_APL_ENDPOINT=
|
REST_APL_ENDPOINT=
|
||||||
REST_APL_TOKEN=
|
REST_APL_TOKEN=
|
||||||
|
|
||||||
# To use Redis as an APL store, set APP_API_BASE_URL and REDIS_URL.
|
|
||||||
# URL is in format redis[s]://[[username][:password]@][host][:port][/db-number],
|
|
||||||
# so for example redis://alice:foobared@awesome.redis.server:6380
|
|
||||||
# For saleor-platform, thats: `redis://redis:6379/1`
|
|
||||||
REDIS_URL=
|
|
||||||
|
|
||||||
APP_LOG_LEVEL=info
|
APP_LOG_LEVEL=info
|
||||||
|
|
||||||
# Local development variables. When developped locally with Saleor inside docker, these can be set to:
|
# Local development variables. When developped locally with Saleor inside docker, these can be set to:
|
||||||
|
@ -21,5 +15,4 @@ APP_LOG_LEVEL=info
|
||||||
# If developped with tunnels, set this empty, it will fallback to default Next's localhost:3000
|
# If developped with tunnels, set this empty, it will fallback to default Next's localhost:3000
|
||||||
# https://docs.saleor.io/docs/3.x/developer/extending/apps/local-app-development
|
# https://docs.saleor.io/docs/3.x/developer/extending/apps/local-app-development
|
||||||
APP_IFRAME_BASE_URL=
|
APP_IFRAME_BASE_URL=
|
||||||
APP_API_BASE_URL=
|
APP_API_BASE_URL=
|
||||||
|
|
|
@ -1,40 +1,5 @@
|
||||||
# saleor-app-emails-and-messages
|
# saleor-app-emails-and-messages
|
||||||
|
|
||||||
## 1.9.10
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5d3d81d: Bumped @hookform/resolvers from 2.9.11 to 3.3.1
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 1.9.9
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
- @saleor/apps-ui@1.2.0
|
|
||||||
|
|
||||||
## 1.9.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
- @saleor/react-hook-form-macaw@0.2.5
|
|
||||||
- @saleor/apps-ui@1.1.8
|
|
||||||
|
|
||||||
## 1.9.7
|
## 1.9.7
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-emails-and-messages",
|
"name": "saleor-app-emails-and-messages",
|
||||||
"version": "1.9.10",
|
"version": "1.9.7",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -12,23 +12,24 @@
|
||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.3.1",
|
"@hookform/resolvers": "^3.1.0",
|
||||||
"@monaco-editor/react": "^4.4.6",
|
"@monaco-editor/react": "^4.4.6",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/apps-ui": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||||
"@sendgrid/client": "^7.7.0",
|
"@sendgrid/client": "^7.7.0",
|
||||||
"@sendgrid/mail": "^7.7.0",
|
"@sendgrid/mail": "^7.7.0",
|
||||||
"@sentry/nextjs": "7.67.0",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@tanstack/react-query": "4.29.19",
|
"@tanstack/react-query": "4.29.19",
|
||||||
"@trpc/client": "10.38.1",
|
"@trpc/client": "10.34.0",
|
||||||
"@trpc/next": "10.38.1",
|
"@trpc/next": "10.34.0",
|
||||||
"@trpc/react-query": "10.38.1",
|
"@trpc/react-query": "10.34.0",
|
||||||
"@trpc/server": "10.38.1",
|
"@trpc/server": "10.34.0",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.0.4",
|
||||||
|
"clsx": "^1.2.1",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"graphql": "16.7.1",
|
"graphql": "16.7.1",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
@ -51,7 +52,7 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/schema-ast": "4.0.0",
|
"@graphql-codegen/schema-ast": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||||
"@graphql-typed-document-node/core": "3.2.0",
|
"@graphql-typed-document-node/core": "3.2.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@types/html-to-text": "^9.0.0",
|
"@types/html-to-text": "^9.0.0",
|
||||||
"@types/mjml": "^4.7.0",
|
"@types/mjml": "^4.7.0",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { APL, FileAPL, RedisAPL, SaleorCloudAPL, UpstashAPL } from "@saleor/app-sdk/APL";
|
import { APL, FileAPL, SaleorCloudAPL, UpstashAPL } from "@saleor/app-sdk/APL";
|
||||||
import { SaleorApp } from "@saleor/app-sdk/saleor-app";
|
import { SaleorApp } from "@saleor/app-sdk/saleor-app";
|
||||||
|
|
||||||
const aplType = process.env.APL ?? "file";
|
const aplType = process.env.APL ?? "file";
|
||||||
|
@ -6,12 +6,6 @@ const aplType = process.env.APL ?? "file";
|
||||||
export let apl: APL;
|
export let apl: APL;
|
||||||
|
|
||||||
switch (aplType) {
|
switch (aplType) {
|
||||||
case "redis": {
|
|
||||||
if (!process.env.REDIS_URL) throw new Error("Missing redis url");
|
|
||||||
if (!process.env.APP_API_BASE_URL)
|
|
||||||
throw new Error("Redis relies on APP_API_BASE_URL to store keys, please set env variable");
|
|
||||||
apl = new RedisAPL(new URL(process.env.REDIS_URL), process.env.APP_API_BASE_URL);
|
|
||||||
}
|
|
||||||
case "upstash":
|
case "upstash":
|
||||||
apl = new UpstashAPL();
|
apl = new UpstashAPL();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["//"],
|
||||||
"//"
|
|
||||||
],
|
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
|
@ -23,8 +21,7 @@
|
||||||
"NEXT_PUBLIC_SENTRY_DSN",
|
"NEXT_PUBLIC_SENTRY_DSN",
|
||||||
"SENTRY_ENVIRONMENT",
|
"SENTRY_ENVIRONMENT",
|
||||||
"APP_IFRAME_BASE_URL",
|
"APP_IFRAME_BASE_URL",
|
||||||
"APP_API_BASE_URL",
|
"APP_API_BASE_URL"
|
||||||
"REDIS_URL"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,5 @@
|
||||||
# saleor-app-invoices
|
# saleor-app-invoices
|
||||||
|
|
||||||
## 1.16.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 4aee4e1: Replace text "loading" messages with skeletons
|
|
||||||
- 4aee4e1: Redesigned app layout. Now app uses shared sections as other apps.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5d3d81d: Bumped @hookform/resolvers from 2.9.11 to 3.3.1
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 1.15.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
|
|
||||||
## 1.15.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
|
|
||||||
## 1.15.5
|
## 1.15.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
const { withSentryConfig } = require("@sentry/nextjs");
|
const { withSentryConfig } = require("@sentry/nextjs");
|
||||||
|
|
||||||
const isSentryPropertiesInEnvironment = Boolean(
|
const isSentryPropertiesInEnvironment = Boolean(
|
||||||
process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_PROJECT && process.env.SENTRY_ORG,
|
process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_PROJECT && process.env.SENTRY_ORG
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
transpilePackages: [
|
transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw"],
|
||||||
"@saleor/apps-shared",
|
|
||||||
"@saleor/apps-ui",
|
|
||||||
"@saleor/react-hook-form-macaw",
|
|
||||||
"@saleor/trpc",
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configWithSentry = withSentryConfig(
|
const configWithSentry = withSentryConfig(
|
||||||
|
@ -28,7 +23,9 @@ const configWithSentry = withSentryConfig(
|
||||||
tunnelRoute: "/monitoring",
|
tunnelRoute: "/monitoring",
|
||||||
hideSourceMaps: true,
|
hideSourceMaps: true,
|
||||||
disableLogger: true,
|
disableLogger: true,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
module.exports = isSentryPropertiesInEnvironment ? configWithSentry : nextConfig;
|
module.exports = isSentryPropertiesInEnvironment ? configWithSentry : nextConfig;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-invoices",
|
"name": "saleor-app-invoices",
|
||||||
"version": "1.16.0",
|
"version": "1.15.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -12,20 +12,19 @@
|
||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.3.1",
|
"@hookform/resolvers": "^3.1.0",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@saleor/trpc": "workspace:*",
|
|
||||||
"@sentry/nextjs": "7.67.0",
|
|
||||||
"@tanstack/react-query": "4.29.19",
|
"@tanstack/react-query": "4.29.19",
|
||||||
"@trpc/client": "10.38.1",
|
"@trpc/client": "10.34.0",
|
||||||
"@trpc/next": "10.38.1",
|
"@trpc/next": "10.34.0",
|
||||||
"@trpc/react-query": "10.38.1",
|
"@trpc/react-query": "10.34.0",
|
||||||
"@trpc/server": "10.38.1",
|
"@trpc/server": "10.34.0",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@web-std/file": "^3.0.2",
|
"@web-std/file": "^3.0.2",
|
||||||
|
"clsx": "^1.2.1",
|
||||||
"eslint": "8.46.0",
|
"eslint": "8.46.0",
|
||||||
"graphql": "16.7.1",
|
"graphql": "16.7.1",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
@ -42,7 +41,7 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/schema-ast": "4.0.0",
|
"@graphql-codegen/schema-ast": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
|
|
25
apps/invoices/src/lib/theme-synchronizer.tsx
Normal file
25
apps/invoices/src/lib/theme-synchronizer.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
|
import { useTheme } from "@saleor/macaw-ui/next";
|
||||||
|
import { memo, 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;
|
||||||
|
}
|
|
@ -53,8 +53,6 @@ describe("appConfigurationRouter", function () {
|
||||||
token: "TOKEN",
|
token: "TOKEN",
|
||||||
saleorApiUrl: "http://localhost:8000/graphql/",
|
saleorApiUrl: "http://localhost:8000/graphql/",
|
||||||
appId: "app",
|
appId: "app",
|
||||||
ssr: true,
|
|
||||||
baseUrl: "localhost:3000",
|
|
||||||
})
|
})
|
||||||
.upsertChannelOverride({
|
.upsertChannelOverride({
|
||||||
channelSlug: "test",
|
channelSlug: "test",
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import React, { useCallback, useEffect } from "react";
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
|
||||||
import { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
|
||||||
import { Box, Button, Input, Text } from "@saleor/macaw-ui/next";
|
import { Box, Button, Input, Text } from "@saleor/macaw-ui/next";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useCallback } from "react";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
|
||||||
import { SellerAddress } from "../address";
|
import { SellerAddress } from "../address";
|
||||||
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { AddressV2Schema, AddressV2Shape } from "../schema-v2/app-config-schema.v2";
|
import { AddressV2Schema, AddressV2Shape } from "../schema-v2/app-config-schema.v2";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -58,29 +57,12 @@ export const AddressForm = (props: Props & InnerFormProps) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard
|
<form
|
||||||
as="form"
|
|
||||||
footer={
|
|
||||||
<ButtonsBox>
|
|
||||||
<Button
|
|
||||||
variant="tertiary"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
props.onCancel();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text color={"textNeutralSubdued"}>Cancel</Text>
|
|
||||||
</Button>
|
|
||||||
<Button type="submit" variant="primary">
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</ButtonsBox>
|
|
||||||
}
|
|
||||||
onSubmit={handleSubmit((data, event) => {
|
onSubmit={handleSubmit((data, event) => {
|
||||||
return props.onSubmit(data);
|
return props.onSubmit(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Box display={"grid"} gap={3}>
|
<Box display={"grid"} gap={3} marginBottom={9}>
|
||||||
{fieldsBlock1.map((fieldName) => (
|
{fieldsBlock1.map((fieldName) => (
|
||||||
<Controller
|
<Controller
|
||||||
key={fieldName}
|
key={fieldName}
|
||||||
|
@ -146,7 +128,21 @@ export const AddressForm = (props: Props & InnerFormProps) => {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Layout.AppSectionCard>
|
<Box display={"grid"} justifyContent={"flex-end"} gap={1.5} gridAutoFlow={"column"}>
|
||||||
|
<Button
|
||||||
|
variant="tertiary"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
props.onCancel();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text color={"textNeutralSubdued"}>Cancel</Text>
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" variant="primary">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,6 +164,9 @@ export const ConnectedAddressForm = (props: Props) => {
|
||||||
|
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
|
const addressData =
|
||||||
|
channelOverrideConfigQuery.data && channelOverrideConfigQuery.data[props.channelSlug];
|
||||||
|
|
||||||
const submitHandler = useCallback(
|
const submitHandler = useCallback(
|
||||||
async (data: AddressV2Shape) => {
|
async (data: AddressV2Shape) => {
|
||||||
return upsertConfigMutation.mutate({
|
return upsertConfigMutation.mutate({
|
||||||
|
@ -175,7 +174,7 @@ export const ConnectedAddressForm = (props: Props) => {
|
||||||
channelSlug: props.channelSlug,
|
channelSlug: props.channelSlug,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[props.channelSlug, upsertConfigMutation],
|
[props.channelSlug, upsertConfigMutation]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onCancelHandler = useCallback(() => {
|
const onCancelHandler = useCallback(() => {
|
||||||
|
@ -183,7 +182,7 @@ export const ConnectedAddressForm = (props: Props) => {
|
||||||
}, [push]);
|
}, [push]);
|
||||||
|
|
||||||
if (channelOverrideConfigQuery.isLoading) {
|
if (channelOverrideConfigQuery.isLoading) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Text color={"textNeutralSubdued"}>Loading</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||||
import { DefaultShopAddress } from "../../shop-info/ui/default-shop-address";
|
import { DefaultShopAddress } from "../../shop-info/ui/default-shop-address";
|
||||||
|
import { AppSection } from "../../ui/AppSection";
|
||||||
import { PerChannelConfigList } from "../../channels/ui/per-channel-config-list";
|
import { PerChannelConfigList } from "../../channels/ui/per-channel-config-list";
|
||||||
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
|
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
import { Layout } from "@saleor/apps-ui";
|
|
||||||
|
|
||||||
export const AppConfigView = () => {
|
export const AppConfigView = () => {
|
||||||
const { appBridge } = useAppBridge();
|
const { appBridge } = useAppBridge();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Text as={"h1"} variant={"hero"} marginBottom={5}>
|
<Box
|
||||||
Configuration
|
display={"grid"}
|
||||||
</Text>
|
justifyContent={"space-between"}
|
||||||
<Text as={"p"} marginBottom={1.5}>
|
__gridTemplateColumns={"400px 400px"}
|
||||||
The Invoices App will generate invoices for each order, for which{" "}
|
gap={10}
|
||||||
<code>INVOICE_REQUESTED</code> event will be triggered
|
__marginBottom={"200px"}
|
||||||
</Text>
|
>
|
||||||
<Layout.AppSection
|
<Box>
|
||||||
marginTop={10}
|
<Text as={"h1"} variant={"hero"} marginBottom={5}>
|
||||||
heading={"Default address of the shop"}
|
Configuration
|
||||||
sideContent={
|
</Text>
|
||||||
|
<Text as={"p"} marginBottom={1.5}>
|
||||||
|
The Invoices App will generate invoices for each order, for which{" "}
|
||||||
|
<code>INVOICE_REQUESTED</code> event will be triggered
|
||||||
|
</Text>
|
||||||
<Text as={"p"} marginBottom={1.5}>
|
<Text as={"p"} marginBottom={1.5}>
|
||||||
By default it will use{" "}
|
By default it will use{" "}
|
||||||
<a
|
<a
|
||||||
|
@ -28,7 +32,7 @@ export const AppConfigView = () => {
|
||||||
appBridge?.dispatch(
|
appBridge?.dispatch(
|
||||||
actions.Redirect({
|
actions.Redirect({
|
||||||
to: "/site-settings",
|
to: "/site-settings",
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -36,25 +40,22 @@ export const AppConfigView = () => {
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
address, but each channel can be configured separately
|
address, but each channel can be configured separately
|
||||||
</Text>
|
</Text>
|
||||||
}
|
</Box>
|
||||||
>
|
<Box>
|
||||||
<DefaultShopAddress />
|
<DefaultShopAddress />
|
||||||
</Layout.AppSection>
|
</Box>
|
||||||
|
</Box>
|
||||||
<Layout.AppSection
|
<AppSection
|
||||||
marginTop={10}
|
includePadding={true}
|
||||||
heading={"Shop address per channel"}
|
heading={"Shop address per channel"}
|
||||||
|
mainContent={<PerChannelConfigList />}
|
||||||
sideContent={
|
sideContent={
|
||||||
<Text>
|
<Text>
|
||||||
Configure custom billing address for each channel. If not set, default shop address will
|
Configure custom billing address for each channel. If not set, default shop address will
|
||||||
be used
|
be used
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<Layout.AppSectionCard>
|
|
||||||
<PerChannelConfigList />
|
|
||||||
</Layout.AppSectionCard>
|
|
||||||
</Layout.AppSection>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Box, ChevronRightIcon, Text, Button } from "@saleor/macaw-ui/next";
|
import { Box, ChevronRightIcon, Text, Button } from "@saleor/macaw-ui/next";
|
||||||
|
import { AppSection } from "../../ui/AppSection";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { ConnectedAddressForm } from "../ui/address-form";
|
import { ConnectedAddressForm } from "../ui/address-form";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
import { Layout } from "@saleor/apps-ui";
|
|
||||||
|
|
||||||
export const ChannelConfigView = () => {
|
export const ChannelConfigView = () => {
|
||||||
const {
|
const {
|
||||||
|
@ -15,7 +15,7 @@ export const ChannelConfigView = () => {
|
||||||
const { notifySuccess } = useDashboardNotification();
|
const { notifySuccess } = useDashboardNotification();
|
||||||
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
return null; // TODO: error
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,9 +29,10 @@ export const ChannelConfigView = () => {
|
||||||
<Text>{channel}</Text>
|
<Text>{channel}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Layout.AppSection
|
<AppSection
|
||||||
includePadding={true}
|
includePadding={true}
|
||||||
heading={"Shop address per channel"}
|
heading={"Shop address per channel"}
|
||||||
|
mainContent={<ConnectedAddressForm channelSlug={channel as string} />}
|
||||||
sideContent={
|
sideContent={
|
||||||
<Box>
|
<Box>
|
||||||
<Text marginBottom={5} as={"p"}>
|
<Text marginBottom={5} as={"p"}>
|
||||||
|
@ -50,9 +51,7 @@ export const ChannelConfigView = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<ConnectedAddressForm channelSlug={channel as string} />
|
|
||||||
</Layout.AppSection>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Box, Text, Chip, Button } from "@saleor/macaw-ui/next";
|
import { Box, Text, Chip, Button } from "@saleor/macaw-ui/next";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { SkeletonLayout } from "@saleor/apps-ui";
|
|
||||||
|
|
||||||
const defaultAddressChip = (
|
const defaultAddressChip = (
|
||||||
<Chip __display={"inline-block"} size={"large"}>
|
<Chip __display={"inline-block"} size={"large"}>
|
||||||
|
@ -18,7 +17,7 @@ export const PerChannelConfigList = () => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
if (shopChannelsQuery.isLoading || channelsOverridesQuery.isLoading) {
|
if (shopChannelsQuery.isLoading || channelsOverridesQuery.isLoading) {
|
||||||
return <SkeletonLayout.Section />;
|
return <Text color={"textNeutralSubdued"}>Loading...</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderChannelAddress = (slug: string) => {
|
const renderChannelAddress = (slug: string) => {
|
||||||
|
|
|
@ -2,31 +2,30 @@ import { Box, Text, Button } from "@saleor/macaw-ui/next";
|
||||||
import { trpcClient } from "../../trpc/trpc-client";
|
import { trpcClient } from "../../trpc/trpc-client";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
|
import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||||
import { ButtonsBox, Layout, SkeletonLayout } from "@saleor/apps-ui";
|
|
||||||
|
|
||||||
const Wrapper = ({ children }: PropsWithChildren<{}>) => {
|
const Wrapper = ({ children }: PropsWithChildren<{}>) => {
|
||||||
const { appBridge } = useAppBridge();
|
const { appBridge } = useAppBridge();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.AppSectionCard
|
<Box>
|
||||||
footer={
|
<Box display={"flex"} justifyContent={"space-between"} marginBottom={5}>
|
||||||
<ButtonsBox>
|
<Text variant={"heading"}>Default address of the shop</Text>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
size={"small"}
|
||||||
appBridge?.dispatch(
|
variant={"tertiary"}
|
||||||
actions.Redirect({
|
onClick={() => {
|
||||||
to: "/site-settings",
|
appBridge?.dispatch(
|
||||||
}),
|
actions.Redirect({
|
||||||
);
|
to: "/site-settings",
|
||||||
}}
|
})
|
||||||
>
|
);
|
||||||
Edit
|
}}
|
||||||
</Button>
|
>
|
||||||
</ButtonsBox>
|
<Text color={"textNeutralSubdued"}>Edit</Text>
|
||||||
}
|
</Button>
|
||||||
>
|
</Box>
|
||||||
<Box>{children}</Box>
|
<Box>{children}</Box>
|
||||||
</Layout.AppSectionCard>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ export const DefaultShopAddress = () => {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<SkeletonLayout.Section />
|
<Text color={"textNeutralSubdued"}>Loading...</Text>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -71,9 +70,6 @@ export const DefaultShopAddress = () => {
|
||||||
if (data && data.companyAddress) {
|
if (data && data.companyAddress) {
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Text as="p" marginBottom={4} variant="caption">
|
|
||||||
This address will be used if custom address is not set for channel
|
|
||||||
</Text>
|
|
||||||
<Text size={"small"} as={"p"}>
|
<Text size={"small"} as={"p"}>
|
||||||
{data.companyAddress.companyName}
|
{data.companyAddress.companyName}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,13 +1,34 @@
|
||||||
|
import { httpBatchLink } from "@trpc/client";
|
||||||
import { createTRPCNext } from "@trpc/next";
|
import { createTRPCNext } from "@trpc/next";
|
||||||
|
|
||||||
import { createHttpBatchLink } from "@saleor/trpc";
|
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
||||||
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() {
|
config({ ctx }) {
|
||||||
return {
|
return {
|
||||||
links: [createHttpBatchLink(appBridgeInstance)],
|
links: [
|
||||||
|
httpBatchLink({
|
||||||
|
url: `${getBaseUrl()}/api/trpc`,
|
||||||
|
headers() {
|
||||||
|
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: { staleTime: 60 } } },
|
// queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
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";
|
|
||||||
|
|
||||||
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
export const createTrpcContext = async ({ res, req }: trpcNext.CreateNextContextOptions) => {
|
||||||
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,
|
||||||
saleorApiUrl: req.headers[SALEOR_API_URL_HEADER] as string | undefined,
|
saleorApiUrl: req.headers[SALEOR_API_URL_HEADER] as string | undefined,
|
||||||
appId: undefined as undefined | string,
|
appId: undefined as undefined | string,
|
||||||
ssr: undefined as undefined | boolean,
|
|
||||||
baseUrl,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { initTRPC } from "@trpc/server";
|
import { initTRPC } from "@trpc/server";
|
||||||
|
import { TrpcContext } from "./trpc-context";
|
||||||
import { Permission } from "@saleor/app-sdk/types";
|
import { Permission } from "@saleor/app-sdk/types";
|
||||||
import { TrpcContext } from "@saleor/trpc";
|
|
||||||
|
|
||||||
interface Meta {
|
interface Meta {
|
||||||
requiredClientPermissions?: Permission[];
|
requiredClientPermissions?: Permission[];
|
||||||
|
|
36
apps/invoices/src/modules/ui/AppSection.tsx
Normal file
36
apps/invoices/src/modules/ui/AppSection.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
|
@ -6,11 +6,12 @@ import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
|
|
||||||
import { NoSSRWrapper, ThemeSynchronizer } from "@saleor/apps-shared";
|
import { NoSSRWrapper } from "@saleor/apps-shared";
|
||||||
import { trpcClient } from "../modules/trpc/trpc-client";
|
import { trpcClient } from "../modules/trpc/trpc-client";
|
||||||
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
||||||
|
|
||||||
import { NextPage } from "next";
|
import { NextPage } from "next";
|
||||||
|
import { ThemeSynchronizer } from "../lib/theme-synchronizer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure instance is a singleton.
|
* Ensure instance is a singleton.
|
||||||
|
|
|
@ -1,7 +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 { createLogger } from "@saleor/apps-shared";
|
import { createLogger } from "@saleor/apps-shared";
|
||||||
import { createTrpcContext } from "@saleor/trpc";
|
|
||||||
|
|
||||||
const logger = createLogger({ name: "tRPC error" });
|
const logger = createLogger({ name: "tRPC error" });
|
||||||
|
|
||||||
|
|
BIN
apps/invoices/src/public/favicon.ico
Normal file
BIN
apps/invoices/src/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
4
apps/invoices/src/public/vercel.svg
Normal file
4
apps/invoices/src/public/vercel.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<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>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,3 +1,8 @@
|
||||||
|
body {
|
||||||
|
font-family: Inter, -apple-system, "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||||
|
"Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -1,40 +1,5 @@
|
||||||
# saleor-app-klaviyo
|
# saleor-app-klaviyo
|
||||||
|
|
||||||
## 1.9.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 4aee4e1: Improved app layout to match modern style.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
- 4aee4e1: Fixed error where config couldn't be saved
|
|
||||||
|
|
||||||
## 1.8.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 30140ee: Improved some text typos.
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
|
|
||||||
## 1.8.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
|
|
||||||
## 1.8.4
|
## 1.8.4
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -36,7 +36,7 @@ Klaviyo app is based on App Template - you can check it [here](https://github.co
|
||||||
3. Install app dependencies with `pnpm i`
|
3. Install app dependencies with `pnpm i`
|
||||||
4. Start dev server `pnpm dev`
|
4. Start dev server `pnpm dev`
|
||||||
5. To install app in your Saleor Instance and expose dev server to it, run in the separate terminal `saleor app tunnel`
|
5. To install app in your Saleor Instance and expose dev server to it, run in the separate terminal `saleor app tunnel`
|
||||||
6. Now you can access the app configuration at [your dashboard]/apps and click on [klaviyo]
|
6. Now you can access the app configuration at [your dashboard]/apps and click on [klavio]
|
||||||
7. Read how to connect the app with the Klaviyo [here](https://github.com/saleor/saleor-app-template)
|
7. Read how to connect the app with the Klaviyo [here](https://github.com/saleor/saleor-app-template)
|
||||||
|
|
||||||
### Local development without CLI
|
### Local development without CLI
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# How to connect your App with Klaviyo
|
# How to connect your App with Klavio
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ First and foremost, you need to perform an initial API call to Klaviyo, which wi
|
||||||
|
|
||||||
Let's navigate to "Customers" and create the first, dummy customer.
|
Let's navigate to "Customers" and create the first, dummy customer.
|
||||||
|
|
||||||
Then, open Klaviyo [Metrics page](https://www.klaviyo.com/analytics/metrics).
|
Then, open Klavio [Metrics page](https://www.klaviyo.com/analytics/metrics).
|
||||||
|
|
||||||
Your Metric should be visible on the list:
|
Your Metric should be visible on the list:
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-klaviyo",
|
"name": "saleor-app-klaviyo",
|
||||||
"version": "1.9.0",
|
"version": "1.8.4",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -14,12 +14,12 @@
|
||||||
"@material-ui/core": "^4.12.4",
|
"@material-ui/core": "^4.12.4",
|
||||||
"@material-ui/icons": "^4.11.3",
|
"@material-ui/icons": "^4.11.3",
|
||||||
"@material-ui/lab": "4.0.0-alpha.61",
|
"@material-ui/lab": "4.0.0-alpha.61",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@sentry/nextjs": "7.67.0",
|
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
|
"clsx": "^1.2.1",
|
||||||
"graphql": "16.7.1",
|
"graphql": "16.7.1",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
"next": "13.4.8",
|
"next": "13.4.8",
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
"vitest": "0.34.1"
|
"vitest": "0.34.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/schema-ast": "4.0.0",
|
"@graphql-codegen/schema-ast": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
|
|
25
apps/klaviyo/src/hooks/theme-synchronizer.tsx
Normal file
25
apps/klaviyo/src/hooks/theme-synchronizer.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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;
|
||||||
|
}
|
17
apps/klaviyo/src/lib/ui/app-columns-layout.tsx
Normal file
17
apps/klaviyo/src/lib/ui/app-columns-layout.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Box } from "@saleor/macaw-ui/next";
|
||||||
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
export function AppColumnsLayout({ children }: PropsWithChildren<{}>) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display={"grid"}
|
||||||
|
__gridTemplateColumns={"280px auto 280px"}
|
||||||
|
gap={4}
|
||||||
|
__maxWidth={"1180px"}
|
||||||
|
marginX={"auto"}
|
||||||
|
marginY={0}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
|
||||||
import { NoSSRWrapper, ThemeSynchronizer } from "@saleor/apps-shared";
|
|
||||||
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
|
||||||
import "@saleor/macaw-ui/next/style";
|
import "@saleor/macaw-ui/next/style";
|
||||||
|
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
||||||
|
import React from "react";
|
||||||
import { AppProps } from "next/app";
|
import { AppProps } from "next/app";
|
||||||
|
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
||||||
|
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
||||||
|
import { NoSSRWrapper } from "@saleor/apps-shared";
|
||||||
|
import { ThemeSynchronizer } from "../hooks/theme-synchronizer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure instance is a singleton.
|
* Ensure instance is a singleton.
|
||||||
|
@ -15,9 +18,7 @@ function SaleorApp({ Component, pageProps }: AppProps) {
|
||||||
<AppBridgeProvider appBridgeInstance={appBridgeInstance}>
|
<AppBridgeProvider appBridgeInstance={appBridgeInstance}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ThemeSynchronizer />
|
<ThemeSynchronizer />
|
||||||
<Box padding={10}>
|
<Component {...pageProps} />
|
||||||
<Component {...pageProps} />
|
|
||||||
</Box>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</AppBridgeProvider>
|
</AppBridgeProvider>
|
||||||
</NoSSRWrapper>
|
</NoSSRWrapper>
|
||||||
|
|
|
@ -3,8 +3,13 @@ import { Head, Html, Main, NextScript } from "next/document";
|
||||||
export default function Document() {
|
export default function Document() {
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
||||||
<Head />
|
<Head />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700;800&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<body>
|
<body>
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
|
|
|
@ -61,20 +61,12 @@ const handler: NextProtectedApiHandler = async (request, res, ctx) => {
|
||||||
data: await getAppSettings(settings),
|
data: await getAppSettings(settings),
|
||||||
});
|
});
|
||||||
case "POST": {
|
case "POST": {
|
||||||
try {
|
await settings.set((request.body as PostRequestBody).data);
|
||||||
await settings.set((JSON.parse(request.body) as PostRequestBody).data);
|
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: await getAppSettings(settings),
|
data: await getAppSettings(settings),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
success: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return res.status(405).end();
|
return res.status(405).end();
|
||||||
|
|
|
@ -8,17 +8,17 @@ import { orderCreatedWebhook } from "./webhooks/order-created";
|
||||||
import { orderFullyPaidWebhook } from "./webhooks/order-fully-paid";
|
import { orderFullyPaidWebhook } from "./webhooks/order-fully-paid";
|
||||||
|
|
||||||
const handler = createManifestHandler({
|
const handler = createManifestHandler({
|
||||||
async manifestFactory({ appBaseUrl }): Promise<AppManifest> {
|
async manifestFactory(context): Promise<AppManifest> {
|
||||||
const iframeBaseUrl = process.env.APP_IFRAME_BASE_URL ?? appBaseUrl;
|
const { appBaseUrl } = context;
|
||||||
const apiBaseURL = process.env.APP_API_BASE_URL ?? appBaseUrl;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
about: "Klaviyo integration allows sending Klaviyo notifications on Saleor events.",
|
about:
|
||||||
appUrl: iframeBaseUrl,
|
"Klaviyo integration allows sending Klaviyo notifications on Saleor events.",
|
||||||
|
appUrl: appBaseUrl,
|
||||||
author: "Saleor Commerce",
|
author: "Saleor Commerce",
|
||||||
brand: {
|
brand: {
|
||||||
logo: {
|
logo: {
|
||||||
default: `${apiBaseURL}/logo.png`,
|
default: `${context.appBaseUrl}/logo.png`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
dataPrivacyUrl: "https://saleor.io/legal/privacy/",
|
dataPrivacyUrl: "https://saleor.io/legal/privacy/",
|
||||||
|
@ -27,7 +27,7 @@ const handler = createManifestHandler({
|
||||||
name: "Klaviyo",
|
name: "Klaviyo",
|
||||||
permissions: ["MANAGE_USERS", "MANAGE_ORDERS"],
|
permissions: ["MANAGE_USERS", "MANAGE_ORDERS"],
|
||||||
supportUrl: "https://github.com/saleor/apps/discussions",
|
supportUrl: "https://github.com/saleor/apps/discussions",
|
||||||
tokenTargetUrl: `${apiBaseURL}/api/register`,
|
tokenTargetUrl: `${appBaseUrl}/api/register`,
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
webhooks: [
|
webhooks: [
|
||||||
customerCreatedWebhook.getWebhookManifest(appBaseUrl),
|
customerCreatedWebhook.getWebhookManifest(appBaseUrl),
|
||||||
|
|
|
@ -1,31 +1,47 @@
|
||||||
import { useAppBridge, useAuthenticatedFetch } from "@saleor/app-sdk/app-bridge";
|
import { useAppBridge, withAuthorization } from "@saleor/app-sdk/app-bridge";
|
||||||
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
import { SALEOR_API_URL_HEADER, SALEOR_AUTHORIZATION_BEARER_HEADER } from "@saleor/app-sdk/const";
|
||||||
|
|
||||||
import { ChangeEvent, SyntheticEvent, useEffect, useState } from "react";
|
import { ChangeEvent, SyntheticEvent, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useDashboardNotification } from "@saleor/apps-shared";
|
|
||||||
import { Breadcrumbs, ButtonsBox, Layout, SkeletonLayout, TextLink } from "@saleor/apps-ui";
|
|
||||||
import { Box, Button, Input, Text } from "@saleor/macaw-ui/next";
|
|
||||||
import { useAppApi } from "../hooks/useAppApi";
|
import { useAppApi } from "../hooks/useAppApi";
|
||||||
|
import { AppColumnsLayout } from "../lib/ui/app-columns-layout";
|
||||||
|
import { useDashboardNotification } from "@saleor/apps-shared";
|
||||||
|
import { Box, BoxProps, Text, Input, Button } from "@saleor/macaw-ui/next";
|
||||||
|
|
||||||
interface ConfigurationField {
|
interface ConfigurationField {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Section(props: BoxProps) {
|
||||||
|
return <Box padding={4} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
function Instructions() {
|
function Instructions() {
|
||||||
|
const { appBridge } = useAppBridge();
|
||||||
|
|
||||||
|
const openExternalUrl = (url: string) => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
appBridge?.dispatch({
|
||||||
|
type: "redirect",
|
||||||
|
payload: {
|
||||||
|
newContext: true,
|
||||||
|
actionId: "redirect_from_klaviyo_app",
|
||||||
|
to: url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Section>
|
||||||
<Text as={"h3"} variant="heading" marginY={4}>
|
<Text as={"h3"} variant="heading">
|
||||||
How to set up
|
How to set up
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="p" marginBottom={2}>
|
<Text as="p">App will send events as Klaviyo metrics each time Saleor Event occurs.</Text>
|
||||||
App will send events as Klaviyo metrics each time Saleor Event occurs.
|
<Text as="p">
|
||||||
</Text>
|
|
||||||
<Text as="p" marginBottom={2}>
|
|
||||||
When first metric is sent, it should be available in Klaviyo to build on top of.
|
When first metric is sent, it should be available in Klaviyo to build on top of.
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="p" marginBottom={4}>
|
<Text as="p">
|
||||||
Metric name can be customized, PUBLIC_TOKEN must be provided to enable the app.
|
Metric name can be customized, PUBLIC_TOKEN must be provided to enable the app.
|
||||||
</Text>
|
</Text>
|
||||||
<Text as={"h3"} variant="heading">
|
<Text as={"h3"} variant="heading">
|
||||||
|
@ -33,9 +49,16 @@ function Instructions() {
|
||||||
</Text>
|
</Text>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<TextLink href="https://github.com/saleor/saleor-app-klaviyo" newTab>
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
openExternalUrl("https://github.com/saleor/saleor-app-klaviyo");
|
||||||
|
}}
|
||||||
|
href="https://github.com/saleor/saleor-app-klaviyo"
|
||||||
|
>
|
||||||
Visit repository & readme
|
Visit repository & readme
|
||||||
</TextLink>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<Text as={"h3"} variant="heading">
|
<Text as={"h3"} variant="heading">
|
||||||
|
@ -43,28 +66,47 @@ function Instructions() {
|
||||||
</Text>
|
</Text>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<TextLink
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
openExternalUrl(
|
||||||
|
"https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys"
|
||||||
|
);
|
||||||
|
}}
|
||||||
href="https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys"
|
href="https://help.klaviyo.com/hc/en-us/articles/115005062267-How-to-Manage-Your-Account-s-API-Keys"
|
||||||
newTab
|
|
||||||
>
|
>
|
||||||
Read about public tokens
|
Read about public tokens
|
||||||
</TextLink>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<TextLink href="https://www.klaviyo.com/account#api-keys-tab" newTab>
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
openExternalUrl("https://www.klaviyo.com/account#api-keys-tab");
|
||||||
|
}}
|
||||||
|
href="https://www.klaviyo.com/account#api-keys-tab"
|
||||||
|
>
|
||||||
Get public token here
|
Get public token here
|
||||||
</TextLink>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<TextLink
|
<a
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
openExternalUrl(
|
||||||
|
"https://help.klaviyo.com/hc/en-us/articles/115005076787-Guide-to-Managing-Your-Metrics"
|
||||||
|
);
|
||||||
|
}}
|
||||||
href="https://help.klaviyo.com/hc/en-us/articles/115005076787-Guide-to-Managing-Your-Metrics"
|
href="https://help.klaviyo.com/hc/en-us/articles/115005076787-Guide-to-Managing-Your-Metrics"
|
||||||
newTab
|
|
||||||
>
|
>
|
||||||
Read about metrics
|
Read about metrics
|
||||||
</TextLink>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Box>
|
</Section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +114,6 @@ function Configuration() {
|
||||||
const { appBridgeState } = useAppBridge();
|
const { appBridgeState } = useAppBridge();
|
||||||
const { notifySuccess, notifyError } = useDashboardNotification();
|
const { notifySuccess, notifyError } = useDashboardNotification();
|
||||||
const [configuration, setConfiguration] = useState<ConfigurationField[]>();
|
const [configuration, setConfiguration] = useState<ConfigurationField[]>();
|
||||||
const authenticatedFetch = useAuthenticatedFetch() as typeof window.fetch;
|
|
||||||
|
|
||||||
const { data: configurationData, error } = useAppApi({
|
const { data: configurationData, error } = useAppApi({
|
||||||
url: "/api/configuration",
|
url: "/api/configuration",
|
||||||
|
@ -90,8 +131,13 @@ function Configuration() {
|
||||||
const handleSubmit = (event: SyntheticEvent) => {
|
const handleSubmit = (event: SyntheticEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
authenticatedFetch("/api/configuration", {
|
fetch("/api/configuration", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
headers: [
|
||||||
|
["content-type", "application/json"],
|
||||||
|
[SALEOR_API_URL_HEADER, appBridgeState?.saleorApiUrl!],
|
||||||
|
[SALEOR_AUTHORIZATION_BEARER_HEADER, appBridgeState?.token!],
|
||||||
|
],
|
||||||
body: JSON.stringify({ data: configuration }),
|
body: JSON.stringify({ data: configuration }),
|
||||||
})
|
})
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
|
@ -103,7 +149,7 @@ function Configuration() {
|
||||||
})
|
})
|
||||||
.catch(async () => {
|
.catch(async () => {
|
||||||
await notifyError(
|
await notifyError(
|
||||||
"Configuration update failed. Ensure fields are filled correctly and you have MANAGE_APPS permission",
|
"Configuration update failed. Ensure fields are filled correctly and you have MANAGE_APPS permission"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -112,7 +158,7 @@ function Configuration() {
|
||||||
const { name, value } = event.target as HTMLInputElement;
|
const { name, value } = event.target as HTMLInputElement;
|
||||||
|
|
||||||
setConfiguration((prev) =>
|
setConfiguration((prev) =>
|
||||||
prev!.map((prevField) => (prevField.key === name ? { ...prevField, value } : prevField)),
|
prev!.map((prevField) => (prevField.key === name ? { ...prevField, value } : prevField))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,36 +192,31 @@ function Configuration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuration === undefined) {
|
if (configuration === undefined) {
|
||||||
return <SkeletonLayout.Section />;
|
return <p>Loading...</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<AppColumnsLayout>
|
||||||
<Breadcrumbs marginBottom={10}>
|
<div />
|
||||||
<Breadcrumbs.Item>Configuration</Breadcrumbs.Item>
|
<Section>
|
||||||
</Breadcrumbs>
|
<Text variant={"heading"} marginBottom={4} as={"h2"}>
|
||||||
<Layout.AppSection heading="Set up integration" sideContent={<Instructions />}>
|
Klaviyo configuration
|
||||||
<Layout.AppSectionCard
|
</Text>
|
||||||
as={"form"}
|
<Box as={"form"} display={"grid"} gap={4} gridAutoFlow={"row"} onSubmit={handleSubmit}>
|
||||||
onSubmit={handleSubmit}
|
{configuration!.map(({ key, value }) => (
|
||||||
footer={
|
<div key={key}>
|
||||||
<ButtonsBox>
|
<Input label={key} name={key} onChange={onChange} value={value} />
|
||||||
<Button type="submit" variant="primary">
|
</div>
|
||||||
Save
|
))}
|
||||||
</Button>
|
<div>
|
||||||
</ButtonsBox>
|
<Button type="submit" variant="primary">
|
||||||
}
|
Save
|
||||||
>
|
</Button>
|
||||||
<Box display={"grid"} gap={4} gridAutoFlow={"row"}>
|
</div>
|
||||||
{configuration!.map(({ key, value }) => (
|
</Box>
|
||||||
<div key={key}>
|
</Section>
|
||||||
<Input label={key} name={key} onChange={onChange} value={value} />
|
<Instructions />
|
||||||
</div>
|
</AppColumnsLayout>
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Layout.AppSectionCard>
|
|
||||||
</Layout.AppSection>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
"SENTRY_PROJECT",
|
"SENTRY_PROJECT",
|
||||||
"SENTRY_AUTH_TOKEN",
|
"SENTRY_AUTH_TOKEN",
|
||||||
"NEXT_PUBLIC_VERCEL_ENV",
|
"NEXT_PUBLIC_VERCEL_ENV",
|
||||||
"SENTRY_ENVIRONMENT",
|
"SENTRY_ENVIRONMENT"
|
||||||
"APP_API_BASE_URL",
|
|
||||||
"APP_IFRAME_BASE_URL"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,5 @@
|
||||||
# saleor-app-products-feed
|
# saleor-app-products-feed
|
||||||
|
|
||||||
## 1.12.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- ae6dbb1: Removed webhooks on product changes used for feed cache due to changed max execution time.
|
|
||||||
- ae6dbb1: Changed Vercel's maximum execution time to be 5 minutes for feed generation. This should help with the previous limits of 60s, that was not enough for feed to be generated.
|
|
||||||
|
|
||||||
## 1.12.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 5d3d81d: Bumped @hookform/resolvers from 2.9.11 to 3.3.1
|
|
||||||
- 5dee65a: Updated dependencies:
|
|
||||||
- @graphql-codegen/cli@5.0.0
|
|
||||||
- 2e29699: Updated Sentry package
|
|
||||||
|
|
||||||
## 1.12.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 261e9d1: Added additional images attribute to the feed for media uploaded to the product.
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 23e71bc: Fix typo on button label
|
|
||||||
- 261e9d1: Improved default resolution of the submitted images. Was: 500px, now it's 1024px.
|
|
||||||
Users can now configure the size in the app configuration.
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- Updated dependencies [e8660e8]
|
|
||||||
- @saleor/apps-shared@1.9.0
|
|
||||||
- @saleor/apps-ui@1.2.0
|
|
||||||
|
|
||||||
## 1.11.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 2a1385b: Plaintext EditorJS renderer has been moved to the shared package.
|
|
||||||
- a81f061: Updated Macaw to pre-127
|
|
||||||
- fcc37e7: Remove clsx package from the projects no longer using it.
|
|
||||||
- Updated dependencies [2a1385b]
|
|
||||||
- Updated dependencies [a81f061]
|
|
||||||
- Updated dependencies [fcc37e7]
|
|
||||||
- @saleor/apps-shared@1.8.1
|
|
||||||
- @saleor/react-hook-form-macaw@0.2.5
|
|
||||||
- @saleor/apps-ui@1.1.8
|
|
||||||
|
|
||||||
## 1.11.3
|
## 1.11.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -32,21 +32,6 @@ fragment GoogleFeedProductVariant on ProductVariant {
|
||||||
slug
|
slug
|
||||||
description
|
description
|
||||||
seoDescription
|
seoDescription
|
||||||
media{
|
|
||||||
id
|
|
||||||
alt
|
|
||||||
url(size: $imageSize)
|
|
||||||
type
|
|
||||||
}
|
|
||||||
variants{
|
|
||||||
id
|
|
||||||
media{
|
|
||||||
id
|
|
||||||
alt
|
|
||||||
url(size: $imageSize)
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributes{
|
attributes{
|
||||||
attribute{
|
attribute{
|
||||||
id
|
id
|
||||||
|
@ -56,7 +41,7 @@ fragment GoogleFeedProductVariant on ProductVariant {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thumbnail(size: $imageSize) {
|
thumbnail {
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
category {
|
category {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
fragment ProductVariantWebhookPayload on ProductVariant {
|
||||||
|
channel
|
||||||
|
channelListings {
|
||||||
|
channel {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fragment ProductWebhookPayload on Product {
|
||||||
|
channel
|
||||||
|
channelListings {
|
||||||
|
channel {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
query FetchProductDataForFeed($first:Int!, $after: String, $channel: String!, $imageSize: Int = 1024){
|
query FetchProductDataForFeed($first:Int!, $after: String, $channel: String!){
|
||||||
productVariants(first:$first, after: $after, channel: $channel){
|
productVariants(first:$first, after: $after, channel: $channel){
|
||||||
pageInfo{
|
pageInfo{
|
||||||
hasNextPage
|
hasNextPage
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
subscription ProductCreated {
|
||||||
|
event {
|
||||||
|
... on ProductCreated {
|
||||||
|
product {
|
||||||
|
...ProductWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
subscription ProductDeleted {
|
||||||
|
event {
|
||||||
|
... on ProductDeleted {
|
||||||
|
product {
|
||||||
|
...ProductWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
subscription ProductUpdated {
|
||||||
|
event {
|
||||||
|
... on ProductUpdated {
|
||||||
|
product {
|
||||||
|
...ProductWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
subscription ProductVariantCreated {
|
||||||
|
event {
|
||||||
|
... on ProductVariantCreated {
|
||||||
|
productVariant {
|
||||||
|
...ProductVariantWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
subscription ProductVariantDeleted {
|
||||||
|
event {
|
||||||
|
... on ProductVariantDeleted {
|
||||||
|
productVariant {
|
||||||
|
...ProductVariantWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
subscription ProductVariantUpdated {
|
||||||
|
event {
|
||||||
|
... on ProductVariantUpdated {
|
||||||
|
productVariant {
|
||||||
|
...ProductVariantWebhookPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,12 +6,7 @@ const isSentryPropertiesInEnvironment =
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
transpilePackages: [
|
transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw"],
|
||||||
"@saleor/apps-shared",
|
|
||||||
"@saleor/apps-ui",
|
|
||||||
"@saleor/react-hook-form-macaw",
|
|
||||||
"@saleor/webhook-utils",
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configWithSentry = withSentryConfig(
|
const configWithSentry = withSentryConfig(
|
||||||
|
@ -27,7 +22,7 @@ const configWithSentry = withSentryConfig(
|
||||||
tunnelRoute: "/monitoring",
|
tunnelRoute: "/monitoring",
|
||||||
hideSourceMaps: true,
|
hideSourceMaps: true,
|
||||||
disableLogger: true,
|
disableLogger: true,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = isSentryPropertiesInEnvironment ? configWithSentry : nextConfig;
|
module.exports = isSentryPropertiesInEnvironment ? configWithSentry : nextConfig;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "saleor-app-products-feed",
|
"name": "saleor-app-products-feed",
|
||||||
"version": "1.12.2",
|
"version": "1.11.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm generate && next build",
|
"build": "pnpm generate && next build",
|
||||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||||
|
@ -13,22 +13,21 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.332.0",
|
"@aws-sdk/client-s3": "^3.332.0",
|
||||||
"@hookform/resolvers": "^3.3.1",
|
"@hookform/resolvers": "^3.1.0",
|
||||||
"@saleor/app-sdk": "0.43.1",
|
"@saleor/app-sdk": "0.41.1",
|
||||||
"@saleor/apps-shared": "workspace:*",
|
"@saleor/apps-shared": "workspace:*",
|
||||||
"@saleor/apps-ui": "workspace:*",
|
"@saleor/apps-ui": "workspace:*",
|
||||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
"@saleor/macaw-ui": "0.8.0-pre.118",
|
||||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||||
"@saleor/webhook-utils": "workspace:*",
|
"@sentry/nextjs": "7.55.2",
|
||||||
"@sentry/nextjs": "7.67.0",
|
|
||||||
"@tanstack/react-query": "4.29.19",
|
"@tanstack/react-query": "4.29.19",
|
||||||
"@trpc/client": "10.38.1",
|
"@trpc/client": "10.34.0",
|
||||||
"@trpc/next": "10.38.1",
|
"@trpc/next": "10.34.0",
|
||||||
"@trpc/react-query": "10.38.1",
|
"@trpc/react-query": "10.34.0",
|
||||||
"@trpc/server": "10.38.1",
|
"@trpc/server": "10.34.0",
|
||||||
"@urql/exchange-auth": "^2.1.4",
|
"@urql/exchange-auth": "^2.1.4",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.0.4",
|
||||||
"dotenv": "^16.3.1",
|
"clsx": "^1.2.1",
|
||||||
"fast-xml-parser": "^4.0.15",
|
"fast-xml-parser": "^4.0.15",
|
||||||
"graphql": "16.7.1",
|
"graphql": "16.7.1",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
@ -48,7 +47,7 @@
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "5.0.0",
|
"@graphql-codegen/cli": "4.0.1",
|
||||||
"@graphql-codegen/introspection": "4.0.0",
|
"@graphql-codegen/introspection": "4.0.0",
|
||||||
"@graphql-codegen/schema-ast": "4.0.0",
|
"@graphql-codegen/schema-ast": "4.0.0",
|
||||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||||
|
@ -56,7 +55,7 @@
|
||||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||||
"@graphql-typed-document-node/core": "3.2.0",
|
"@graphql-typed-document-node/core": "3.2.0",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/react-hooks": "^8.0.1",
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@types/react": "18.2.5",
|
"@types/react": "18.2.5",
|
||||||
"@types/react-dom": "18.2.5",
|
"@types/react-dom": "18.2.5",
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Webhook migration scripts
|
|
||||||
|
|
||||||
Test migration with dry run, operation will not modify any data:
|
|
||||||
`npx tsx scripts/migrations/run-webhooks-migration-dry-run.ts`
|
|
||||||
|
|
||||||
To start the migration run command:
|
|
||||||
`npx tsx scripts/migrations/run-webhooks-migration.ts`
|
|
|
@ -1,20 +0,0 @@
|
||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
|
|
||||||
import { SaleorCloudAPL } from "@saleor/app-sdk/APL";
|
|
||||||
|
|
||||||
export const verifyRequiredEnvs = () => {
|
|
||||||
const requiredEnvs = ["SALEOR_CLOUD_TOKEN", "SALEOR_CLOUD_RESOURCE_URL"];
|
|
||||||
|
|
||||||
if (!requiredEnvs.every((env) => process.env[env])) {
|
|
||||||
throw new Error(`Missing envs: ${requiredEnvs.join(" | ")}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchCloudAplEnvs = () => {
|
|
||||||
const saleorAPL = new SaleorCloudAPL({
|
|
||||||
token: process.env.SALEOR_CLOUD_TOKEN!,
|
|
||||||
resourceUrl: process.env.SALEOR_CLOUD_RESOURCE_URL!,
|
|
||||||
});
|
|
||||||
|
|
||||||
return saleorAPL.getAll();
|
|
||||||
};
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
|
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
import { fetchCloudAplEnvs, verifyRequiredEnvs } from "./migration-utils";
|
|
||||||
import { updateWebhooksScript } from "./update-webhooks";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const runMigration = async () => {
|
|
||||||
console.log("Starting webhooks migration (dry run)");
|
|
||||||
|
|
||||||
verifyRequiredEnvs();
|
|
||||||
|
|
||||||
console.log("Envs verified, fetching envs");
|
|
||||||
|
|
||||||
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
|
|
||||||
console.error("Could not fetch instances from the APL");
|
|
||||||
console.error(r);
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const env of allEnvs) {
|
|
||||||
await updateWebhooksScript({ authData: env, dryRun: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Migration dry run complete");
|
|
||||||
};
|
|
||||||
|
|
||||||
runMigration();
|
|
|
@ -1,30 +0,0 @@
|
||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
|
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
import { fetchCloudAplEnvs, verifyRequiredEnvs } from "./migration-utils";
|
|
||||||
import { updateWebhooksScript } from "./update-webhooks";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const runMigration = async () => {
|
|
||||||
console.log("Starting running migration");
|
|
||||||
|
|
||||||
verifyRequiredEnvs();
|
|
||||||
|
|
||||||
console.log("Envs verified, fetching envs");
|
|
||||||
|
|
||||||
const allEnvs = await fetchCloudAplEnvs().catch((r) => {
|
|
||||||
console.error("Could not fetch instances from the APL");
|
|
||||||
console.error(r);
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const env of allEnvs) {
|
|
||||||
await updateWebhooksScript({ authData: env, dryRun: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Migration complete");
|
|
||||||
};
|
|
||||||
|
|
||||||
runMigration();
|
|
|
@ -1,29 +0,0 @@
|
||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
|
|
||||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
|
||||||
import { AuthData } from "@saleor/app-sdk/APL";
|
|
||||||
import { webhookMigrationRunner } from "@saleor/webhook-utils";
|
|
||||||
|
|
||||||
export const updateWebhooksScript = async ({
|
|
||||||
authData,
|
|
||||||
dryRun,
|
|
||||||
}: {
|
|
||||||
authData: AuthData;
|
|
||||||
dryRun: boolean;
|
|
||||||
}) => {
|
|
||||||
console.log("Working on env: ", authData.saleorApiUrl);
|
|
||||||
|
|
||||||
const client = createGraphQLClient({
|
|
||||||
saleorApiUrl: authData.saleorApiUrl,
|
|
||||||
token: authData.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
await webhookMigrationRunner({
|
|
||||||
client,
|
|
||||||
dryRun,
|
|
||||||
getManifests: async ({ appDetails }) => {
|
|
||||||
// Products feed application has currently no webhooks, so we return empty array
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -28,14 +28,11 @@ const exampleAttributeMappingConfig: RootConfig["attributeMapping"] = {
|
||||||
const exampleTitleTemplate: RootConfig["titleTemplate"] =
|
const exampleTitleTemplate: RootConfig["titleTemplate"] =
|
||||||
"Example {{ variant.product.name }} - {{ variant.name }}";
|
"Example {{ variant.product.name }} - {{ variant.name }}";
|
||||||
|
|
||||||
const exampleImageSize: RootConfig["imageSize"] = 1024;
|
|
||||||
|
|
||||||
const exampleConfiguration: RootConfig = {
|
const exampleConfiguration: RootConfig = {
|
||||||
channelConfig: exampleChannelConfig,
|
channelConfig: exampleChannelConfig,
|
||||||
s3: exampleS3Config,
|
s3: exampleS3Config,
|
||||||
attributeMapping: exampleAttributeMappingConfig,
|
attributeMapping: exampleAttributeMappingConfig,
|
||||||
titleTemplate: exampleTitleTemplate,
|
titleTemplate: exampleTitleTemplate,
|
||||||
imageSize: exampleImageSize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("AppConfig", function () {
|
describe("AppConfig", function () {
|
||||||
|
@ -54,7 +51,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: [],
|
sizeAttributeIds: [],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{variant.product.name}} - {{variant.name}}",
|
titleTemplate: "{{variant.product.name}} - {{variant.name}}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,15 +60,13 @@ describe("AppConfig", function () {
|
||||||
expect(instance.getRootConfig()).toEqual(exampleConfiguration);
|
expect(instance.getRootConfig()).toEqual(exampleConfiguration);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Fill attribute mapping, image size and title template with default values, when initial data are lacking those fields", () => {
|
it("Fill attribute mapping and title template with default values, when initial data are lacking those fields", () => {
|
||||||
const configurationWithoutMapping = structuredClone(exampleConfiguration);
|
const configurationWithoutMapping = structuredClone(exampleConfiguration);
|
||||||
|
|
||||||
// @ts-expect-error: Simulating data before the migration
|
// @ts-expect-error: Simulating data before the migration
|
||||||
delete configurationWithoutMapping.attributeMapping;
|
delete configurationWithoutMapping.attributeMapping;
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
delete configurationWithoutMapping.titleTemplate;
|
delete configurationWithoutMapping.titleTemplate;
|
||||||
// @ts-expect-error
|
|
||||||
delete configurationWithoutMapping.imageSize;
|
|
||||||
|
|
||||||
const instance = new AppConfig(configurationWithoutMapping as any); // Casting used to prevent TS from reporting an error
|
const instance = new AppConfig(configurationWithoutMapping as any); // Casting used to prevent TS from reporting an error
|
||||||
|
|
||||||
|
@ -86,7 +80,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: [],
|
sizeAttributeIds: [],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{variant.product.name}} - {{variant.name}}",
|
titleTemplate: "{{variant.product.name}} - {{variant.name}}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -117,7 +110,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: [],
|
sizeAttributeIds: [],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{ variant.name }}",
|
titleTemplate: "{{ variant.name }}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const serialized = instance1.serialize();
|
const serialized = instance1.serialize();
|
||||||
|
@ -140,7 +132,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: [],
|
sizeAttributeIds: [],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{ variant.name }}",
|
titleTemplate: "{{ variant.name }}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -169,7 +160,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: ["size-id"],
|
sizeAttributeIds: ["size-id"],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{ variant.product.name }} - {{ variant.name }}",
|
titleTemplate: "{{ variant.product.name }} - {{ variant.name }}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getRootConfig returns root config data", () => {
|
it("getRootConfig returns root config data", () => {
|
||||||
|
@ -196,7 +186,6 @@ describe("AppConfig", function () {
|
||||||
sizeAttributeIds: ["size-id"],
|
sizeAttributeIds: ["size-id"],
|
||||||
},
|
},
|
||||||
titleTemplate: "{{ variant.product.name }} - {{ variant.name }}",
|
titleTemplate: "{{ variant.product.name }} - {{ variant.name }}",
|
||||||
imageSize: 1024,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue